Skip to content
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

Return value ignored #3821

Closed
Keno opened this issue Jul 24, 2013 · 10 comments
Closed

Return value ignored #3821

Keno opened this issue Jul 24, 2013 · 10 comments
Assignees

Comments

@Keno
Copy link
Member

Keno commented Jul 24, 2013

Consider the following method in my Docker package:

    function create_container(host, image, cmd::Cmd; 
                tty = true, 
                attachStdin  = false,
                openStdin    = false,
                attachStdout = true,
                attachStderr = true,
                ports = [])

        url = "http://$host/v1.3"

        params =   ["Image" => image, 
                    "Cmd" => [cmd.exec], 
                    "Tty" => tty,
                    "AttachStdin"   => attachStdin,
                    "OpenStdin"     => openStdin,
                    "AttachStdout"  => attachStdout,
                    "AttachStderr"  => attachStderr,
                    "PortSpecs"     => [dec(p) for p in ports], 
                    "Entrypoint"    => [],
                    "Volumes"       => ["/.julia" => Dict{String,String}()],
                    "VolumesFrom"   => ""]
        resp = HTTPClient.post(URI("$url/containers/create"),JSON.to_json(params);headers=headers)
        println(resp)
        return resp
    end

The problem is that the return value of this function is being ignored

julia> Docker.create_container(docker_host,"51d4c6302994",
                              `/bin/bash`)
Response(201 Created, 9 Headers, 21 Bytes in Body)

julia> ans
:(nothing)

I tried cutting it down further (the println isn't necessary but otherwise you don't know that resp is actually being assigned), but it seems that anything that makes the actual request fail makes the return work.

EDIT: the :(nothing) is printed by my REPL, but the same thing happens with the default REPL.

@Keno
Copy link
Member Author

Keno commented Jul 24, 2013

So, here's something fun:

julia> finfer(Docker.create_container,(ASCIIString,ASCIIString,Cmd))
:($(Expr(:lambda, {:host,:image,:cmd}, {{},{{:host,ASCIIString,0},{:image,ASCIIString,0},{:cmd,Cmd,0}},{}}, quote $(Expr(:line, 20, :/home/keno/.julia/Docker/src/Docker.jl, :))
        return create_container#g48(true,false,false,true,true,top(ccall)(:jl_alloc_array_1d,$(Array{None,1}),top(tuple)(Any,Int)::(Type{Any},Type{Int64}),$(Array{None,1}),0,0,0)::Array{None,1},host::ASCIIString,image::ASCIIString,cmd::Cmd)::None
    end)))

julia> finfer(Docker.(symbol("create_container#g48")),(Bool,Bool,Bool,Bool,Bool,Array{None,1},ASCIIString,ASCIIString,Cmd))
ERROR: host not defined

julia> finfer(Docker.(symbol("create_container#g48")),(Bool,Bool,Bool,Bool,Bool,Array{None,1},ASCIIString,ASCIIString,Cmd))
ERROR: host not defined

julia> Docker.(symbol("create_container#g48"))
ERROR: host not defined

@Keno
Copy link
Member Author

Keno commented Jul 25, 2013

With @vtjnash 's help I found out that the culprit is the comprehension on the ports array. static_typeof(None) gives None so the Array constructor fails (even though it has no elements). Changing static_typeof(None) to give Type{None} fixes this, though @JeffBezanson I imagine there's a reason why you had that special case there in the first place.

@vtjnash
Copy link
Sponsor Member

vtjnash commented Jul 25, 2013

@JeffBezanson the following 3 line patch fixes the issue, and demonstrates some really cool possibilities in type inference:

diff --git a/base/dict.jl b/base/dict.jl
index 49ecc9b..25be0b1 100644
--- a/base/dict.jl
+++ b/base/dict.jl
@@ -285,6 +285,7 @@ type Dict{K,V} <: Associative{K,V}
         return h
     end
 end
