About.jl/src/functions.jl

173 lines
7.7 KiB
Julia

function about(io::IO, fn::Function)
source = Main.InteractiveUtils.which(parentmodule(fn), Symbol(fn))
methodmodules = getproperty.(methods(fn).ms, :module)
others = setdiff(methodmodules, [source])
fn_name, fn_extra = split(Base.summary(fn), ' ', limit=2)
print(io, S"{julia_funcall:$fn_name} $fn_extra\n Defined in {about_module:$source}")
if length(others) > 0
print(io, S"{shadow:({emphasis:$(sum(Ref(source) .=== methodmodules))})} extended in ")
for (i, oth) in enumerate(others)
print(io, S"{about_module:$oth}{shadow:({emphasis:$(sum(Ref(oth) .=== methodmodules))})}")
if length(others) == 2 && i == 1
print(io, " and ")
elseif length(others) > 2 && i < length(others)-1
print(io, ", ")
elseif length(others) > 2 && i == length(others)-1
print(io, ", and ")
end
end
end
print(io, ".\n")
end
function about(io::IO, @nospecialize(cfn::ComposedFunction))
print(io, S"{bold:Composed function:} ")
fnstack = Function[]
function decompose!(fnstk, c::ComposedFunction)
decompose!(fnstk, c.outer)
decompose!(fnstk, c.inner)
end
decompose!(fnstk, c::Function) = push!(fnstk, c)
decompose!(fnstack, cfn)
join(io, map(f -> S"{julia_funcall:$f}", fnstack), S" {julia_operator:∘} ")
println(io)
for fn in fnstack
print(io, S" {emphasis:•} ")
about(io, fn)
end
end
function about(io::IO, method::Method)
fn, sig = first(method.sig.types).instance, Tuple{map(Base.unwrap_unionall, method.sig.types[2:end])...}
show(io, method)
println(io)
print_effects(io, fn, sig)
end
function about(io::IO, fn::Function, @nospecialize(argtypes::Type{<:Tuple}))
about(io, fn); println(io)
ms = methods(fn, argtypes)
if isempty(ms)
fncall = highlight("$fn($(join(collect(argtypes.types), ", ")))")
println(io, S" {error:!} No methods matched $fncall")
return
end
rinfo = let rtypes = Base.return_types(fn, argtypes) # HACK: this is technically private API
unique!(rtypes)
for i in eachindex(rtypes), j in eachindex(rtypes)
Tᵢ, Tⱼ = rtypes[i], rtypes[j]
if Tᵢ <: Tⱼ
rtypes[i] = Tⱼ
elseif Tⱼ <: Tᵢ
rtypes[j] = Tᵢ
end
end
unique!(rtypes)
sort!(rtypes, by=length supertypes)
join(map(t -> S"{julia_type:$t}", rtypes), ", ")
end
println(io, S" Matched {emphasis:$(length(ms))} method$(ifelse(length(ms) > 1, \"s\", \"\")) {julia_type:::} $rinfo")
for method in ms
mcall, msrc = split(sprint(show, method), " @ ")
msrcinfo = match(r"^([A-Z][A-Za-z0-9\.]+) (.+)$", msrc)
msrcpretty = if isnothing(msrcinfo)
S"{shadow,underline:$msrc}"
else
mmod, mfile = msrcinfo.captures
S"{about_module:$mmod} {shadow,underline:$mfile}"
end
println(io, S" {light:$(highlight(mcall))} {shadow,bold:@} $msrcpretty")
end
println(io)
@static if VERSION >= v"1.8"
about(io, Base.infer_effects(fn, argtypes))
end
end
@static if VERSION >= v"1.8"
struct CompatibleCoreCompilerConstants end
function Base.getproperty(::CompatibleCoreCompilerConstants, name::Symbol)
if isdefined(Core.Compiler, name)
getglobal(Core.Compiler, name)
end
end
const C4 = CompatibleCoreCompilerConstants()
function about(io::IO, effects::Core.Compiler.Effects)
function effectinfo(io::IO, field::Symbol, name::String, labels::Pair{<:Union{UInt8, Bool, Nothing}, AnnotatedString{String}}...;
prefix::AbstractString = "", suffix::AbstractString = "")
hasproperty(effects, field) || return
value = @static if VERSION >= v"1.9"
getproperty(effects, field)
else # v1.8
getproperty(effects, field).state
end
icon, accent = if value == C4.ALWAYS_TRUE || value === true
'✔', :success
elseif value == C4.ALWAYS_FALSE || value === false
'✗', :error
else
'~', :warning
end
msg = S"{bold,italic,grey:???}"
for (id, label) in labels
if id == value
msg = label
break
end
end
name_pad_width = 13
dispwidth = last(displaysize(io))
declr = S" {bold,$accent:$icon $(rpad(name, name_pad_width))} "
print(io, declr)
indent = name_pad_width + 5
desc = S"{grey:$prefix$(ifelse(isempty(prefix), \"\", \" \"))$msg$(ifelse(isempty(suffix), \"\", \" \"))$suffix}"
desclines = wraplines(desc, dispwidth - indent, indent)
for (i, line) in enumerate(desclines)
i > 1 && print(io, ' '^indent)
println(io, line)
end
end
println(io, S"{bold:Method effects:}")
effectinfo(io, :consistent, "consistent",
C4.ALWAYS_TRUE => S"guaranteed to",
C4.ALWAYS_FALSE => S"might {italic:not}",
C4.CONSISTENT_IF_NOTRETURNED => S"when the return value never involved newly allocated mutable objects, will",
C4.CONSISTENT_IF_INACCESSIBLEMEMONLY => S"when {code:inaccessible memory only} is also proven, will",
suffix = "return or terminate consistently")
effectinfo(io, :effect_free, "effect free",
C4.ALWAYS_TRUE => S"guaranteed to be",
C4.ALWAYS_FALSE => S"might {italic:not} be",
C4.EFFECT_FREE_IF_INACCESSIBLEMEMONLY => S"when {code:inaccessible memory only} is also proven, is",
suffix = "free from externally semantically visible side effects")
effectinfo(io, :nothrow, "no throw",
C4.ALWAYS_TRUE => S"guaranteed to never",
C4.ALWAYS_FALSE => S"may",
suffix = "throw an exception")
effectinfo(io, :terminates, "terminates",
C4.ALWAYS_TRUE => S"guaranteed to",
C4.ALWAYS_FALSE => S"might {italic:not}",
suffix = "always terminate")
effectinfo(io, :notaskstate, "no task state",
C4.ALWAYS_TRUE => S"guaranteed not to access task state (allowing migration between tasks)",
C4.ALWAYS_FALSE => S"may access task state (preventing migration between tasks)")
effectinfo(io, :inaccessiblememonly, "inaccessible memory only",
C4.ALWAYS_TRUE => S"guaranteed to never access or modify externally accessible mutable memory",
C4.ALWAYS_FALSE => S"may access or modify externally accessible mutable memory",
C4.INACCESSIBLEMEM_OR_ARGMEMONLY => S"may access or modify mutable memory {italic:iff} pointed to by its call arguments")
effectinfo(io, :noub, "no undefined behaviour",
C4.ALWAYS_TRUE => S"guaranteed to never",
C4.ALWAYS_FALSE => S"may",
C4.NOUB_IF_NOINBOUNDS => S"so long as {code,julia_macro:@inbounds} is not used or propagated, will not",
suffix = "execute undefined behaviour")
effectinfo(io, :nonoverlayed, "non-overlayed",
true => S"never calls any methods from an overlayed method table",
false => S"{warning:may} call methods from an overlayed method table")
end
end
about(io::IO, fn::Function, sig::NTuple{N, <:Type}) where {N} = about(io, fn, Tuple{sig...})
about(io::IO, fn::Function, sig::Type...) = about(io, fn, sig)