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

Get better type info from partially generated functions #31025

Merged
merged 2 commits into from
Feb 13, 2019

Commits on Feb 10, 2019

  1. Configuration menu
    Copy the full SHA
    635b8c5 View commit details
    Browse the repository at this point in the history

Commits on Feb 11, 2019

  1. Get better type info from partially generated functions

    Consider the following function:
    ```
    julia> function foo(a, b)
               ntuple(i->(a+b; i), Val(4))
           end
    foo (generic function with 1 method)
    ```
    
    (In particular note that the return type of the closure does not depend on the types
    of `a` and b`). Unfortunately, prior to this change, inference was unable to determine
    the return type in this situation:
    
    ```
    julia> code_typed(foo, Tuple{Any, Any}, trace=true)
    Refused to call generated function with non-concrete argument types ntuple(::getfield(Main, Symbol("##15#16")){_A,_B} where _B where _A, ::Val{4}) [GeneratedNotConcrete]
    
    1-element Array{Any,1}:
     CodeInfo(
    1 ─ %1 = Main.:(##15#16)::Const(##15#16, false)
    │   %2 = Core.typeof(a)::DataType
    │   %3 = Core.typeof(b)::DataType
    │   %4 = Core.apply_type(%1, %2, %3)::Type{##15#16{_A,_B}} where _B where _A
    │   %5 = %new(%4, a, b)::##15#16{_A,_B} where _B where _A
    │   %6 = Main.ntuple(%5, $(QuoteNode(Val{4}())))::Any
    └──      return %6
    ) => Any
    ```
    
    Looking at the definition of ntuple
    
    https://github.com/JuliaLang/julia/blob/abb09f88804c4e74c752a66157e767c9b0f8945d/base/ntuple.jl#L45-L56
    
    we see that it is a generated function an inference thus refuses to invoke it,
    unless it can prove the concrete type of *all* arguments to the function. As
    the above example illustrates, this restriction is more stringent than necessary.
    It is true that we cannot invoke generated functions on arbitrary abstract
    signatures (because we neither want to the user to have to be able to nor
    do we trust that users are able to preverse monotonicity - i.e. that the return
    type of the generated code will always be a subtype of the return type of a more
    abstract signature).
    
    However, if some piece of information is not used (the type of the passed function
    in this case), there is no problem with calling the generated function (since
    information that is unnused cannot possibly affect monotnicity).
    
    This PR allows us to recognize pieces of information that are *syntactically* unused,
    and call the generated functions, even if we do not have those pieces of information.
    
    As a result, we are now able to infer the return type of the above function:
    ```
    julia> code_typed(foo, Tuple{Any, Any})
    1-element Array{Any,1}:
     CodeInfo(
    1 ─ %1 = Main.:(##3#4)::Const(##3#4, false)
    │   %2 = Core.typeof(a)::DataType
    │   %3 = Core.typeof(b)::DataType
    │   %4 = Core.apply_type(%1, %2, %3)::Type{##3#4{_A,_B}} where _B where _A
    │   %5 = %new(%4, a, b)::##3#4{_A,_B} where _B where _A
    │   %6 = Main.ntuple(%5, $(QuoteNode(Val{4}())))::NTuple{4,Int64}
    └──      return %6
    ) => NTuple{4,Int64}
    ```
    
    In particular, we use the new frontent `used` flags from the previous commit.
    One additional complication is that we want to accesss these flags without
    uncompressing the generator source, so we change the compression scheme to
    place the flags at a known location.
    
    Fixes #31004
    Keno committed Feb 11, 2019
    Configuration menu
    Copy the full SHA
    3bc4ea7 View commit details
    Browse the repository at this point in the history