+typejoin{K1,V1,K2,V2}(::Type{Dict{K1,V1}},::Type{Dict{K2,V2}}) = Dict{typejoin(K1,K2),typejoin(V1,V2)}
 Dict() = Dict{Any,Any}()

 Dict{K,V}(ks::AbstractArray{K}, vs::AbstractArray{V}) = Dict{K,V}(ks,vs)
diff --git a/base/inference.jl b/base/inference.jl
index 71ca1db..077efa3 100644
--- a/base/inference.jl
+++ b/base/inference.jl
@@ -778,6 +778,7 @@ function abstract_eval(e::ANY, vtypes, sv::StaticVarInfo)
             t = typejoin(t.types...)
         end
         if is(t,None)
+            t = Type{None}
         elseif isleaftype(t)
             t = Type{t}
         elseif isleaftype(inference_stack.types)
diff --git a/base/promotion.jl b/base/promotion.jl
index a2674c9..1a78f52 100644
--- a/base/promotion.jl
+++ b/base/promotion.jl
@@ -116,6 +116,7 @@ promote_result(t,s,T,S) = typejoin(T,S)
 # If no promote_rule is defined, both directions give None. In that
 # case use typejoin on the original types instead.
 promote_result{T,S}(::Type{T},::Type{S},::Type{None},::Type{None}) = typejoin(T, S)
+typejoin{T,U,N}(::Type{Array{T,N}},::Type{Array{U,N}}) = Array{typejoin(T,U),N}

 promote() = ()
 promote(x) = (x,)

Keno wondered whether it should be returning the Union instead of the typejoin?

@JeffBezanson
Copy link
Sponsor Member

Adding methods to typejoin is entirely incorrect; the type system defines
what is a subtype of what and trying to fight it will lead to incorrect
type deductions. This should not really be a normal function but is defined
that way for convenience.
On Jul 25, 2013 2:50 PM, "Jameson Nash" notifications@github.com wrote:

@JeffBezanson https://github.com/JeffBezanson the following 3 line
patch fixes the issue, and demonstrates some really cool possibilities in
type inference:

diff --git a/base/dict.jl b/base/dict.jl
index 49ecc9b..25be0b1 100644
--- a/base/dict.jl
+++ b/base/dict.jl
@@ -285,6 +285,7 @@ type Dict{K,V} <: Associative{K,V}
return h
end
end
+typejoin{K1,V1,K2,V2}(::Type{Dict{K1,V1}},::Type{Dict{K2,V2}}) = Dict{typejoin(K1,K2),typejoin(V1,V2)}
Dict() = Dict{Any,Any}()

Dict{K,V}(ks::AbstractArray{K}, vs::AbstractArray{V}) = Dict{K,V}(ks,vs)
diff --git a/base/inference.jl b/base/inference.jl
index 71ca1db..077efa3 100644
--- a/base/inference.jl
+++ b/base/inference.jl
@@ -778,6 +778,7 @@ function abstract_eval(e::ANY, vtypes, sv::StaticVarInfo)
t = typejoin(t.types...)
end
if is(t,None)

  •        t = Type{None}
     elseif isleaftype(t)
         t = Type{t}
     elseif isleaftype(inference_stack.types)
    

    diff --git a/base/promotion.jl b/base/promotion.jl
    index a2674c9..1a78f52 100644
    --- a/base/promotion.jl
    +++ b/base/promotion.jl
    @@ -116,6 +116,7 @@ promote_result(t,s,T,S) = typejoin(T,S)

    If no promote_rule is defined, both directions give None. In that

    case use typejoin on the original types instead.

    promote_result{T,S}(::Type{T},::Type{S},::Type{None},::Type{None}) = typejoin(T, S)
    +typejoin{T,U,N}(::Type{Array{T,N}},::Type{Array{U,N}}) = Array{typejoin(T,U),N}

    promote() = ()
    promote(x) = (x,)

Keno wondered whether it should be returning the Union instead of the
typejoin?


Reply to this email directly or view it on GitHubhttps://github.com//issues/3821#issuecomment-21587373
.

@vtjnash
Copy link
Sponsor Member

vtjnash commented Jul 25, 2013

