Skip to content

Commit

Permalink
REPLCompletions: unswitch Union of same UnionAll results (#50483)
Browse files Browse the repository at this point in the history
With this commit the inference-base REPL completion algorithm converts
`Union` result of same `UnionAll` instances to `UnionAll` of `Union`s
so that it can use the field information of the `UnionAll`.

This allows us to get completions for cases like:
```julia
julia> union_somes() = rand() < 0.5 ? Some(1) : Some(2.);

julia> union_somes().| # completes to `value`
```
  • Loading branch information
aviatesk authored Jul 11, 2023
1 parent 25eeba4 commit 680e3b3
Show file tree
Hide file tree
Showing 3 changed files with 32 additions and 6 deletions.
23 changes: 17 additions & 6 deletions base/compiler/typeutils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -318,21 +318,32 @@ function unionall_depth(@nospecialize ua) # aka subtype_env_size
end

# convert a Union of Tuple types to a Tuple of Unions
function unswitchtupleunion(u::Union)
unswitchtupleunion(u::Union) = unswitchtypeunion(u, Tuple.name)

function unswitchtypeunion(u::Union, typename::Union{Nothing,Core.TypeName}=nothing)
ts = uniontypes(u)
n = -1
for t in ts
if t isa DataType && t.name === Tuple.name && length(t.parameters) != 0 && !isvarargtype(t.parameters[end])
if n == -1
n = length(t.parameters)
elseif n != length(t.parameters)
if t isa DataType
if typename === nothing
typename = t.name
elseif typename !== t.name
return u
end
if length(t.parameters) != 0 && !isvarargtype(t.parameters[end])
if n == -1
n = length(t.parameters)
elseif n != length(t.parameters)
return u
end
end
else
return u
end
end
Tuple{Any[ Union{Any[(t::DataType).parameters[i] for t in ts]...} for i in 1:n ]...}
Head = (typename::Core.TypeName).wrapper
unionparams = Any[ Union{Any[(t::DataType).parameters[i] for t in ts]...} for i in 1:n ]
return Head{unionparams...}
end

function unwraptv_ub(@nospecialize t)
Expand Down
5 changes: 5 additions & 0 deletions stdlib/REPL/src/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,11 @@ function repl_eval_ex(@nospecialize(ex), context_module::Module)

result = frame.result.result
result === Union{} && return nothing # for whatever reason, callers expect this as the Bottom and/or Top type instead
if isa(result, Union)
# unswitch `Union` of same `UnionAll` instances to `UnionAll` of `Union`s
# so that we can use the field information of the `UnionAll`
return CC.unswitchtypeunion(result)
end
return result
end

Expand Down
10 changes: 10 additions & 0 deletions stdlib/REPL/test/replcompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1873,3 +1873,13 @@ let s = "`abc`.e"
# (completions for the fields of `Cmd`)
@test c == Any["env", "exec"]
end

# Test completion for a case when type inference returned `Union` of the same types
function union_somes()
return rand() < 0.5 ? Some(1) : Some(2.)
end
let s = "union_somes()."
c, r, res = test_complete_context(s, @__MODULE__)
@test res
@test "value" in c
end

2 comments on commit 680e3b3

@aviatesk
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nanosoldier runbenchmarks("inference", vs="@25eeba414eeac9d2b6014886b34840d5ef9c6351")

@nanosoldier
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your benchmark job has completed - possible performance regressions were detected. A full report can be found here.

Please sign in to comment.