From 03dfc522c5f06a486a2ac07f07e52eb97ca9ae2f Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Tue, 3 Jul 2018 16:14:11 -0400 Subject: [PATCH] performance: mark REPL and Doc code as non-specializeable Implicit return is bad for compiler performance (and sometimes runtime performance) and can adversely affect code readability, so every function which does _not_ return a value should end in a `return` statement. Here, we also introduce a new meaning to the `@nospecialize` macro, and a new macro `@specialize` to reverse its effect. When used without arguments, it applies to all arguments of the parent. In local scope, this means the containing function. In global scope, this means all methods subsequently defined. --- NEWS.md | 3 + base/client.jl | 2 +- base/compiler/ssair/show.jl | 6 +- base/compiler/tfuncs.jl | 16 ++- base/docs/Docs.jl | 2 + base/docs/basedocs.jl | 8 +- base/docs/core.jl | 7 +- base/essentials.jl | 53 ++++++-- base/exports.jl | 1 + base/precompile.jl | 30 ++--- doc/src/base/base.md | 1 + src/ast.c | 14 +- src/ast.scm | 2 +- src/dump.c | 4 +- src/gf.c | 48 ++++--- src/interpreter.c | 12 +- src/julia-syntax.scm | 6 +- src/julia.h | 2 + src/julia_internal.h | 5 +- src/method.c | 48 ++++--- src/module.c | 6 + stdlib/REPL/src/LineEdit.jl | 248 +++++++++++++++++++++--------------- stdlib/REPL/src/REPL.jl | 65 +++++----- stdlib/REPL/src/docview.jl | 3 +- 24 files changed, 363 insertions(+), 229 deletions(-) diff --git a/NEWS.md b/NEWS.md index c6fe261d7d238..cc6d4e8c656dc 100644 --- a/NEWS.md +++ b/NEWS.md @@ -78,6 +78,9 @@ Language changes * Declaring arguments as `x::ANY` to avoid specialization has been replaced by `@nospecialize x`. ([#22666]). + This can also be used in global scope, to apply to all subsequent method definitions + in the module (until `@specialize`). ([#28065]) + * Keyword argument default values are now evaluated in successive scopes --- the scope for each expression includes only previous keyword arguments, in left-to-right order ([#17240]). diff --git a/base/client.jl b/base/client.jl index da6150bf8eaab..462c2ccdd34e1 100644 --- a/base/client.jl +++ b/base/client.jl @@ -349,7 +349,7 @@ function run_main_repl(interactive::Bool, quiet::Bool, banner::Bool, history_fil term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb") term = REPL.Terminals.TTYTerminal(term_env, stdin, stdout, stderr) color_set || (global have_color = REPL.Terminals.hascolor(term)) - banner && REPL.banner(term, term) + banner && Base.banner(term) if term.term_type == "dumb" active_repl = REPL.BasicREPL(term) quiet || @warn "Terminal not fully functional" diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index 5a2e153f83c7d..0bfbffc3b847e 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -1,5 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +@nospecialize + if Pair != Base.Pair import Base: Base, IOContext, string, join, sprint IOContext(io::IO, KV::Pair) = IOContext(io, Base.Pair(KV[1], KV[2])) @@ -26,7 +28,7 @@ end print_ssa(io::IO, @nospecialize(val), argnames) = Base.show(io, val) -function print_node(io::IO, idx::Int, @nospecialize(stmt), used, argnames, maxsize; color = true, print_typ=true) +function print_node(io::IO, idx::Int, @nospecialize(stmt), used, argnames, maxsize; color::Bool=true, print_typ::Bool=true) if idx in used pad = " "^(maxsize-length(string(idx))) Base.print(io, "%$idx $pad= ") @@ -428,3 +430,5 @@ function show_ir(io::IO, code::IRCode, expr_type_printer=default_expr_type_print println(io) end end + +@specialize diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index ae67100538b94..0b429d87dbc82 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -4,6 +4,8 @@ # constants # ############# +@nospecialize + const AbstractEvalConstant = Const const _NAMEDTUPLE_NAME = NamedTuple.body.body.name @@ -345,9 +347,9 @@ add_tfunc(nfields, 1, 1, end return Int end, 0) -add_tfunc(Core._expr, 1, INT_INF, (args...)->Expr, 100) +add_tfunc(Core._expr, 1, INT_INF, (@nospecialize args...)->Expr, 100) add_tfunc(applicable, 1, INT_INF, (@nospecialize(f), args...)->Bool, 100) -add_tfunc(Core.Intrinsics.arraylen, 1, 1, x->Int, 4) +add_tfunc(Core.Intrinsics.arraylen, 1, 1, @nospecialize(x)->Int, 4) add_tfunc(arraysize, 2, 2, (@nospecialize(a), @nospecialize(d))->Int, 4) add_tfunc(pointerref, 3, 3, function (@nospecialize(a), @nospecialize(i), @nospecialize(align)) @@ -466,7 +468,7 @@ add_tfunc(<:, 2, 2, return Bool end, 0) -function const_datatype_getfield_tfunc(sv, fld) +function const_datatype_getfield_tfunc(@nospecialize(sv), @nospecialize(fld)) if (fld == DATATYPE_NAME_FIELDINDEX || fld == DATATYPE_PARAMETERS_FIELDINDEX || fld == DATATYPE_TYPES_FIELDINDEX || @@ -980,7 +982,7 @@ function tuple_tfunc(@nospecialize(argtype)) return argtype end -function array_builtin_common_nothrow(argtypes, first_idx_idx) +function array_builtin_common_nothrow(argtypes::Array{Any,1}, first_idx_idx::Int) length(argtypes) >= 4 || return false (argtypes[1] ⊑ Bool && argtypes[2] ⊑ Array) || return false for i = first_idx_idx:length(argtypes) @@ -1052,7 +1054,7 @@ function builtin_tfunction(@nospecialize(f), argtypes::Array{Any,1}, return tuple_tfunc(argtypes_to_type(argtypes)) end end - return Const(tuple(anymap(a->a.val, argtypes)...)) + return Const(tuple(anymap(a::Const -> a.val, argtypes)...)) elseif f === svec return SimpleVector elseif f === arrayset @@ -1111,7 +1113,7 @@ function builtin_tfunction(@nospecialize(f), argtypes::Array{Any,1}, end if isa(f, IntrinsicFunction) if is_pure_intrinsic_infer(f) && _all(@nospecialize(a) -> isa(a, Const), argtypes) - argvals = anymap(a -> a.val, argtypes) + argvals = anymap(a::Const -> a.val, argtypes) try return Const(f(argvals...)) catch @@ -1193,3 +1195,5 @@ function typename_static(@nospecialize(t)) t = unwrap_unionall(widenconst(t)) return isType(t) ? _typename(t.parameters[1]) : Core.TypeName end + +@specialize diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index 4fc99b6156887..5e32ec3a0f776 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -11,6 +11,8 @@ information. """ module Docs +@nospecialize # don't specialize on any arguments of the methods declared herein + """ # Documentation diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index f5ef2aef631ac..74fe53e5e5f54 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -2,10 +2,14 @@ module BaseDocs +@nospecialize # don't specialize on any arguments of the methods declared herein + struct Keyword - name :: Symbol + name::Symbol +end +macro kw_str(text) + return Keyword(Symbol(text)) end -macro kw_str(text) Keyword(Symbol(text)) end """ **Welcome to Julia $(string(VERSION)).** The full manual is available at diff --git a/base/docs/core.jl b/base/docs/core.jl index af784f2574200..718e49917632f 100644 --- a/base/docs/core.jl +++ b/base/docs/core.jl @@ -4,17 +4,18 @@ module CoreDocs import ..esc, ..push!, ..getindex, ..unsafe_load, ..Csize_t, ..@nospecialize +@nospecialize # don't specialize on any arguments of the methods declared herein + function doc!(source::LineNumberNode, mod::Module, str, ex) - @nospecialize str ex push!(DOCS, Core.svec(mod, ex, str, source.file, source.line)) nothing end const DOCS = Array{Core.SimpleVector,1}() -isexpr(@nospecialize(x), h::Symbol) = isa(x, Expr) && x.head === h +isexpr(x, h::Symbol) = isa(x, Expr) && x.head === h lazy_iterpolate(s::AbstractString) = Expr(:call, Core.svec, s) -lazy_iterpolate(@nospecialize x) = isexpr(x, :string) ? Expr(:call, Core.svec, x.args...) : x +lazy_iterpolate(x) = isexpr(x, :string) ? Expr(:call, Core.svec, x.args...) : x function docm(source::LineNumberNode, mod::Module, str, x) out = Expr(:call, doc!, QuoteNode(source), mod, lazy_iterpolate(str), QuoteNode(x)) diff --git a/base/essentials.jl b/base/essentials.jl index 3621beecabe26..603c5248843b2 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -30,15 +30,21 @@ end @nospecialize Applied to a function argument name, hints to the compiler that the method -should not be specialized for different types of that argument. +should not be specialized for different types of that argument, +but instead to use precisely the declared type for each argument. This is only a hint for avoiding excess code generation. -Can be applied to an argument within a formal argument list, or in the -function body. -When applied to an argument, the macro must wrap the entire argument -expression. +Can be applied to an argument within a formal argument list, +or in the function body. +When applied to an argument, the macro must wrap the entire argument expression. When used in a function body, the macro must occur in statement position and before any code. +When used without arguments, it applies to all arguments of the parent scope. +In local scope, this means all arguments of the containing function. +In global (top-level) scope, this means all methods subsequently defined in the current module. + +Specialization can reset back to the default by using [`@specialize`](@ref). + ```julia function example_function(@nospecialize x) ... @@ -52,21 +58,46 @@ function example_function(x, y, z) @nospecialize x y ... end + +@nospecialize +f(y) = [x for x in y] +@specialize ``` """ -macro nospecialize(var, vars...) - if isa(var, Expr) && var.head === :(=) - var.head = :kw +macro nospecialize(vars...) + if nfields(vars) === 1 + # in argument position, need to fix `@nospecialize x=v` to `@nospecialize (kw x v)` + var = getfield(vars, 1) + if isa(var, Expr) && var.head === :(=) + var.head = :kw + end + end + return Expr(:meta, :nospecialize, vars...) +end + +""" + @specialize + +Reset the specialization hint for an argument back to the default. +For details, see [`@specialize`](@ref). +""" +macro specialize(vars...) + if nfields(vars) === 1 + # in argument position, need to fix `@specialize x=v` to `@specialize (kw x v)` + var = getfield(vars, 1) + if isa(var, Expr) && var.head === :(=) + var.head = :kw + end end - Expr(:meta, :nospecialize, var, vars...) + return Expr(:meta, :specialize, vars...) end macro _pure_meta() - Expr(:meta, :pure) + return Expr(:meta, :pure) end # another version of inlining that propagates an inbounds context macro _propagate_inbounds_meta() - Expr(:meta, :inline, :propagate_inbounds) + return Expr(:meta, :inline, :propagate_inbounds) end """ diff --git a/base/exports.jl b/base/exports.jl index d87889eb047df..958fd6a299937 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -970,6 +970,7 @@ export @inline, @noinline, @nospecialize, + @specialize, @polly, @assert, diff --git a/base/precompile.jl b/base/precompile.jl index 4889e1729d20e..715c376774ca6 100644 --- a/base/precompile.jl +++ b/base/precompile.jl @@ -247,9 +247,9 @@ precompile(Tuple{typeof(Base.getproperty), Core.CodeInfo, Symbol}) precompile(Tuple{typeof(Base.getproperty), Core.LineInfoNode, Symbol}) precompile(Tuple{typeof(Base.getproperty), Core.MethodTable, Symbol}) precompile(Tuple{typeof(Base.getproperty), Method, Symbol}) -precompile(Tuple{typeof(Base.getproperty), REPL.LineEdit.HistoryPrompt{REPL.REPLHistoryProvider}, Symbol}) +precompile(Tuple{typeof(Base.getproperty), REPL.LineEdit.HistoryPrompt, Symbol}) precompile(Tuple{typeof(Base.getproperty), REPL.LineEdit.ModalInterface, Symbol}) -precompile(Tuple{typeof(Base.getproperty), REPL.LineEdit.PrefixHistoryPrompt{REPL.REPLHistoryProvider}, Symbol}) +precompile(Tuple{typeof(Base.getproperty), REPL.LineEdit.PrefixHistoryPrompt, Symbol}) precompile(Tuple{typeof(Base.getproperty), REPL.LineEdit.PrefixSearchState, Symbol}) precompile(Tuple{typeof(Base.getproperty), REPL.LineEdit.Prompt, Symbol}) precompile(Tuple{typeof(Base.getproperty), REPL.LineEdit.PromptState, Symbol}) @@ -440,7 +440,7 @@ precompile(Tuple{typeof(Base.setproperty!), Base.Iterators.Stateful{Tuple{String precompile(Tuple{typeof(Base.setproperty!), Base.Process, Symbol, Ptr{Nothing}}) precompile(Tuple{typeof(Base.setproperty!), Base.Process, Symbol, Symbol}) precompile(Tuple{typeof(Base.setproperty!), REPL.LineEdit.MIState, Symbol, Symbol}) -precompile(Tuple{typeof(Base.setproperty!), REPL.LineEdit.PrefixHistoryPrompt{REPL.REPLHistoryProvider}, Symbol, REPL.LineEdit.Prompt}) +precompile(Tuple{typeof(Base.setproperty!), REPL.LineEdit.PrefixHistoryPrompt, Symbol, REPL.LineEdit.Prompt}) precompile(Tuple{typeof(Base.setproperty!), REPL.LineEdit.PrefixSearchState, Symbol, Int64}) precompile(Tuple{typeof(Base.setproperty!), REPL.LineEdit.PrefixSearchState, Symbol, REPL.LineEdit.MIState}) precompile(Tuple{typeof(Base.setproperty!), REPL.LineEdit.PrefixSearchState, Symbol, REPL.LineEdit.Prompt}) @@ -618,9 +618,9 @@ precompile(Tuple{typeof(Markdown.terminline_string), Base.IOContext{REPL.Termina precompile(Tuple{typeof(OldPkg.dir)}) precompile(Tuple{typeof(Pkg.REPLMode.create_mode), REPL.LineEditREPL, REPL.LineEdit.Prompt}) precompile(Tuple{typeof(Pkg.REPLMode.repl_init), REPL.LineEditREPL}) -precompile(Tuple{typeof(REPL.LineEdit.accept_result), REPL.LineEdit.MIState, REPL.LineEdit.PrefixHistoryPrompt{REPL.REPLHistoryProvider}}) -precompile(Tuple{typeof(REPL.LineEdit.activate), REPL.LineEdit.HistoryPrompt{REPL.REPLHistoryProvider}, REPL.LineEdit.SearchState, REPL.Terminals.TerminalBuffer, REPL.Terminals.TTYTerminal}) -precompile(Tuple{typeof(REPL.LineEdit.activate), REPL.LineEdit.PrefixHistoryPrompt{REPL.REPLHistoryProvider}, REPL.LineEdit.PrefixSearchState, REPL.Terminals.TerminalBuffer, REPL.Terminals.TTYTerminal}) +precompile(Tuple{typeof(REPL.LineEdit.accept_result), REPL.LineEdit.MIState, REPL.LineEdit.PrefixHistoryPrompt}) +precompile(Tuple{typeof(REPL.LineEdit.activate), REPL.LineEdit.HistoryPrompt, REPL.LineEdit.SearchState, REPL.Terminals.TerminalBuffer, REPL.Terminals.TTYTerminal}) +precompile(Tuple{typeof(REPL.LineEdit.activate), REPL.LineEdit.PrefixHistoryPrompt, REPL.LineEdit.PrefixSearchState, REPL.Terminals.TerminalBuffer, REPL.Terminals.TTYTerminal}) precompile(Tuple{typeof(REPL.LineEdit.activate), REPL.LineEdit.Prompt, REPL.LineEdit.MIState, REPL.Terminals.TTYTerminal, REPL.Terminals.TTYTerminal}) precompile(Tuple{typeof(REPL.LineEdit.activate), REPL.LineEdit.Prompt, REPL.LineEdit.PromptState, REPL.Terminals.TTYTerminal, REPL.Terminals.TTYTerminal}) precompile(Tuple{typeof(REPL.LineEdit.activate), REPL.LineEdit.Prompt, REPL.LineEdit.PromptState, REPL.Terminals.TerminalBuffer, REPL.Terminals.TTYTerminal}) @@ -635,8 +635,8 @@ precompile(Tuple{typeof(REPL.LineEdit.commit_changes), REPL.Terminals.TTYTermina precompile(Tuple{typeof(REPL.LineEdit.common_prefix), Array{String, 1}}) precompile(Tuple{typeof(REPL.LineEdit.complete_line), REPL.LineEdit.PromptState, Int64}) precompile(Tuple{typeof(REPL.LineEdit.complete_line), REPL.REPLCompletionProvider, REPL.LineEdit.PromptState}) -precompile(Tuple{typeof(REPL.LineEdit.deactivate), REPL.LineEdit.HistoryPrompt{REPL.REPLHistoryProvider}, REPL.LineEdit.SearchState, REPL.Terminals.TerminalBuffer, REPL.Terminals.TTYTerminal}) -precompile(Tuple{typeof(REPL.LineEdit.deactivate), REPL.LineEdit.PrefixHistoryPrompt{REPL.REPLHistoryProvider}, REPL.LineEdit.PrefixSearchState, REPL.Terminals.TerminalBuffer, REPL.Terminals.TTYTerminal}) +precompile(Tuple{typeof(REPL.LineEdit.deactivate), REPL.LineEdit.HistoryPrompt, REPL.LineEdit.SearchState, REPL.Terminals.TerminalBuffer, REPL.Terminals.TTYTerminal}) +precompile(Tuple{typeof(REPL.LineEdit.deactivate), REPL.LineEdit.PrefixHistoryPrompt, REPL.LineEdit.PrefixSearchState, REPL.Terminals.TerminalBuffer, REPL.Terminals.TTYTerminal}) precompile(Tuple{typeof(REPL.LineEdit.deactivate), REPL.LineEdit.Prompt, REPL.LineEdit.PromptState, REPL.Terminals.TerminalBuffer, REPL.Terminals.TTYTerminal}) precompile(Tuple{typeof(REPL.LineEdit.deactivate_region), REPL.LineEdit.MIState}) precompile(Tuple{typeof(REPL.LineEdit.deactivate_region), REPL.LineEdit.PromptState}) @@ -650,14 +650,14 @@ precompile(Tuple{typeof(REPL.LineEdit.edit_splice!), REPL.LineEdit.PromptState, precompile(Tuple{typeof(REPL.LineEdit.eval), Module, Expr}) precompile(Tuple{typeof(REPL.LineEdit.history_next_prefix), REPL.LineEdit.PrefixSearchState, REPL.REPLHistoryProvider, String}) precompile(Tuple{typeof(REPL.LineEdit.history_prev_prefix), REPL.LineEdit.PrefixSearchState, REPL.REPLHistoryProvider, String}) -precompile(Tuple{typeof(REPL.LineEdit.init_state), REPL.Terminals.TTYTerminal, REPL.LineEdit.HistoryPrompt{REPL.REPLHistoryProvider}}) +precompile(Tuple{typeof(REPL.LineEdit.init_state), REPL.Terminals.TTYTerminal, REPL.LineEdit.HistoryPrompt}) precompile(Tuple{typeof(REPL.LineEdit.init_state), REPL.Terminals.TTYTerminal, REPL.LineEdit.ModalInterface}) -precompile(Tuple{typeof(REPL.LineEdit.init_state), REPL.Terminals.TTYTerminal, REPL.LineEdit.PrefixHistoryPrompt{REPL.REPLHistoryProvider}}) +precompile(Tuple{typeof(REPL.LineEdit.init_state), REPL.Terminals.TTYTerminal, REPL.LineEdit.PrefixHistoryPrompt}) precompile(Tuple{typeof(REPL.LineEdit.is_region_active), REPL.LineEdit.MIState}) -precompile(Tuple{typeof(REPL.LineEdit.keymap), REPL.LineEdit.PrefixSearchState, REPL.LineEdit.PrefixHistoryPrompt{REPL.REPLHistoryProvider}}) -precompile(Tuple{typeof(REPL.LineEdit.keymap), REPL.LineEdit.SearchState, REPL.LineEdit.HistoryPrompt{REPL.REPLHistoryProvider}}) -precompile(Tuple{typeof(REPL.LineEdit.keymap_data), REPL.LineEdit.PrefixSearchState, REPL.LineEdit.PrefixHistoryPrompt{REPL.REPLHistoryProvider}}) -precompile(Tuple{typeof(REPL.LineEdit.keymap_data), REPL.LineEdit.SearchState, REPL.LineEdit.HistoryPrompt{REPL.REPLHistoryProvider}}) +precompile(Tuple{typeof(REPL.LineEdit.keymap), REPL.LineEdit.PrefixSearchState, REPL.LineEdit.PrefixHistoryPrompt}) +precompile(Tuple{typeof(REPL.LineEdit.keymap), REPL.LineEdit.SearchState, REPL.LineEdit.HistoryPrompt}) +precompile(Tuple{typeof(REPL.LineEdit.keymap_data), REPL.LineEdit.PrefixSearchState, REPL.LineEdit.PrefixHistoryPrompt}) +precompile(Tuple{typeof(REPL.LineEdit.keymap_data), REPL.LineEdit.SearchState, REPL.LineEdit.HistoryPrompt}) precompile(Tuple{typeof(REPL.LineEdit.match_input), Base.Dict{Char, Any}, REPL.LineEdit.MIState, Base.GenericIOBuffer{Array{UInt8, 1}}}) precompile(Tuple{typeof(REPL.LineEdit.match_input), Base.Dict{Char, Any}, REPL.LineEdit.MIState, REPL.Terminals.TTYTerminal, Array{Char, 1}, Base.Dict{Char, Any}}) precompile(Tuple{typeof(REPL.LineEdit.match_input), Base.Dict{Char, Any}, REPL.LineEdit.MIState}) @@ -705,7 +705,7 @@ precompile(Tuple{typeof(REPL.Terminals.cmove_col), REPL.Terminals.TTYTerminal, I precompile(Tuple{typeof(REPL.Terminals.cmove_down), REPL.Terminals.TTYTerminal, Int64}) precompile(Tuple{typeof(REPL.Terminals.hascolor), REPL.Terminals.TTYTerminal}) precompile(Tuple{typeof(REPL.Terminals.width), REPL.Terminals.TTYTerminal}) -precompile(Tuple{typeof(REPL.banner), REPL.Terminals.TTYTerminal, REPL.Terminals.TTYTerminal}) +precompile(Tuple{typeof(Base.banner), REPL.Terminals.TTYTerminal}) precompile(Tuple{typeof(REPL.ends_with_semicolon), String}) precompile(Tuple{typeof(REPL.eval), Module, Expr}) precompile(Tuple{typeof(REPL.eval), Module, String}) diff --git a/doc/src/base/base.md b/doc/src/base/base.md index 44d0fa2c2eb92..00ed2db829336 100644 --- a/doc/src/base/base.md +++ b/doc/src/base/base.md @@ -213,6 +213,7 @@ Base.@boundscheck Base.@inline Base.@noinline Base.@nospecialize +Base.@specialize Base.gensym Base.@gensym Base.@goto diff --git a/src/ast.c b/src/ast.c index d19e4713e73ad..5bb9889911380 100644 --- a/src/ast.c +++ b/src/ast.c @@ -60,12 +60,13 @@ jl_sym_t *inert_sym; jl_sym_t *vararg_sym; jl_sym_t *unused_sym; jl_sym_t *static_parameter_sym; jl_sym_t *inline_sym; jl_sym_t *noinline_sym; jl_sym_t *polly_sym; -jl_sym_t *propagate_inbounds_sym; jl_sym_t *generated_sym; -jl_sym_t *generated_only_sym; -jl_sym_t *isdefined_sym; jl_sym_t *nospecialize_sym; -jl_sym_t *macrocall_sym; jl_sym_t *colon_sym; -jl_sym_t *hygienicscope_sym; -jl_sym_t *escape_sym; +jl_sym_t *propagate_inbounds_sym; +jl_sym_t *generated_sym; jl_sym_t *generated_only_sym; +jl_sym_t *isdefined_sym; +jl_sym_t *specialize_sym; jl_sym_t *nospecialize_sym; +jl_sym_t *macrocall_sym; +jl_sym_t *colon_sym; +jl_sym_t *hygienicscope_sym; jl_sym_t *escape_sym; jl_sym_t *gc_preserve_begin_sym; jl_sym_t *gc_preserve_end_sym; jl_sym_t *throw_undef_if_not_sym; jl_sym_t *getfield_undefref_sym; @@ -384,6 +385,7 @@ void jl_init_frontend(void) propagate_inbounds_sym = jl_symbol("propagate_inbounds"); isdefined_sym = jl_symbol("isdefined"); nospecialize_sym = jl_symbol("nospecialize"); + specialize_sym = jl_symbol("specialize"); macrocall_sym = jl_symbol("macrocall"); escape_sym = jl_symbol("escape"); hygienicscope_sym = jl_symbol("hygienic-scope"); diff --git a/src/ast.scm b/src/ast.scm index af762c4b5d0f5..7ceb85fcd9562 100644 --- a/src/ast.scm +++ b/src/ast.scm @@ -465,7 +465,7 @@ (define (nospecialize-meta? e (one #f)) (and (if one (length= e 3) (length> e 2)) - (eq? (car e) 'meta) (eq? (cadr e) 'nospecialize))) + (eq? (car e) 'meta) (memq (cadr e) '(nospecialize specialize)))) (define (if-generated? e) (and (length= e 4) (eq? (car e) 'if) (equal? (cadr e) '(generated)))) diff --git a/src/dump.c b/src/dump.c index c5073c1bff113..d2234a88ccc65 100644 --- a/src/dump.c +++ b/src/dump.c @@ -443,6 +443,7 @@ static void jl_serialize_module(jl_serializer_state *s, jl_module_t *m) write_uint64(s->s, m->uuid.lo); write_uint64(s->s, m->build_id); write_int32(s->s, m->counter); + write_int32(s->s, m->nospecialize); } static int is_ast_node(jl_value_t *v) @@ -1781,8 +1782,9 @@ static jl_value_t *jl_deserialize_value_module(jl_serializer_state *s) m->uuid.hi = read_uint64(s->s); m->uuid.lo = read_uint64(s->s); m->build_id = read_uint64(s->s); - m->primary_world = jl_world_counter; m->counter = read_int32(s->s); + m->nospecialize = read_int32(s->s); + m->primary_world = jl_world_counter; return (jl_value_t*)m; } diff --git a/src/gf.c b/src/gf.c index 8a108087bee43..b6b1d450b9eba 100644 --- a/src/gf.c +++ b/src/gf.c @@ -17,7 +17,7 @@ #endif #include "julia_assert.h" -// @nospecialize has no effect if the number of overlapping methods is greater than this +// The compilation signature is not used to cache the method if the number of overlapping methods is greater than this #define MAX_UNSPECIALIZED_CONFLICTS 32 #ifdef __cplusplus @@ -202,7 +202,8 @@ JL_DLLEXPORT jl_value_t *jl_methtable_lookup(jl_methtable_t *mt, jl_value_t *typ // ----- MethodInstance specialization instantiation ----- // -JL_DLLEXPORT jl_method_t *jl_new_method_uninit(void); +JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t*); + void jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_args_t fptr) { jl_sym_t *sname = jl_symbol(name); @@ -219,7 +220,7 @@ void jl_mk_builtin_func(jl_datatype_t *dt, const char *name, jl_fptr_args_t fptr li->max_world = ~(size_t)0; JL_GC_PUSH1(&li); - jl_method_t *m = jl_new_method_uninit(); + jl_method_t *m = jl_new_method_uninit(jl_core_module); li->def.method = m; jl_gc_wb(li, m); m->name = sname; @@ -632,17 +633,13 @@ static void jl_compilation_sig( if (i_arg > 0 && i_arg <= sizeof(definition->nospecialize) * 8 && (definition->nospecialize & (1 << (i_arg - 1)))) { - if (decl_i == (jl_value_t*)jl_any_type) { - if (!*newparams) *newparams = jl_svec_copy(tt->parameters); - jl_svecset(*newparams, i, (jl_value_t*)jl_any_type); - continue; - } - if (decl_i == (jl_value_t*)jl_tuple_type) { - if (!*newparams) *newparams = jl_svec_copy(tt->parameters); - jl_svecset(*newparams, i, (jl_value_t*)jl_tuple_type); + if (!jl_has_free_typevars(decl_i) && !jl_is_kind(decl_i)) { + if (decl_i != elt) { + if (!*newparams) *newparams = jl_svec_copy(tt->parameters); + jl_svecset(*newparams, i, (jl_value_t*)decl_i); + } continue; } - // TODO: handle @nospecialize with other declared types } if (jl_is_type_type(elt)) { @@ -823,17 +820,11 @@ JL_DLLEXPORT int jl_isa_compileable_sig( if (i_arg > 0 && i_arg <= sizeof(definition->nospecialize) * 8 && (definition->nospecialize & (1 << (i_arg - 1)))) { - if (decl_i == (jl_value_t*)jl_any_type) { - if (elt == (jl_value_t*)jl_any_type) + if (!jl_has_free_typevars(decl_i) && !jl_is_kind(decl_i)) { + if (jl_egal(elt, decl_i)) continue; return 0; } - if (decl_i == (jl_value_t*)jl_tuple_type) { - if (elt == (jl_value_t*)jl_tuple_type) - continue; - return 0; - } - // TODO: handle @nospecialize with other declared types } if (jl_is_kind(elt)) { @@ -1049,7 +1040,7 @@ static jl_method_instance_t *cache_method( if (!newparams) newparams = jl_svec_copy(cachett->parameters); jl_svecset(newparams, i, kind); } - else if (!jl_is_concrete_type(elt)) { // currently just jl_function_type and jl_tuple_type + else if (!jl_is_concrete_type(elt)) { // for example, jl_function_type or jl_tuple_type if (!newparams) newparams = jl_svec_copy(cachett->parameters); jl_svecset(newparams, i, jl_any_type); } @@ -1935,6 +1926,21 @@ JL_DLLEXPORT int jl_compile_hint(jl_tupletype_t *types) } } } + // In addition to full compilation of the compilation-signature, if `types` is more specific (e.g. due to nospecialize), + // also run inference now on the original `types`, since that may help us guide inference to find + // additional useful methods that should be compiled + //ALT: if (jl_is_datatype(types) && ((jl_datatype_t*)types)->isdispatchtuple && !jl_egal(li->specTypes, types)) + //ALT: if (jl_subtype(types, li->specTypes)) + if (!jl_subtype(li->specTypes, (jl_value_t*)types)) { + jl_svec_t *tpenv2 = jl_emptysvec; + jl_value_t *types2 = NULL; + JL_GC_PUSH2(&tpenv2, &types2); + types2 = jl_type_intersection_env((jl_value_t*)types, (jl_value_t*)li->def.method->sig, &tpenv2); + jl_method_instance_t *li2 = jl_specializations_get_linfo(li->def.method, (jl_value_t*)types2, tpenv2, world); + JL_GC_POP(); + if (!jl_is_rettype_inferred(li2)) + (void)jl_type_infer(&li2, world, 0); + } } else { // Otherwise (this branch), assuming we are at runtime (normal JIT) and diff --git a/src/interpreter.c b/src/interpreter.c index 11fdec9d8c7fa..33676ec383097 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -511,8 +511,8 @@ SECT_INTERP static jl_value_t *eval_value(jl_value_t *e, interpreter_state *s) else if (head == boundscheck_sym) { return jl_true; } - else if (head == boundscheck_sym || head == inbounds_sym || head == fastmath_sym || - head == simdloop_sym || head == meta_sym) { + else if (head == meta_sym || head == inbounds_sym || + head == fastmath_sym || head == simdloop_sym) { return jl_nothing; } else if (head == gc_preserve_begin_sym || head == gc_preserve_end_sym) { @@ -671,6 +671,14 @@ SECT_INTERP static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s else if (jl_is_toplevel_only_expr(stmt)) { jl_toplevel_eval(s->module, stmt); } + else if (head == meta_sym) { + if (jl_expr_nargs(stmt) == 1 && jl_exprarg(stmt, 0) == (jl_value_t*)nospecialize_sym) { + jl_set_module_nospecialize(s->module, 1); + } + if (jl_expr_nargs(stmt) == 1 && jl_exprarg(stmt, 0) == (jl_value_t*)specialize_sym) { + jl_set_module_nospecialize(s->module, 0); + } + } else { eval_stmt_value(stmt, s); } diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 67ee14ab8b9fa..e688af90847d8 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -436,7 +436,7 @@ keynames)) ;; list of function's initial line number and meta nodes (empty if none) (prologue (extract-method-prologue body)) - (annotations (map (lambda (a) `(meta nospecialize ,(arg-name (cadr (caddr a))))) + (annotations (map (lambda (a) `(meta ,(cadr a) ,(arg-name (cadr (caddr a))))) (filter nospecialize-meta? kargl))) ;; body statements (stmts (cdr body)) @@ -1035,7 +1035,7 @@ (string "function Base.broadcast(::typeof(" (deparse op_) "), ...)") #f)) op_)) (name (if op '(|.| Base (inert broadcast)) name)) - (annotations (map (lambda (a) `(meta nospecialize ,(arg-name a))) + (annotations (map (lambda (a) `(meta ,(cadr a) ,(arg-name (caddr a)))) (filter nospecialize-meta? argl))) (body (insert-after-meta (caddr e) annotations)) (argl (map (lambda (a) @@ -4152,7 +4152,7 @@ f(x) = yt(x) (cadr e)) ((nospecialize-meta? e) ;; convert nospecialize vars to slot numbers - `(meta nospecialize ,@(map renumber-stuff (cddr e)))) + `(meta ,(cadr e) ,@(map renumber-stuff (cddr e)))) ((or (atom? e) (quoted? e) (eq? (car e) 'global)) e) ((ssavalue? e) diff --git a/src/julia.h b/src/julia.h index e5b1d86e1251e..1f4f8d489d66e 100644 --- a/src/julia.h +++ b/src/julia.h @@ -450,6 +450,7 @@ typedef struct _jl_module_t { jl_uuid_t uuid; size_t primary_world; uint32_t counter; + int32_t nospecialize; // global bit flags: initialization for new methods uint8_t istopmod; } jl_module_t; @@ -1312,6 +1313,7 @@ extern JL_DLLEXPORT jl_module_t *jl_core_module JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_module_t *jl_base_module JL_GLOBALLY_ROOTED; extern JL_DLLEXPORT jl_module_t *jl_top_module JL_GLOBALLY_ROOTED; JL_DLLEXPORT jl_module_t *jl_new_module(jl_sym_t *name); +JL_DLLEXPORT void jl_set_module_nospecialize(jl_module_t *self, int on); // get binding for reading JL_DLLEXPORT jl_binding_t *jl_get_binding(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var); JL_DLLEXPORT jl_binding_t *jl_get_binding_or_error(jl_module_t *m, jl_sym_t *var); diff --git a/src/julia_internal.h b/src/julia_internal.h index ede2b78d80758..51a2758910237 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -970,11 +970,10 @@ extern jl_sym_t *inline_sym; extern jl_sym_t *noinline_sym; extern jl_sym_t *polly_sym; extern jl_sym_t *propagate_inbounds_sym; extern jl_sym_t *isdefined_sym; -extern jl_sym_t *nospecialize_sym; +extern jl_sym_t *nospecialize_sym; extern jl_sym_t *specialize_sym; extern jl_sym_t *boundscheck_sym; extern jl_sym_t *gc_preserve_begin_sym; extern jl_sym_t *gc_preserve_end_sym; -extern jl_sym_t *generated_sym; -extern jl_sym_t *generated_only_sym; +extern jl_sym_t *generated_sym; extern jl_sym_t *generated_only_sym; extern jl_sym_t *throw_undef_if_not_sym; extern jl_sym_t *getfield_undefref_sym; diff --git a/src/method.c b/src/method.c index d29ae57e77d6c..485e66351129f 100644 --- a/src/method.c +++ b/src/method.c @@ -496,29 +496,37 @@ static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) for (i = 0; i < n; i++) { jl_value_t *st = jl_array_ptr_ref(stmts, i); if (jl_is_expr(st) && ((jl_expr_t*)st)->head == meta_sym) { - if (jl_expr_nargs(st) > 1 && jl_exprarg(st, 0) == (jl_value_t*)nospecialize_sym) { - for (size_t j=1; j < jl_expr_nargs(st); j++) { + size_t nargs = jl_expr_nargs(st); + if (nargs >= 1 && jl_exprarg(st, 0) == (jl_value_t*)nospecialize_sym) { + if (nargs == 1) // bare `@nospecialize` is special: it prevents specialization on all args + m->nospecialize = -1; + size_t j; + for (j = 1; j < nargs; j++) { jl_value_t *aj = jl_exprarg(st, j); - if (jl_is_slot(aj)) { - int sn = (int)jl_slot_number(aj) - 2; - if (sn >= 0) { // @nospecialize on self is valid but currently ignored - if (sn > (m->nargs - 2)) { - jl_error("@nospecialize annotation applied to a non-argument"); - } - else if (sn >= sizeof(m->nospecialize) * 8) { - jl_printf(JL_STDERR, - "WARNING: @nospecialize annotation only supported on the first %d arguments.\n", - (int)(sizeof(m->nospecialize) * 8)); - } - else { - m->nospecialize |= (1 << sn); - } - } + if (!jl_is_slot(aj)) + continue; + int sn = (int)jl_slot_number(aj) - 2; + if (sn < 0) // @nospecialize on self is valid but currently ignored + continue; + if (sn > (m->nargs - 2)) { + jl_error("@nospecialize annotation applied to a non-argument"); } + if (sn >= sizeof(m->nospecialize) * 8) { + jl_printf(JL_STDERR, + "WARNING: @nospecialize annotation only supported on the first %d arguments.\n", + (int)(sizeof(m->nospecialize) * 8)); + continue; + } + m->nospecialize |= (1 << sn); } st = jl_nothing; } - else if (jl_expr_nargs(st) == 2 && jl_exprarg(st, 0) == (jl_value_t*)generated_sym) { + else if (nargs >= 1 && jl_exprarg(st, 0) == (jl_value_t*)specialize_sym) { + if (nargs == 1) // bare `@specialize` is special: it causes specialization on all args + m->nospecialize = 0; + st = jl_nothing; + } + else if (nargs == 2 && jl_exprarg(st, 0) == (jl_value_t*)generated_sym) { m->generator = NULL; jl_value_t *gexpr = jl_exprarg(st, 1); if (jl_expr_nargs(gexpr) == 7) { @@ -535,7 +543,7 @@ static void jl_method_set_source(jl_method_t *m, jl_code_info_t *src) } st = jl_nothing; } - else if (jl_expr_nargs(st) == 1 && jl_exprarg(st, 0) == (jl_value_t*)generated_only_sym) { + else if (nargs == 1 && jl_exprarg(st, 0) == (jl_value_t*)generated_only_sym) { gen_only = 1; st = jl_nothing; } @@ -574,7 +582,7 @@ JL_DLLEXPORT jl_method_t *jl_new_method_uninit(jl_module_t *module) m->file = empty_sym; m->line = 0; m->called = 0xff; - m->nospecialize = 0; + m->nospecialize = module->nospecialize; m->invokes.unknown = NULL; m->isva = 0; m->nargs = 0; diff --git a/src/module.c b/src/module.c index d529002d1d147..2afa015aace22 100644 --- a/src/module.c +++ b/src/module.c @@ -35,6 +35,7 @@ JL_DLLEXPORT jl_module_t *jl_new_module(jl_sym_t *name) m->build_id++; // build id 0 is invalid m->primary_world = 0; m->counter = 0; + m->nospecialize = 0; htable_new(&m->bindings, 0); arraylist_new(&m->usings, 0); if (jl_core_module) { @@ -63,6 +64,11 @@ JL_DLLEXPORT jl_value_t *jl_f_new_module(jl_sym_t *name, uint8_t std_imports) return (jl_value_t*)m; } +JL_DLLEXPORT void jl_set_module_nospecialize(jl_module_t *self, int on) +{ + self->nospecialize = (on ? -1 : 0); +} + JL_DLLEXPORT void jl_set_istopmod(jl_module_t *self, uint8_t isprimary) { self->istopmod = 1; diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 342d0fa82486b..3bbb550aad97e 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -16,6 +16,8 @@ abstract type ModeState end export run_interface, Prompt, ModalInterface, transition, reset_state, edit_insert, keymap +@nospecialize # use only declared type signatures + struct ModalInterface <: TextInterface modes::Vector{TextInterface} end @@ -94,6 +96,7 @@ function setmark(s::MIState, guess_region_active::Bool=true) guess_region_active && activate_region(s, s.key_repeats > 0 ? :mark : :off) mark(buffer(s)) was_active && refresh_line(s) + nothing end # the default mark is 0 @@ -113,6 +116,7 @@ content(s, reg::Region = 0=>bufend(s)) = String(buffer(s).data[axes(reg)]) function activate_region(s::PromptState, state::Symbol) @assert state in (:mark, :shift, :off) s.region_active = state + nothing end activate_region(s::ModeState, state::Symbol) = false @@ -174,6 +178,7 @@ function beep(s::PromptState, duration::Real=options(s).beep_duration, s.beeping = 0.0 unlock(s.refresh_lock) end + nothing end function cancel_beep(s::PromptState) @@ -183,6 +188,7 @@ function cancel_beep(s::PromptState) sleep(.05) end unlock(s.refresh_lock) + nothing end beep(::ModeState) = nothing @@ -249,6 +255,7 @@ function set_action!(s::MIState, command::Symbol) ## record current action s.current_action = command + nothing end set_action!(s, command::Symbol) = nothing @@ -299,10 +306,10 @@ end function complete_line(s::MIState) set_action!(s, :complete_line) if complete_line(state(s), s.key_repeats) - refresh_line(s) + return refresh_line(s) else beep(s) - :ignore + return :ignore end end @@ -317,7 +324,7 @@ function complete_line(s::PromptState, repeats) # Replace word by completion prev_pos = position(s) push_undo(s) - edit_splice!(s, prev_pos-sizeof(partial) => prev_pos, completions[1]) + edit_splice!(s, (prev_pos - sizeof(partial)) => prev_pos, completions[1]) else p = common_prefix(completions) if !isempty(p) && p != partial @@ -325,12 +332,12 @@ function complete_line(s::PromptState, repeats) # well complete that prev_pos = position(s) push_undo(s) - edit_splice!(s, prev_pos-sizeof(partial) => prev_pos, p) + edit_splice!(s, (prev_pos - sizeof(partial)) => prev_pos, p) elseif repeats > 0 show_completions(s, completions) end end - true + return true end clear_input_area(terminal, s) = (_clear_input_area(terminal, s.ias); s.ias = InputAreaState(0, 0)) @@ -349,6 +356,7 @@ function _clear_input_area(terminal, state::InputAreaState) # Clear top line clear_line(terminal) + nothing end prompt_string(s::PromptState) = prompt_string(s.p) @@ -442,7 +450,7 @@ function highlight_region(lwrite::String, regstart::Int, regstop::Int, written:: if written <= regstart <= written+slength lwrite = lwrite[1:regstart-written] * Base.text_colors[:reverse] * lwrite[regstart-written+1:end] end - lwrite + return lwrite end function refresh_multi_line(terminal::UnixTerminal, args...; kwargs...) @@ -463,7 +471,7 @@ function reset_key_repeats(f::Function, s::MIState) key_repeats_sav = s.key_repeats try s.key_repeats = 0 - f() + return f() finally s.key_repeats = key_repeats_sav end @@ -471,7 +479,7 @@ end function edit_exchange_point_and_mark(s::MIState) set_action!(s, :edit_exchange_point_and_mark) - edit_exchange_point_and_mark(buffer(s)) && refresh_line(s) + return edit_exchange_point_and_mark(buffer(s)) ? refresh_line(s) : false end function edit_exchange_point_and_mark(buf::IOBuffer) @@ -479,7 +487,7 @@ function edit_exchange_point_and_mark(buf::IOBuffer) m == position(buf) && return false mark(buf) seek(buf, m) - true + return true end char_move_left(s::PromptState) = char_move_left(s.input_buffer) @@ -492,7 +500,7 @@ function char_move_left(buf::IOBuffer) pos = position(buf) c = read(buf, Char) seek(buf, pos) - c + return c end function edit_move_left(buf::IOBuffer) @@ -509,18 +517,19 @@ function edit_move_left(buf::IOBuffer) return false end -edit_move_left(s::PromptState) = edit_move_left(s.input_buffer) && refresh_line(s) +edit_move_left(s::PromptState) = edit_move_left(s.input_buffer) ? refresh_line(s) : false function edit_move_word_left(s) if position(s) > 0 char_move_word_left(s.input_buffer) - refresh_line(s) + return refresh_line(s) end + return nothing end char_move_right(s) = char_move_right(buffer(s)) function char_move_right(buf::IOBuffer) - !eof(buf) && read(buf, Char) + return !eof(buf) && read(buf, Char) end function char_move_word_right(buf::IOBuffer, is_delimiter=is_non_word_char) @@ -565,13 +574,14 @@ function edit_move_right(buf::IOBuffer) end return false end -edit_move_right(s::PromptState) = edit_move_right(s.input_buffer) && refresh_line(s) +edit_move_right(s::PromptState) = edit_move_right(s.input_buffer) ? refresh_line(s) : false function edit_move_word_right(s) if !eof(s.input_buffer) char_move_word_right(s) - refresh_line(s) + return refresh_line(s) end + return nothing end ## Move line up/down @@ -599,7 +609,7 @@ function edit_move_up(s) set_action!(s, :edit_move_up) changed = edit_move_up(buffer(s)) changed && refresh_line(s) - changed + return changed end function edit_move_down(buf::IOBuffer) @@ -624,13 +634,13 @@ function edit_move_down(s) set_action!(s, :edit_move_down) changed = edit_move_down(buffer(s)) changed && refresh_line(s) - changed + return changed end function edit_shift_move(s::MIState, move_function::Function) @assert command_group(move_function) == :movement set_action!(s, Symbol(:shift_, move_function)) - move_function(s) + return move_function(s) end @@ -660,7 +670,7 @@ function edit_splice!(s, r::Region=region(s), ins::AbstractString = ""; rigid_ma ret = splice!(buf.data, A+1:B, codeunits(String(ins))) # position(), etc, are 0-indexed buf.size = buf.size + sizeof(ins) - B + A adjust_pos && seek(buf, position(buf) + sizeof(ins)) - String(ret) + return String(ret) end edit_splice!(s, ins::AbstractString) = edit_splice!(s, region(s), ins) @@ -715,10 +725,10 @@ function edit_backspace(s::PromptState, align::Bool=options(s).backspace_align, adjust=options(s).backspace_adjust) push_undo(s) if edit_backspace(buffer(s), align, adjust) - refresh_line(s) + return refresh_line(s) else pop_undo(s) - beep(s) + return beep(s) end end @@ -765,10 +775,10 @@ function edit_delete(s) set_action!(s, :edit_delete) push_undo(s) if edit_delete(buffer(s)) - refresh_line(s) + return refresh_line(s) else pop_undo(s) - beep(s) + return beep(s) end end @@ -777,24 +787,24 @@ function edit_delete(buf::IOBuffer) oldpos = position(buf) char_move_right(buf) edit_splice!(buf, oldpos => position(buf)) - true + return true end function edit_werase(buf::IOBuffer) pos1 = position(buf) char_move_word_left(buf, isspace) pos0 = position(buf) - edit_splice!(buf, pos0 => pos1) + return edit_splice!(buf, pos0 => pos1) end function edit_werase(s::MIState) set_action!(s, :edit_werase) push_undo(s) if push_kill!(s, edit_werase(buffer(s)), rev=true) - refresh_line(s) + return refresh_line(s) else pop_undo(s) - :ignore + return :ignore end end @@ -802,17 +812,17 @@ function edit_delete_prev_word(buf::IOBuffer) pos1 = position(buf) char_move_word_left(buf) pos0 = position(buf) - edit_splice!(buf, pos0 => pos1) + return edit_splice!(buf, pos0 => pos1) end function edit_delete_prev_word(s::MIState) set_action!(s, :edit_delete_prev_word) push_undo(s) if push_kill!(s, edit_delete_prev_word(buffer(s)), rev=true) - refresh_line(s) + return refresh_line(s) else pop_undo(s) - :ignore + return :ignore end end @@ -820,17 +830,17 @@ function edit_delete_next_word(buf::IOBuffer) pos0 = position(buf) char_move_word_right(buf) pos1 = position(buf) - edit_splice!(buf, pos0 => pos1) + return edit_splice!(buf, pos0 => pos1) end function edit_delete_next_word(s) set_action!(s, :edit_delete_next_word) push_undo(s) if push_kill!(s, edit_delete_next_word(buffer(s))) - refresh_line(s) + return refresh_line(s) else pop_undo(s) - :ignore + return :ignore end end @@ -843,7 +853,7 @@ function edit_yank(s::MIState) setmark(s) # necessary for edit_yank_pop push_undo(s) edit_insert(buffer(s), s.kill_ring[mod1(s.kill_idx, end)]) - refresh_line(s) + return refresh_line(s) end function edit_yank_pop(s::MIState, require_previous_yank=true) @@ -855,8 +865,8 @@ function edit_yank_pop(s::MIState, require_previous_yank=true) else require_previous_yank || repeat || setmark(s) push_undo(s) - edit_splice!(s, s.kill_ring[mod1(s.kill_idx-=1, end)]) - refresh_line(s) + edit_splice!(s, s.kill_ring[mod1(s.kill_idx -= 1, end)]) + return refresh_line(s) end end @@ -871,7 +881,7 @@ function push_kill!(s::MIState, killed::String, concat = s.key_repeats > 0; rev= length(s.kill_ring) > options(s).kill_ring_max && popfirst!(s.kill_ring) end s.kill_idx = lastindex(s.kill_ring) - true + return true end function edit_kill_line(s::MIState, backwards::Bool=false) @@ -889,7 +899,7 @@ function edit_kill_line(s::MIState, backwards::Bool=false) end push_undo(s) if push_kill!(s, edit_splice!(s, pos => endpos); rev=backwards) - refresh_line(s) + return refresh_line(s) else pop_undo(s) beep(s) @@ -909,13 +919,14 @@ function edit_copy_region(s::MIState) sleep(options(s).region_animation_duration) edit_exchange_point_and_mark(s) end + nothing end function edit_kill_region(s::MIState) set_action!(s, :edit_kill_region) push_undo(s) if push_kill!(s, edit_splice!(s), false) - refresh_line(s) + return refresh_line(s) else pop_undo(s) return :ignore @@ -925,7 +936,7 @@ end function edit_transpose_chars(s::MIState) set_action!(s, :edit_transpose_chars) push_undo(s) - edit_transpose_chars(buffer(s)) ? refresh_line(s) : pop_undo(s) + return edit_transpose_chars(buffer(s)) ? refresh_line(s) : pop_undo(s) end function edit_transpose_chars(buf::IOBuffer) @@ -942,7 +953,7 @@ end function edit_transpose_words(s) set_action!(s, :edit_transpose_words) push_undo(s) - edit_transpose_words(buffer(s)) ? refresh_line(s) : pop_undo(s) + return edit_transpose_words(buffer(s)) ? refresh_line(s) : pop_undo(s) end function edit_transpose_words(buf::IOBuffer, mode=:emacs) @@ -965,7 +976,7 @@ function edit_transpose_words(buf::IOBuffer, mode=:emacs) word2 = edit_splice!(buf, b2 => e2, content(buf, b1 => e1)) edit_splice!(buf, b1 => e1, word2) seek(buf, e2) - true + return true end @@ -981,7 +992,7 @@ function edit_transpose_lines_up!(buf::IOBuffer, reg::Region) b = lastindexline(buf, last(reg) - b2 + b1) # b2-b1 is the size of the removed line1 edit_splice!(buf, b => b, line1) seek(buf, pos) - true + return true end # swap all lines intersecting the region with line below @@ -993,7 +1004,7 @@ function edit_transpose_lines_down!(buf::IOBuffer, reg::Region) line2 = line2[2:end]*'\n' # don't include leading '\n' b = beginofline(buf, first(reg)) edit_splice!(buf, b => b, line2, rigid_mark=false) - true + return true end # return the region if active, or the current position as a Region otherwise @@ -1002,38 +1013,38 @@ region_if_active(s)::Region = is_region_active(s) ? region(s) : position(s)=>pos function edit_transpose_lines_up!(s::MIState) set_action!(s, :edit_transpose_lines_up!) if edit_transpose_lines_up!(buffer(s), region_if_active(s)) - refresh_line(s) + return refresh_line(s) else # beeping would be too noisy here - :ignore + return :ignore end end function edit_transpose_lines_down!(s::MIState) set_action!(s, :edit_transpose_lines_down!) if edit_transpose_lines_down!(buffer(s), region_if_active(s)) - refresh_line(s) + return refresh_line(s) else - :ignore + return :ignore end end function edit_upper_case(s) set_action!(s, :edit_upper_case) - edit_replace_word_right(s, uppercase) + return edit_replace_word_right(s, uppercase) end function edit_lower_case(s) set_action!(s, :edit_lower_case) - edit_replace_word_right(s, lowercase) + return edit_replace_word_right(s, lowercase) end function edit_title_case(s) set_action!(s, :edit_title_case) - edit_replace_word_right(s, uppercasefirst) + return edit_replace_word_right(s, uppercasefirst) end function edit_replace_word_right(s, replace::Function) push_undo(s) - edit_replace_word_right(buffer(s), replace) ? refresh_line(s) : pop_undo(s) + return edit_replace_word_right(buffer(s), replace) ? refresh_line(s) : pop_undo(s) end function edit_replace_word_right(buf::IOBuffer, replace::Function) @@ -1044,7 +1055,7 @@ function edit_replace_word_right(buf::IOBuffer, replace::Function) e = position(buf) e == b && return false edit_splice!(buf, b => e, replace(content(buf, b => e))) - true + return true end edit_clear(buf::IOBuffer) = truncate(buf, 0) @@ -1053,7 +1064,7 @@ function edit_clear(s::MIState) set_action!(s, :edit_clear) push_undo(s) if push_kill!(s, edit_splice!(s, 0 => bufend(s)), false) - refresh_line(s) + return refresh_line(s) else pop_undo(s) return :ignore @@ -1064,6 +1075,7 @@ function replace_line(s::PromptState, l::IOBuffer) empty_undo(s) s.input_buffer = copy(l) deactivate_region(s) + nothing end function replace_line(s::PromptState, l, keep_undo=false) @@ -1072,6 +1084,7 @@ function replace_line(s::PromptState, l, keep_undo=false) s.input_buffer.size = 0 write(s.input_buffer, l) deactivate_region(s) + nothing end @@ -1082,10 +1095,10 @@ function edit_indent(s::MIState, num::Int) set_action!(s, :edit_indent) push_undo(s) if edit_indent(buffer(s), num, is_region_active(s)) - refresh_line(s) + return refresh_line(s) else pop_undo(s) - :ignore + return :ignore end end @@ -1101,7 +1114,7 @@ function get_lines_in_region(s)::Vector{Int} # b < e ==> b+1 <= e <= buf.size push!(bol, b += 1) end - bol + return bol end # compute the number of spaces from b till the next non-space on the right @@ -1110,7 +1123,7 @@ function leadingspaces(buf::IOBuffer, b::Int)::Int ls = something(findnext(_notspace, buf.data, b+1), 0)-1 ls == -1 && (ls = buf.size) ls -= b - ls + return ls end # indent by abs(num) characters, on the right if num >= 0, on the left otherwise @@ -1127,7 +1140,7 @@ function edit_indent(buf::IOBuffer, num::Int, multiline::Bool)::Bool for b in reverse!(bol) # reverse! to not mess-up the bol's offsets _edit_indent(buf, b, num) end - true + return true end # indents line starting a position b by num positions @@ -1135,7 +1148,7 @@ end # at the beginning of line _edit_indent(buf::IOBuffer, b::Int, num::Int) = num >= 0 ? edit_splice!(buf, b => b, ' '^num, rigid_mark=false) : - edit_splice!(buf, b => b - num) + edit_splice!(buf, b => (b - num)) history_prev(::EmptyHistoryProvider) = ("", false) @@ -1157,6 +1170,7 @@ function history_prev(s, hist) else beep(s) end + nothing end function history_next(s, hist) l, ok = history_next(mode(s).hist) @@ -1167,6 +1181,7 @@ function history_next(s, hist) else beep(s) end + nothing end refresh_line(s) = refresh_multi_line(s) @@ -1185,14 +1200,14 @@ function write_prompt(terminal, p::Prompt) width = write_prompt(terminal, p.prompt) write(terminal, Base.text_colors[:normal]) write(terminal, suffix) - width + return width end # returns the width of the written prompt function write_prompt(terminal, s::Union{AbstractString,Function}) promptstr = prompt_string(s) write(terminal, promptstr) - textwidth(promptstr) + return textwidth(promptstr) end ### Keymap Support @@ -1379,6 +1394,7 @@ function fixup_keymaps!(dict::Dict, level, s, subkeymap) dict[s] = deepcopy(subkeymap) end end + nothing end function add_specialisations(dict, subdict, level) @@ -1450,7 +1466,7 @@ function keymap_merge(target,source) end add_nested_key!(ret, key, value; override = true) end - ret + return ret end function keymap_unify(keymaps) @@ -1480,7 +1496,7 @@ function keymap(keymaps::Array{<:Dict}) # keymaps is a vector of prioritized keymaps, with highest priority first ret = keymap_unify(map(normalize_keys, reverse(keymaps))) validate_keymap(ret) - ret + return ret end const escape_defaults = merge!( @@ -1540,15 +1556,18 @@ terminal(s::SearchState) = s.terminal function update_display_buffer(s::SearchState, data) history_search(data.histprompt.hp, data.query_buffer, data.response_buffer, data.backward, false) || beep(s) refresh_line(s) + nothing end function history_next_result(s::MIState, data::SearchState) history_search(data.histprompt.hp, data.query_buffer, data.response_buffer, data.backward, true) || beep(s) refresh_line(data) + nothing end function history_set_backward(s::SearchState, backward) s.backward = backward + nothing end input_string(s::SearchState) = String(take!(copy(s.query_buffer))) @@ -1563,16 +1582,16 @@ function reset_state(s::SearchState) s.response_buffer.ptr = 1 end reset_state(s.histprompt.hp) + nothing end -mutable struct HistoryPrompt{T<:HistoryProvider} <: TextInterface - hp::T +mutable struct HistoryPrompt <: TextInterface + hp # ::HistoryProvider complete # ::CompletionProvider keymap_dict::Dict{Char,Any} - HistoryPrompt{T}(hp) where T<:HistoryProvider = new(hp, EmptyCompletionProvider()) + HistoryPrompt(hp) = new(hp, EmptyCompletionProvider()) end -HistoryPrompt(hp::T) where T<:HistoryProvider = HistoryPrompt{T}(hp) init_state(terminal, p::HistoryPrompt) = SearchState(terminal, p, true, IOBuffer(), IOBuffer()) mutable struct PrefixSearchState <: ModeState @@ -1599,25 +1618,26 @@ end function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, s::Union{PromptState,PrefixSearchState}; beeping=false) beeping || cancel_beep(s) - s.ias = refresh_multi_line(termbuf, terminal, buffer(s), s.ias, s, - indent = s.indent, - region_active = is_region_active(s)) + ias = refresh_multi_line(termbuf, terminal, buffer(s), s.ias, s, + indent = s.indent, + region_active = is_region_active(s)) + s.ias = ias + return ias end input_string(s::PrefixSearchState) = String(take!(copy(s.response_buffer))) # a meta-prompt that presents itself as parent_prompt, but which has an independent keymap # for prefix searching -mutable struct PrefixHistoryPrompt{T<:HistoryProvider} <: TextInterface - hp::T +mutable struct PrefixHistoryPrompt <: TextInterface + hp # ::HistoryProvider parent_prompt::Prompt complete # ::CompletionProvider keymap_dict::Dict{Char,Any} - PrefixHistoryPrompt{T}(hp, parent_prompt) where T<:HistoryProvider = + PrefixHistoryPrompt(hp, parent_prompt) = new(hp, parent_prompt, EmptyCompletionProvider()) end -PrefixHistoryPrompt(hp::T, parent_prompt) where T<:HistoryProvider = PrefixHistoryPrompt{T}(hp, parent_prompt) init_state(terminal, p::PrefixHistoryPrompt) = PrefixSearchState(terminal, p, "", IOBuffer()) write_prompt(terminal, s::PrefixSearchState) = write_prompt(terminal, s.histprompt.parent_prompt) @@ -1631,6 +1651,7 @@ function reset_state(s::PrefixSearchState) s.response_buffer.ptr = 1 end reset_state(s.histprompt.hp) + nothing end function transition(f::Function, s::PrefixSearchState, mode) @@ -1644,13 +1665,15 @@ function transition(f::Function, s::PrefixSearchState, mode) else f() end + nothing end -replace_line(s::PrefixSearchState, l::IOBuffer) = s.response_buffer = l +replace_line(s::PrefixSearchState, l::IOBuffer) = (s.response_buffer = l; nothing) function replace_line(s::PrefixSearchState, l) s.response_buffer.ptr = 1 s.response_buffer.size = 0 write(s.response_buffer, l) + nothing end function refresh_multi_line(termbuf::TerminalBuffer, s::SearchState) @@ -1663,7 +1686,9 @@ function refresh_multi_line(termbuf::TerminalBuffer, s::SearchState) write(buf, read(s.response_buffer, String)) buf.ptr = offset + ptr - 1 s.response_buffer.ptr = ptr - s.ias = refresh_multi_line(termbuf, s.terminal, buf, s.ias, s.backward ? "(reverse-i-search)`" : "(forward-i-search)`") + ias = refresh_multi_line(termbuf, s.terminal, buf, s.ias, s.backward ? "(reverse-i-search)`" : "(forward-i-search)`") + s.ias = ias + return ias end state(s::MIState, p=mode(s)) = s.mode_state[p] @@ -1680,17 +1705,20 @@ function complete_line(s::SearchState, repeats) if length(completions) == 1 prev_pos = position(s) push_undo(s) - edit_splice!(s, prev_pos-sizeof(partial) => prev_pos, completions[1]) + edit_splice!(s, (prev_pos - sizeof(partial)) => prev_pos, completions[1]) return true end - false + return false end -function accept_result(s, p) - parent = state(s, p).parent +accept_result_newmode(hp::HistoryProvider) = nothing +function accept_result(s, p) # p must be either a HistoryPrompt or PrefixHistoryPrompt, probably + parent = something(accept_result_newmode(p.hp), state(s, p).parent) transition(s, parent) do replace_line(state(s, parent), state(s, p).response_buffer) + nothing end + nothing end function copybuf!(dst::IOBuffer, src::IOBuffer) @@ -1699,6 +1727,7 @@ function copybuf!(dst::IOBuffer, src::IOBuffer) copyto!(dst.data, 1, src.data, 1, n) dst.size = src.size dst.ptr = src.ptr + nothing end function enter_search(s::MIState, p::HistoryPrompt, backward::Bool) @@ -1715,6 +1744,7 @@ function enter_search(s::MIState, p::HistoryPrompt, backward::Bool) truncate(ss.query_buffer, 0) copybuf!(ss.response_buffer, buf) end + nothing end function enter_prefix_search(s::MIState, p::PrefixHistoryPrompt, backward::Bool) @@ -1736,6 +1766,7 @@ function enter_prefix_search(s::MIState, p::PrefixHistoryPrompt, backward::Bool) else history_next_prefix(pss, pss.histprompt.hp, pss.prefix) end + nothing end function setup_search_keymap(hp) @@ -1819,7 +1850,7 @@ function setup_search_keymap(hp) "^R" => (s,o...)->(enter_search(s, p, true)), "^S" => (s,o...)->(enter_search(s, p, false)), ) - (p, skeymap) + return (p, skeymap) end keymap(state, p::Union{HistoryPrompt,PrefixHistoryPrompt}) = p.keymap_dict @@ -1829,9 +1860,9 @@ Base.isempty(s::PromptState) = s.input_buffer.size == 0 on_enter(s::PromptState) = s.p.on_enter(s) -move_input_start(s) = (seek(buffer(s), 0)) -move_input_end(buf::IOBuffer) = seekend(buf) -move_input_end(s) = move_input_end(buffer(s)) +move_input_start(s) = (seek(buffer(s), 0); nothing) +move_input_end(buf::IOBuffer) = (seekend(buf); nothing) +move_input_end(s) = (move_input_end(buffer(s)); nothing) function move_line_start(s::MIState) set_action!(s, :move_line_start) @@ -1843,6 +1874,7 @@ function move_line_start(s::MIState) else seek(buf, something(findprev(isequal(UInt8('\n')), buf.data, curpos), 0)) end + nothing end function move_line_end(s::MIState) @@ -1850,6 +1882,7 @@ function move_line_end(s::MIState) s.key_repeats > 0 ? move_input_end(s) : move_line_end(buffer(s)) + nothing end function move_line_end(buf::IOBuffer) @@ -1859,7 +1892,8 @@ function move_line_end(buf::IOBuffer) move_input_end(buf) return end - seek(buf, pos-1) + seek(buf, pos - 1) + nothing end function commit_line(s) @@ -1868,7 +1902,9 @@ function commit_line(s) refresh_line(s) println(terminal(s)) add_history(s) - state(s, mode(s)).ias = InputAreaState(0, 0) + ias = InputAreaState(0, 0) + state(s, mode(s)).ias = ias + nothing end function bracketed_paste(s; tabwidth=options(s).tabwidth) @@ -1891,7 +1927,7 @@ function tab_should_complete(s) pos = position(buf) pos == 0 && return true c = buf.data[pos] - c != _newline && c != UInt8('\t') && + return c != _newline && c != UInt8('\t') && # hack to allow path completion in cmds # after a space, e.g., `cd `, while still # allowing multiple indent levels @@ -1905,7 +1941,7 @@ function edit_tab(s::MIState, jump_spaces=false, delete_trailing=jump_spaces) set_action!(s, :edit_insert_tab) push_undo(s) edit_insert_tab(buffer(s), jump_spaces, delete_trailing) || pop_undo(s) - refresh_line(s) + return refresh_line(s) end # return true iff the content of the buffer is modified @@ -1932,10 +1968,10 @@ function edit_abort(s, confirm::Bool=options(s).confirm_exit; key="^D") set_action!(s, :edit_abort) if !confirm || s.last_action == :edit_abort println(terminal(s)) - :abort + return :abort else println("Type $key again to exit.\n") - refresh_line(s) + return refresh_line(s) end end @@ -2114,27 +2150,29 @@ function setup_prefix_keymap(hp, parent_prompt) # Down Arrow "\e[B" => (s,o...)->(edit_move_down(s) || enter_prefix_search(s, p, false)), ) - (p, pkeymap) + return (p, pkeymap) end function deactivate(p::TextInterface, s::ModeState, termbuf, term::TextTerminal) clear_input_area(termbuf, s) - s + return s end function activate(p::TextInterface, s::ModeState, termbuf, term::TextTerminal) s.ias = InputAreaState(0, 0) refresh_line(s, termbuf) + nothing end function activate(p::TextInterface, s::MIState, termbuf, term::TextTerminal) @assert p == mode(s) activate(p, state(s), termbuf, term) + nothing end activate(m::ModalInterface, s::MIState, termbuf, term::TextTerminal) = activate(mode(s), s, termbuf, term) -commit_changes(t::UnixTerminal, termbuf) = write(t, take!(termbuf.out_stream)) +commit_changes(t::UnixTerminal, termbuf) = (write(t, take!(termbuf.out_stream)); nothing) function transition(f::Function, s::MIState, newmode) cancel_beep(s) @@ -2156,6 +2194,7 @@ function transition(f::Function, s::MIState, newmode) f() activate(newmode, state(s, newmode), termbuf, t) commit_changes(t, termbuf) + nothing end transition(s::MIState, mode) = transition((args...)->nothing, s, mode) @@ -2166,7 +2205,9 @@ function reset_state(s::PromptState) end empty_undo(s) deactivate_region(s) - s.ias = InputAreaState(0, 0) + ias = InputAreaState(0, 0) + s.ias = ias + return ias end function reset_state(s::MIState) @@ -2177,7 +2218,8 @@ end const default_keymap_dict = keymap([default_keymap, escape_defaults]) -function Prompt(prompt; +function Prompt(prompt + ; prompt_prefix = "", prompt_suffix = "", keymap_dict = default_keymap_dict, @@ -2188,7 +2230,7 @@ function Prompt(prompt; hist = EmptyHistoryProvider(), sticky = false) - Prompt(prompt, prompt_prefix, prompt_suffix, keymap_dict, repl, + return Prompt(prompt, prompt_prefix, prompt_suffix, keymap_dict, repl, complete, on_enter, on_done, hist, sticky) end @@ -2203,7 +2245,7 @@ function init_state(terminal, m::ModalInterface) for mode in m.modes s.mode_state[mode] = init_state(terminal, mode) end - s + return s end @@ -2235,6 +2277,7 @@ position(s::Union{MIState,ModeState}) = position(buffer(s)) function empty_undo(s::PromptState) empty!(s.undo_buffers) s.undo_idx = 1 + nothing end empty_undo(s) = nothing @@ -2243,6 +2286,7 @@ function push_undo(s::PromptState, advance=true) resize!(s.undo_buffers, s.undo_idx) s.undo_buffers[end] = copy(s.input_buffer) advance && (s.undo_idx += 1) + nothing end push_undo(s) = nothing @@ -2251,6 +2295,7 @@ push_undo(s) = nothing function pop_undo(s::PromptState) pop!(s.undo_buffers) s.undo_idx -= 1 + nothing end function edit_undo!(s::MIState) @@ -2260,13 +2305,14 @@ function edit_undo!(s::MIState) beep(s) return :ignore end + return nothing end function edit_undo!(s::PromptState) s.undo_idx > 1 || return false s.input_buffer = s.undo_buffers[s.undo_idx -=1] refresh_line(s) - true + return true end edit_undo!(s) = nothing @@ -2276,13 +2322,14 @@ function edit_redo!(s::MIState) beep(s) return :ignore end + return nothing end function edit_redo!(s::PromptState) s.undo_idx < length(s.undo_buffers) || return false s.input_buffer = s.undo_buffers[s.undo_idx += 1] refresh_line(s) - true + return true end edit_redo!(s) = nothing @@ -2332,6 +2379,7 @@ function prompt!(term::TextTerminal, prompt::ModalInterface, s::MIState = init_s finally raw!(term, false) && disable_bracketed_paste(term) end + # unreachable end diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index e2784dd8fd6f2..b260bc109275f 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -48,6 +48,8 @@ using .REPLCompletions include("TerminalMenus/TerminalMenus.jl") include("docview.jl") +@nospecialize # use only declared type signatures + function __init__() Base.REPL_MODULE_REF[] = REPL end @@ -98,6 +100,7 @@ function eval_user_input(@nospecialize(ast), backend::REPLBackend) end end Base.sigatomic_end() + nothing end function start_repl_backend(repl_channel::Channel, response_channel::Channel) @@ -116,7 +119,7 @@ function start_repl_backend(repl_channel::Channel, response_channel::Channel) eval_user_input(ast, backend) end end - backend + return backend end struct REPLDisplay{R<:AbstractREPL} <: AbstractDisplay repl::R @@ -129,12 +132,14 @@ function display(d::REPLDisplay, mime::MIME"text/plain", x) get(io, :color, false) && write(io, answer_color(d.repl)) show(IOContext(io, :limit => true), mime, x) println(io) + nothing end display(d::REPLDisplay, x) = display(d, MIME("text/plain"), x) function print_response(repl::AbstractREPL, @nospecialize(val), bt, show_value::Bool, have_color::Bool) repl.waserror = bt !== nothing print_response(outstream(repl), val, bt, show_value, have_color, specialdisplay(repl)) + nothing end function print_response(errio::IO, @nospecialize(val), bt, show_value::Bool, have_color::Bool, specialdisplay=nothing) Base.sigatomic_begin() @@ -171,6 +176,7 @@ function print_response(errio::IO, @nospecialize(val), bt, show_value::Bool, hav end end Base.sigatomic_end() + nothing end # A reference to a backend @@ -184,7 +190,7 @@ function run_repl(repl::AbstractREPL, consumer::Function = x->nothing) response_channel = Channel(1) backend = start_repl_backend(repl_channel, response_channel) consumer(backend) - run_frontend(repl, REPLBackendRef(repl_channel,response_channel)) + run_frontend(repl, REPLBackendRef(repl_channel, response_channel)) return backend end @@ -193,7 +199,7 @@ end mutable struct BasicREPL <: AbstractREPL terminal::TextTerminal waserror::Bool - BasicREPL(t) = new(t,false) + BasicREPL(t) = new(t, false) end outstream(r::BasicREPL) = r.terminal @@ -245,6 +251,7 @@ function run_frontend(repl::BasicREPL, backend::REPLBackendRef) # terminate backend put!(repl_channel, (nothing, -1)) dopushdisplay && popdisplay(d) + nothing end ## User Options @@ -434,7 +441,7 @@ function hist_from_file(hp, file, path) end seekend(file) hp.start_idx = length(hp.history) - hp + return hp end function mode_idx(hist::REPLHistoryProvider, mode) @@ -463,6 +470,7 @@ function add_history(hist::REPLHistoryProvider, s) seekend(hist.history_file) print(hist.history_file, entry) flush(hist.history_file) + nothing end function history_move(s::Union{LineEdit.MIState,LineEdit.PrefixSearchState}, hist::REPLHistoryProvider, idx::Int, save_idx::Int = hist.cur_idx) @@ -502,18 +510,12 @@ function history_move(s::Union{LineEdit.MIState,LineEdit.PrefixSearchState}, his return :ok end -# Modified version of accept_result that also transitions modes -function LineEdit.accept_result(s, p::LineEdit.HistoryPrompt{REPLHistoryProvider}) - parent = LineEdit.state(s, p).parent - hist = p.hp +# REPL History can also transitions modes +function LineEdit.accept_result_newmode(hist::REPLHistoryProvider) if 1 <= hist.cur_idx <= length(hist.modes) - m = hist.mode_mapping[hist.modes[hist.cur_idx]] - LineEdit.transition(s, m) do - LineEdit.replace_line(LineEdit.state(s, m), LineEdit.state(s, p).response_buffer) - end - else - LineEdit.transition(s, parent) + return hist.mode_mapping[hist.modes[hist.cur_idx]] end + return nothing end function history_prev(s::LineEdit.MIState, hist::REPLHistoryProvider, @@ -526,11 +528,11 @@ function history_prev(s::LineEdit.MIState, hist::REPLHistoryProvider, LineEdit.reset_key_repeats(s) do LineEdit.move_line_end(s) end - LineEdit.refresh_line(s) + return LineEdit.refresh_line(s) elseif m === :skip - history_prev(s, hist, num+1, save_idx) + return history_prev(s, hist, num+1, save_idx) else - Terminals.beep(s) + return Terminals.beep(s) end end @@ -551,11 +553,11 @@ function history_next(s::LineEdit.MIState, hist::REPLHistoryProvider, m = history_move(s, hist, cur_idx+num, save_idx) if m === :ok LineEdit.move_input_end(s) - LineEdit.refresh_line(s) + return LineEdit.refresh_line(s) elseif m === :skip - history_next(s, hist, num+1, save_idx) + return history_next(s, hist, num+1, save_idx) else - Terminals.beep(s) + return Terminals.beep(s) end end @@ -600,6 +602,7 @@ function history_move_prefix(s::LineEdit.PrefixSearchState, end end Terminals.beep(s) + nothing end history_next_prefix(s::LineEdit.PrefixSearchState, hist::REPLHistoryProvider, prefix::AbstractString) = history_move_prefix(s, hist, prefix, false) @@ -661,6 +664,7 @@ function history_reset_state(hist::REPLHistoryProvider) hist.last_idx = hist.cur_idx hist.cur_idx = length(hist.history) + 1 end + nothing end LineEdit.reset_state(hist::REPLHistoryProvider) = history_reset_state(hist) @@ -708,13 +712,13 @@ function respond(f, repl, main; pass_empty = false) end prepare_next(repl) reset_state(s) - s.current_mode.sticky || transition(s, main) + return s.current_mode.sticky ? true : transition(s, main) end end function reset(repl::LineEditREPL) raw!(repl.t, false) - print(repl.t,Base.text_colors[:normal]) + print(repl.t, Base.text_colors[:normal]) end function prepare_next(repl::LineEditREPL) @@ -1009,7 +1013,8 @@ function setup_interface( shell_mode.keymap_dict = help_mode.keymap_dict = LineEdit.keymap(b) - ModalInterface([julia_prompt, shell_mode, help_mode, search_prompt, prefix_prompt]) + allprompts = [julia_prompt, shell_mode, help_mode, search_prompt, prefix_prompt] + return ModalInterface(allprompts) end function run_frontend(repl::LineEditREPL, backend::REPLBackendRef) @@ -1025,13 +1030,7 @@ function run_frontend(repl::LineEditREPL, backend::REPLBackendRef) repl.mistate = LineEdit.init_state(terminal(repl), interface) run_interface(terminal(repl), interface, repl.mistate) dopushdisplay && popdisplay(d) -end - -if isdefined(Base, :banner_color) - banner(io, t) = banner(io, hascolor(t)) - banner(io, x::Bool) = print(io, x ? Base.banner_color : Base.banner_plain) -else - banner(io,t) = Base.banner(io) + nothing end ## StreamREPL ## @@ -1107,7 +1106,7 @@ end function run_frontend(repl::StreamREPL, backend::REPLBackendRef) have_color = Base.have_color - banner(repl.stream, have_color) + Base.banner(repl.stream) d = REPLDisplay(repl) dopushdisplay = !in(d,Base.Multimedia.displays) dopushdisplay && pushdisplay(d) @@ -1136,12 +1135,14 @@ function run_frontend(repl::StreamREPL, backend::REPLBackendRef) # Terminate Backend put!(repl_channel, (nothing, -1)) dopushdisplay && popdisplay(d) + nothing end function start_repl_server(port::Int) - listen(port) do server, status + return listen(port) do server, status client = accept(server) run_repl(client) + nothing end end diff --git a/stdlib/REPL/src/docview.jl b/stdlib/REPL/src/docview.jl index 1b0d92691f9be..ebb2dcbd70d0d 100644 --- a/stdlib/REPL/src/docview.jl +++ b/stdlib/REPL/src/docview.jl @@ -211,7 +211,8 @@ function summarize(io::IO, m::Module, binding) println(io, "No docstring found for module `", m, "`.\n") end -function summarize(io::IO, ::T, binding) where T +function summarize(io::IO, @nospecialize(T), binding) + T = typeof(T) println(io, "`", binding, "` is of type `", T, "`.\n") summarize(io, T, binding) end