Is it strictly required that the result of typejoin must be a subtype of the original types? Where else does this information get computed? Isn't typejoin supposed to be the implementation of the type system?

@StefanKarpinski
Copy link
Sponsor Member

This needs to be consistent with everything else in the type system, so no you can't just do whatever you'd like to here.

@JeffBezanson
Copy link
Sponsor Member

In fact we didn't have typejoin for a while and it is not used that much in
the type system, but it needs to be consistent with it (the built in
subtype and meet operators). If you want to define other things (like
promote_type) those can be separate functions.
On Jul 25, 2013 4:48 PM, "Stefan Karpinski" notifications@github.com
wrote:

This needs to be consistent with everything else in the type system, so no
you can't just do whatever you'd like to here.


Reply to this email directly or view it on GitHubhttps://github.com//issues/3821#issuecomment-21592842
.

@vtjnash
Copy link
Sponsor Member

vtjnash commented Jul 26, 2013

@JeffBezanson here's a limited example demonstrating the issue:

julia> f(p) = [x for x in p]
# methods for generic function f
f(p) at none:1

julia> finfer(f,(Array{None},))
:($(Expr(:lambda, {:p}, {{:#s4,:x,:#s1,:#s2,:#s6,:#s7,:#s8,:_var0,:_var1},{{:p,Array{None,N},0},{:#s4,None,18},{:x,None,18},{:#s1,None,18},{:#s2,Int64,2},{:#s6,Array{None,N},18},{:#s7,Int64,2},{:#s8,None,18},{:_var0,None,2},{:_var1,Int64,2}},{}}, quote  # none, line 1:
        0: 
        #s1 = top(Array)($(Expr(:static_typeof, :#s4))::None,arraylen(p::Array{None,N})::Int64)::None
        #s2 = 1
        #s6 = p::Array{None,N}
        #s7 = 1
        2: 
        unless top(box)(Bool,top(not_int)(top(slt_int)(arraylen(#s6::Array{None,N})::Int64,#s7::Int64)::Bool))::Bool goto 3
        _var0 = arrayref(#s6::Array{None,N},#s7::Int64)::None
        _var1 = top(box)(Int64,top(add_int)(#s7::Int64,1))::Int64
        x = _var0::None
        #s7 = _var1::Int64
        #s4 = x::None
        $(Expr(:type_goto, 0))
        $(Expr(:boundscheck, false))
        top(setindex!)(#s1::None,#s4::None,#s2::Int64)::None
        $(Expr(:boundscheck, :pop))
        #s2 = top(box)(Int64,top(add_int)(#s2::Int64,1))::Int64
        4: 
        goto 2
        3: 
        1: 
        return #s1::None
    end)))

julia> f([])
0-element Any Array

julia> g() = f([])
# methods for generic function g
g() at none:1

julia> g()

julia> finfer(g,())
:($(Expr(:lambda, {}, {{},{},{}}, quote  # none, line 1:
        return f(top(ccall)(:jl_alloc_array_1d,$(Array{None,1}),top(tuple)(Any,Int)::(Type{Any},Type{Int64}),$(Array{None,1}),0,0,0)::Array{None,1})::None
    end)))

@JeffBezanson
Copy link
Sponsor Member

Working on this now. Turns out the solution of adding t = Type{None} has problems, though it fixes this case. The problem is that there are two control flow edges into the code using static_typeof: falling through from above, and the type_goto. This leads to a type like Union(Type{None},Type{T}), which we don't want.

As it is now (with no change), when the variable we're interested in is actually inferred to have type None, the static_typeof is not revisited since None adds no information to the type we already have there (Undef). So the solution might be for type_goto to know which variables are involved, and use different updating rules for them (since unlike in other cases, we want to know the type whether it adds information or not).

@vtjnash
Copy link
Sponsor Member

vtjnash commented Jul 28, 2013

That was my analysis too. That's why I added the code above so you could decide how to fix it. Any chance we can merge #3796 (while deleting the temporary :inline hack if you like) before you change something that would make merging more difficult for me later?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants