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

[REPLCompletions] improved handling of completions #43865

Merged
merged 5 commits into from
Jan 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ Compiler/Runtime improvements
calls ([#43239]).
* Abstract callsite can now be inlined or statically resolved as far as the callsite has a single
matching method ([#43113]).
* Builtin function are now a bit more like generic functions, and can be enumerated with `methods` ([#43865]).

Command-line option changes
---------------------------
Expand Down
28 changes: 22 additions & 6 deletions base/compiler/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -958,20 +958,36 @@ function typeinf_ext_toplevel(interp::AbstractInterpreter, linfo::MethodInstance
return src
end

function return_type(@nospecialize(f), @nospecialize(t))
function return_type(@nospecialize(f), t::DataType) # this method has a special tfunc
world = ccall(:jl_get_tls_world_age, UInt, ())
return ccall(:jl_call_in_typeinf_world, Any, (Ptr{Ptr{Cvoid}}, Cint), Any[_return_type, f, t, world], 4)
args = Any[_return_type, NativeInterpreter(world), Tuple{Core.Typeof(f), t.parameters...}]
return ccall(:jl_call_in_typeinf_world, Any, (Ptr{Ptr{Cvoid}}, Cint), args, length(args))
end

_return_type(@nospecialize(f), @nospecialize(t), world) = _return_type(NativeInterpreter(world), f, t)
function return_type(@nospecialize(f), t::DataType, world::UInt)
return return_type(Tuple{Core.Typeof(f), t.parameters...}, world)
end

function return_type(t::DataType)
world = ccall(:jl_get_tls_world_age, UInt, ())
return return_type(t, world)
end

function return_type(t::DataType, world::UInt)
args = Any[_return_type, NativeInterpreter(world), t]
return ccall(:jl_call_in_typeinf_world, Any, (Ptr{Ptr{Cvoid}}, Cint), args, length(args))
end

function _return_type(interp::AbstractInterpreter, @nospecialize(f), @nospecialize(t))
function _return_type(interp::AbstractInterpreter, t::DataType)
rt = Union{}
f = singleton_type(t.parameters[1])
if isa(f, Builtin)
rt = builtin_tfunction(interp, f, Any[t.parameters...], nothing)
args = Any[t.parameters...]
popfirst!(args)
rt = builtin_tfunction(interp, f, args, nothing)
rt = widenconst(rt)
else
for match in _methods(f, t, -1, get_world_counter(interp))::Vector
for match in _methods_by_ftype(t, -1, get_world_counter(interp))::Vector
match = match::MethodMatch
ty = typeinf_type(interp, match.method, match.spec_types, match.sparams)
ty === nothing && return Any
Expand Down
39 changes: 22 additions & 17 deletions base/methodshow.jl
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ function show(io::IO, m::Method)
file, line = updated_methodloc(m)
print(io, " at ", file, ":", line)
end
nothing
end

function show_method_list_header(io::IO, ms::MethodList, namefmt::Function)
Expand All @@ -232,24 +233,27 @@ function show_method_list_header(io::IO, ms::MethodList, namefmt::Function)
hasname = isdefined(mt.module, name) &&
typeof(getfield(mt.module, name)) <: Function
n = length(ms)
if mt.module === Core && n == 0 && mt.defs === nothing && mt.cache !== nothing
# try to detect Builtin
print(io, "# built-in function; no methods")
m = n==1 ? "method" : "methods"
print(io, "# $n $m")
sname = string(name)
namedisplay = namefmt(sname)
if hasname
what = (startswith(sname, '@') ?
"macro"
: mt.module === Core && last(ms).sig === Tuple ?
"builtin function"
: # else
"generic function")
print(io, " for ", what, " ", namedisplay)
elseif '#' in sname
print(io, " for anonymous function ", namedisplay)
elseif mt === _TYPE_NAME.mt
print(io, " for type constructor")
else
m = n==1 ? "method" : "methods"
print(io, "# $n $m")
sname = string(name)
namedisplay = namefmt(sname)
if hasname
what = startswith(sname, '@') ? "macro" : "generic function"
print(io, " for ", what, " ", namedisplay)
elseif '#' in sname
print(io, " for anonymous function ", namedisplay)
elseif mt === _TYPE_NAME.mt
print(io, " for type constructor")
end
print(io, ":")
print(io, " for callable object")
end
n > 0 && print(io, ":")
nothing
end

function show_method_table(io::IO, ms::MethodList, max::Int=-1, header::Bool=true)
Expand All @@ -267,7 +271,7 @@ function show_method_table(io::IO, ms::MethodList, max::Int=-1, header::Bool=tru
last_shown_line_infos === nothing || empty!(last_shown_line_infos)

for meth in ms
if max==-1 || n<max
if max == -1 || n < max
n += 1
println(io)
print(io, "[$n] ")
Expand All @@ -292,6 +296,7 @@ function show_method_table(io::IO, ms::MethodList, max::Int=-1, header::Bool=tru
end
end
end
nothing
end

show(io::IO, ms::MethodList) = show_method_table(io, ms)
Expand Down
77 changes: 25 additions & 52 deletions base/reflection.jl
Original file line number Diff line number Diff line change
Expand Up @@ -930,14 +930,6 @@ function _methods_by_ftype(@nospecialize(t), mt::Union{Core.MethodTable, Nothing
return ccall(:jl_matching_methods, Any, (Any, Any, Cint, Cint, UInt, Ptr{UInt}, Ptr{UInt}, Ptr{Int32}), t, mt, lim, ambig, world, min, max, has_ambig)::Union{Array{Any,1}, Bool}
end

function _method_by_ftype(args...)
matches = _methods_by_ftype(args...)
if length(matches) != 1
error("no unique matching method found for the specified argument types")
end
return matches[1]
end

# high-level, more convenient method lookup functions

# type for reflecting and pretty-printing a subset of methods
Expand Down Expand Up @@ -973,9 +965,6 @@ See also: [`which`](@ref) and `@which`.
"""
function methods(@nospecialize(f), @nospecialize(t),
mod::Union{Tuple{Module},AbstractArray{Module},Nothing}=nothing)
if isa(f, Core.Builtin)
throw(ArgumentError("argument is not a generic function"))
end
t = to_tuple_type(t)
world = get_world_counter()
# Lack of specialization => a comprehension triggers too many invalidations via _collect, so collect the methods manually
Expand All @@ -988,15 +977,12 @@ function methods(@nospecialize(f), @nospecialize(t),
end
methods(@nospecialize(f), @nospecialize(t), mod::Module) = methods(f, t, (mod,))

methods(f::Core.Builtin) = MethodList(Method[], typeof(f).name.mt)

function methods_including_ambiguous(@nospecialize(f), @nospecialize(t))
tt = signature_type(f, t)
world = get_world_counter()
min = RefValue{UInt}(typemin(UInt))
max = RefValue{UInt}(typemax(UInt))
ms = _methods_by_ftype(tt, nothing, -1, world, true, min, max, Ptr{Int32}(C_NULL))
isa(ms, Bool) && return ms
ms = _methods_by_ftype(tt, nothing, -1, world, true, min, max, Ptr{Int32}(C_NULL))::Vector
return MethodList(Method[(m::Core.MethodMatch).method for m in ms], typeof(f).name.mt)
end

Expand Down Expand Up @@ -1213,9 +1199,6 @@ function code_typed(@nospecialize(f), @nospecialize(types=default_tt(f));
debuginfo::Symbol=:default,
world = get_world_counter(),
interp = Core.Compiler.NativeInterpreter(world))
if isa(f, Core.Builtin)
throw(ArgumentError("argument is not a generic function"))
end
if isa(f, Core.OpaqueClosure)
return code_typed_opaque_closure(f; optimize, debuginfo, interp)
end
Expand Down Expand Up @@ -1262,18 +1245,18 @@ function code_typed_by_type(@nospecialize(tt#=::Type=#);
throw(ArgumentError("'debuginfo' must be either :source or :none"))
end
tt = to_tuple_type(tt)
matches = _methods_by_ftype(tt, -1, world)
if matches === false
error("signature does not correspond to a generic function")
end
matches = _methods_by_ftype(tt, -1, world)::Vector
asts = []
for match in matches::Vector
for match in matches
match = match::Core.MethodMatch
meth = func_for_method_checked(match.method, tt, match.sparams)
(code, ty) = Core.Compiler.typeinf_code(interp, meth, match.spec_types, match.sparams, optimize)
code === nothing && error("inference not successful") # inference disabled?
debuginfo === :none && remove_linenums!(code)
push!(asts, code => ty)
if code === nothing
push!(asts, meth => Any)
else
debuginfo === :none && remove_linenums!(code)
push!(asts, code => ty)
end
end
return asts
end
Expand All @@ -1295,18 +1278,14 @@ end

function return_types(@nospecialize(f), @nospecialize(types=default_tt(f)), interp=Core.Compiler.NativeInterpreter())
ccall(:jl_is_in_pure_context, Bool, ()) && error("code reflection cannot be used from generated functions")
if isa(f, Core.Builtin)
throw(ArgumentError("argument is not a generic function"))
end
types = to_tuple_type(types)
rt = []
world = get_world_counter()
for match in _methods(f, types, -1, world)::Vector
match = match::Core.MethodMatch
meth = func_for_method_checked(match.method, types, match.sparams)
ty = Core.Compiler.typeinf_type(interp, meth, match.spec_types, match.sparams)
ty === nothing && error("inference not successful") # inference disabled?
push!(rt, ty)
push!(rt, something(ty, Any))
end
return rt
end
Expand All @@ -1318,37 +1297,34 @@ Print type-inferred and optimized code for `f` given argument types `types`,
prepending each line with its cost as estimated by the compiler's inlining engine.
"""
function print_statement_costs(io::IO, @nospecialize(f), @nospecialize(t); kwargs...)
if isa(f, Core.Builtin)
throw(ArgumentError("argument is not a generic function"))
end
tt = signature_type(f, t)
print_statement_costs(io, tt; kwargs...)
end

function print_statement_costs(io::IO, @nospecialize(tt#=::Type=#);
world = get_world_counter(),
interp = Core.Compiler.NativeInterpreter(world))
matches = _methods_by_ftype(tt, -1, world)
if matches === false
error("signature does not correspond to a generic function")
end
matches = _methods_by_ftype(tt, -1, world)::Vector
params = Core.Compiler.OptimizationParams(interp)
cst = Int[]
for match in matches::Vector
for match in matches
match = match::Core.MethodMatch
meth = func_for_method_checked(match.method, tt, match.sparams)
(code, ty) = Core.Compiler.typeinf_code(interp, meth, match.spec_types, match.sparams, true)
code === nothing && error("inference not successful") # inference disabled?
empty!(cst)
resize!(cst, length(code.code))
maxcost = Core.Compiler.statement_costs!(cst, code.code, code, Any[match.sparams...], false, params)
nd = ndigits(maxcost)
println(io, meth)
irshow_config = IRShow.IRShowConfig() do io, linestart, idx
print(io, idx > 0 ? lpad(cst[idx], nd+1) : " "^(nd+1), " ")
return ""
(code, ty) = Core.Compiler.typeinf_code(interp, meth, match.spec_types, match.sparams, true)
if code === nothing
println(io, " inference not successful")
else
empty!(cst)
resize!(cst, length(code.code))
maxcost = Core.Compiler.statement_costs!(cst, code.code, code, Any[match.sparams...], false, params)
nd = ndigits(maxcost)
irshow_config = IRShow.IRShowConfig() do io, linestart, idx
print(io, idx > 0 ? lpad(cst[idx], nd+1) : " "^(nd+1), " ")
return ""
end
IRShow.show_ir(io, code, irshow_config)
end
IRShow.show_ir(io, code, irshow_config)
println(io)
end
end
Expand Down Expand Up @@ -1377,9 +1353,6 @@ If `types` is an abstract type, then the method that would be called by `invoke`
See also: [`parentmodule`](@ref), and `@which` and `@edit` in [`InteractiveUtils`](@ref man-interactive-utils).
"""
function which(@nospecialize(f), @nospecialize(t))
if isa(f, Core.Builtin)
throw(ArgumentError("argument is not a generic function"))
end
t = to_tuple_type(t)
tt = signature_type(f, t)
return which(tt)
Expand Down
5 changes: 3 additions & 2 deletions src/builtins.c
Original file line number Diff line number Diff line change
Expand Up @@ -1771,8 +1771,9 @@ static void add_builtin(const char *name, jl_value_t *v)
jl_fptr_args_t jl_get_builtin_fptr(jl_value_t *b)
{
assert(jl_isa(b, (jl_value_t*)jl_builtin_type));
jl_typemap_entry_t *entry = (jl_typemap_entry_t*)jl_atomic_load_relaxed(&jl_gf_mtable(b)->cache);
jl_code_instance_t *ci = jl_atomic_load_relaxed(&entry->func.linfo->cache);
jl_typemap_entry_t *entry = (jl_typemap_entry_t*)jl_atomic_load_relaxed(&jl_gf_mtable(b)->defs);
jl_method_instance_t *mi = jl_atomic_load_relaxed(&entry->func.method->unspecialized);
jl_code_instance_t *ci = jl_atomic_load_relaxed(&mi->cache);
return jl_atomic_load_relaxed(&ci->specptr.fptr1);
}

Expand Down
Loading