Skip to content

Commit

Permalink
improve REPL completion by inferring subexpressions (#24141)
Browse files Browse the repository at this point in the history
this also makes it compatible with linear IR, and generally more
robust to changes in the result of `expand`.
  • Loading branch information
JeffBezanson authored Oct 16, 2017
1 parent 3fcc2ef commit a54e60f
Show file tree
Hide file tree
Showing 4 changed files with 40 additions and 5 deletions.
1 change: 1 addition & 0 deletions base/inference.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3175,6 +3175,7 @@ function typeinf_ext(linfo::MethodInstance, world::UInt)
ccall(:jl_typeinf_end, Void, ())
@assert frame.inferred # TODO: deal with this better
@assert frame.linfo === linfo
linfo.rettype = widenconst(frame.bestguess)
return svec(linfo, frame.src, linfo.rettype)
end
end
Expand Down
22 changes: 20 additions & 2 deletions base/repl/REPLCompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -316,8 +316,7 @@ function get_type_call(expr::Expr)
end

# Returns the return type. example: get_type(:(Base.strip("", ' ')), Main) returns (String, true)
function get_type(sym::Expr, fn::Module)
sym = expand(fn, sym)
function try_get_type(sym::Expr, fn::Module)
val, found = get_value(sym, fn)
found && return Base.typesof(val).parameters[1], found
if sym.head === :call
Expand All @@ -330,9 +329,28 @@ function get_type(sym::Expr, fn::Module)
return found ? Base.typesof(val).parameters[1] : Any, found
end
return get_type_call(sym)
elseif sym.head === :thunk
thk = sym.args[1]
rt = ccall(:jl_infer_thunk, Any, (Any, Any), thk::CodeInfo, fn)
rt !== Any && return (rt, true)
elseif sym.head === :ref
# some simple cases of `expand`
return try_get_type(Expr(:call, GlobalRef(Base, :getindex), sym.args...), fn)
elseif sym.head === :.
return try_get_type(Expr(:call, GlobalRef(Core, :getfield), sym.args...), fn)
end
return (Any, false)
end

try_get_type(other, fn::Module) = get_type(other, fn)

function get_type(sym::Expr, fn::Module)
# try to analyze nests of calls. if this fails, try using the expanded form.
val, found = try_get_type(sym, fn)
found && return val, found
return try_get_type(expand(fn, sym), fn)
end

function get_type(sym, fn::Module)
val, found = get_value(sym, fn)
return found ? Base.typesof(val).parameters[1] : Any, found
Expand Down
9 changes: 9 additions & 0 deletions src/toplevel.c
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,15 @@ JL_DLLEXPORT jl_value_t *jl_toplevel_eval(jl_module_t *m, jl_value_t *v)
return jl_toplevel_eval_flex(m, v, 1, 0);
}

JL_DLLEXPORT jl_value_t *jl_infer_thunk(jl_code_info_t *thk, jl_module_t *m)
{
jl_method_instance_t *li = jl_new_thunk(thk, m);
JL_GC_PUSH1(&li);
jl_type_infer(&li, jl_get_ptls_states()->world_age, 0);
JL_GC_POP();
return li->rettype;
}

JL_DLLEXPORT jl_value_t *jl_load(jl_module_t *module, const char *fname)
{
if (module->istopmod) {
Expand Down
13 changes: 10 additions & 3 deletions test/replcompletions.jl
Original file line number Diff line number Diff line change
Expand Up @@ -378,15 +378,22 @@ let s = "CompletionFoo.test4(\"e\",r\" \","
end

# (As discussed in #19829, the Base.REPLCompletions.get_type function isn't
# powerful enough to analyze general dot calls because it can't handle
# anonymous-function evaluation.)
let s = "CompletionFoo.test5(push!(Base.split(\"\",' '),\"\",\"\").==\"\","
# powerful enough to analyze anonymous functions.)
let s = "CompletionFoo.test5(broadcast((x,y)->x==y, push!(Base.split(\"\",' '),\"\",\"\"), \"\"),"
c, r, res = test_complete(s)
@test !res
@test_broken length(c) == 1
@test_broken c[1] == string(first(methods(Main.CompletionFoo.test5, Tuple{BitArray{1}})))
end

# test partial expression expansion
let s = "CompletionFoo.test5(Bool[x==1 for x=1:4],"
c, r, res = test_complete(s)
@test !res
@test length(c) == 1
@test c[1] == string(first(methods(Main.CompletionFoo.test5, Tuple{Array{Bool,1}})))
end

let s = "CompletionFoo.test4(CompletionFoo.test_y_array[1]()[1], CompletionFoo.test_y_array[1]()[2], "
c, r, res = test_complete(s)
@test !res
Expand Down

0 comments on commit a54e60f

Please sign in to comment.