diff --git a/Makefile b/Makefile index 2ed0646cf3d6b..5e9ea8a44c66f 100644 --- a/Makefile +++ b/Makefile @@ -159,7 +159,7 @@ release-candidate: release testall @echo 7. Clean out old .tar.gz files living in deps/, "\`git clean -fdx\`" seems to work #"` @echo 8. Replace github release tarball with tarballs created from make light-source-dist and make full-source-dist with USE_BINARYBUILDER=0 @echo 9. Check that 'make && make install && make test' succeed with unpacked tarballs even without Internet access. - @echo 10. Follow packaging instructions in doc/build/distributing.md to create binary packages for all platforms + @echo 10. Follow packaging instructions in doc/src/devdocs/build/distributing.md to create binary packages for all platforms @echo 11. Upload to AWS, update https://julialang.org/downloads and http://status.julialang.org/stable links @echo 12. Update checksums on AWS for tarball and packaged binaries @echo 13. Update versions.json. Wait at least 60 minutes before proceeding to step 14. diff --git a/NEWS.md b/NEWS.md index 51e760b07772c..636c7c53f5120 100644 --- a/NEWS.md +++ b/NEWS.md @@ -16,6 +16,12 @@ Compiler/Runtime improvements Command-line option changes --------------------------- +* The entry point for Julia has been standardized to `Main.main(ARGS)`. When julia is invoked to run a script or expression +(i.e. using `julia script.jl` or `julia -e expr`), julia will subsequently run the `Main.main` function automatically if +such a function has been defined. This is identended to unify script and compilation workflows, where code loading may happen +in the compiler and execution of `Main.main` may happen in the resulting executable. For interactive use, there is no semantic +difference between defining a main function and executing the code directly at the end of the script. ([50974]) + Multi-threading changes ----------------------- @@ -74,6 +80,7 @@ Deprecated or removed External dependencies --------------------- +* `tput` is no longer called to check terminal capabilities, it has been replaced with a pure-Julia terminfo parser. Tooling Improvements -------------------- diff --git a/README.md b/README.md index dcdc99e286ee4..e904b3b8d6770 100644 --- a/README.md +++ b/README.md @@ -93,7 +93,7 @@ and then use the command prompt to change into the resulting julia directory. By Julia. However, most users should use the [most recent stable version](https://github.com/JuliaLang/julia/releases) of Julia. You can get this version by running: - git checkout v1.9.2 + git checkout v1.9.3 To build the `julia` executable, run `make` from within the julia directory. diff --git a/base/Base.jl b/base/Base.jl index ecc0f0e5522ed..0673a1081ae69 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -224,6 +224,7 @@ include("hashing.jl") include("rounding.jl") using .Rounding include("div.jl") +include("rawbigints.jl") include("float.jl") include("twiceprecision.jl") include("complex.jl") @@ -358,7 +359,7 @@ include("filesystem.jl") using .Filesystem include("cmd.jl") include("process.jl") -include("ttyhascolor.jl") +include("terminfo.jl") include("secretbuffer.jl") # core math functions diff --git a/base/array.jl b/base/array.jl index d3d4750743a91..dbc2e00713a2f 100644 --- a/base/array.jl +++ b/base/array.jl @@ -2486,42 +2486,19 @@ function findall(A) end # Allocating result upfront is faster (possible only when collection can be iterated twice) -function _findall(f::Function, A::AbstractArray{Bool}) - n = count(f, A) +function findall(A::AbstractArray{Bool}) + n = count(A) I = Vector{eltype(keys(A))}(undef, n) - isempty(I) && return I - _findall(f, I, A) -end - -function _findall(f::Function, I::Vector, A::AbstractArray{Bool}) - cnt = 1 - len = length(I) - for (k, v) in pairs(A) - @inbounds I[cnt] = k - cnt += f(v) - cnt > len && return I - end - # In case of impure f, this line could potentially be hit. In that case, - # we can't assume I is the correct length. - resize!(I, cnt - 1) -end - -function _findall(f::Function, I::Vector, A::AbstractVector{Bool}) - i = firstindex(A) cnt = 1 - len = length(I) - while cnt โ‰ค len - @inbounds I[cnt] = i - cnt += f(@inbounds A[i]) - i = nextind(A, i) + for (i,a) in pairs(A) + if a + I[cnt] = i + cnt += 1 + end end - cnt - 1 == len ? I : resize!(I, cnt - 1) + I end -findall(f::Function, A::AbstractArray{Bool}) = _findall(f, A) -findall(f::Fix2{typeof(in)}, A::AbstractArray{Bool}) = _findall(f, A) -findall(A::AbstractArray{Bool}) = _findall(identity, A) - findall(x::Bool) = x ? [1] : Vector{Int}() findall(testf::Function, x::Number) = testf(x) ? [1] : Vector{Int}() findall(p::Fix2{typeof(in)}, x::Number) = x in p.x ? [1] : Vector{Int}() diff --git a/base/boot.jl b/base/boot.jl index 78b7daaf47d64..e24a6f4ffc0e0 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -463,7 +463,8 @@ macro _foldable_meta() #=:terminates_globally=#true, #=:terminates_locally=#false, #=:notaskstate=#false, - #=:inaccessiblememonly=#false)) + #=:inaccessiblememonly=#false, + #=:noub=#true)) end const NTuple{N,T} = Tuple{Vararg{T,N}} diff --git a/base/client.jl b/base/client.jl index 5d47e84a14b4d..e7f189826afdb 100644 --- a/base/client.jl +++ b/base/client.jl @@ -4,6 +4,7 @@ ## and REPL have_color = nothing +have_truecolor = nothing const default_color_warn = :yellow const default_color_error = :light_red const default_color_info = :cyan @@ -322,6 +323,10 @@ function exec_options(opts) end end end + + ret = 0 + isdefined(Main, :main) && (ret = invokelatest(Main.main, ARGS)) + if repl || is_interactive::Bool b = opts.banner auto = b == -1 @@ -330,7 +335,7 @@ function exec_options(opts) :short # b == 2 run_main_repl(interactiveinput, quiet, banner, history_file, color_set) end - nothing + return ret end function _global_julia_startup_file() @@ -413,6 +418,7 @@ function run_main_repl(interactive::Bool, quiet::Bool, banner::Symbol, history_f if interactive && isassigned(REPL_MODULE_REF) invokelatest(REPL_MODULE_REF[]) do REPL term_env = get(ENV, "TERM", @static Sys.iswindows() ? "" : "dumb") + global current_terminfo = load_terminfo(term_env) term = REPL.Terminals.TTYTerminal(term_env, stdin, stdout, stderr) banner == :no || Base.banner(term, short=banner==:short) if term.term_type == "dumb" @@ -546,13 +552,31 @@ function _start() append!(ARGS, Core.ARGS) # clear any postoutput hooks that were saved in the sysimage empty!(Base.postoutput_hooks) + local ret try - exec_options(JLOptions()) + if isdefined(Core, :Main) && isdefined(Core.Main, :main) + ret = Core.Main.main(ARGS) + elseif isassigned(REPL_MODULE_REF) + ret = REPL_MODULE_REF[].main(ARGS) + else + # TODO: This is the case for system image execution without main function + # and without REPL loaded. We fall back here to the pre-main behavior. + # However, we may instead want to adjust the sysimage build process to + # emit an explicit main function for this case. We should revisit this once + # the `main` story is fully worked out. + # + # error("No entry point defined and REPL not loaded.") + # + ret = exec_options(JLOptions()) + end + ret === nothing && (ret = 0) + ret = Cint(ret) catch + ret = Cint(1) invokelatest(display_error, scrub_repl_backtrace(current_exceptions())) - exit(1) end if is_interactive && get(stdout, :color, false) print(color_normal) end + return ret end diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index aa2bb35117c86..39fea90cbe993 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -854,8 +854,8 @@ function concrete_eval_eligible(interp::AbstractInterpreter, end end if !effects.noinbounds && stmt_taints_inbounds_consistency(sv) - # If the current statement is @inbounds or we propagate inbounds, the call's consistency - # is tainted and not consteval eligible. + # If the current statement is @inbounds or we propagate inbounds, + # the call's :consistent-cy is tainted and not consteval eligible. add_remark!(interp, sv, "[constprop] Concrete evel disabled for inbounds") return :none end @@ -1999,13 +1999,16 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), end rt = abstract_call_builtin(interp, f, arginfo, sv) effects = builtin_effects(๐•ƒแตข, f, arginfo, rt) - if f === getfield && (fargs !== nothing && isexpr(fargs[end], :boundscheck)) && !is_nothrow(effects) && isa(sv, InferenceState) - # As a special case, we delayed tainting `noinbounds` for getfield calls in case we can prove - # in-boundedness indepedently. Here we need to put that back in other cases. - # N.B.: This isn't about the effects of the call itself, but a delayed contribution of the :boundscheck - # statement, so we need to merge this directly into sv, rather than modifying thte effects. + if (isa(sv, InferenceState) && f === getfield && fargs !== nothing && + isexpr(fargs[end], :boundscheck) && !is_nothrow(effects)) + # As a special case, we delayed tainting `noinbounds` for `getfield` calls + # in case we can prove in-boundedness indepedently. + # Here we need to put that back in other cases. + # N.B. This isn't about the effects of the call itself, + # but a delayed contribution of the :boundscheck statement, + # so we need to merge this directly into sv, rather than modifying the effects. merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; noinbounds=false, - consistent = (get_curr_ssaflag(sv) & IR_FLAG_INBOUNDS) != 0 ? ALWAYS_FALSE : ALWAYS_TRUE)) + consistent = iszero(get_curr_ssaflag(sv) & IR_FLAG_INBOUNDS) ? ALWAYS_TRUE : ALWAYS_FALSE)) end return CallMeta(rt, effects, NoCallInfo()) elseif isa(f, Core.OpaqueClosure) @@ -2209,7 +2212,6 @@ function abstract_eval_cfunction(interp::AbstractInterpreter, e::Expr, vtypes::U end function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable,Nothing}, sv::AbsIntState) - rt = Any head = e.head if head === :static_parameter n = e.args[1]::Int @@ -2218,6 +2220,8 @@ function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, vtypes:: sp = sv.sptypes[n] rt = sp.typ nothrow = !sp.undef + else + rt = Any end merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; nothrow)) return rt @@ -2228,26 +2232,26 @@ function abstract_eval_value_expr(interp::AbstractInterpreter, e::Expr, vtypes:: f = abstract_eval_value(interp, stmt.args[1], vtypes, sv) if f isa Const && f.val === getfield # boundscheck of `getfield` call is analyzed by tfunc potentially without - # tainting :inbounds or :consistent when it's known to be nothrow - @goto delay_effects_analysis + # tainting :noinbounds or :noub when it's known to be nothrow + return Bool end end # If there is no particular `@inbounds` for this function, then we only taint `:noinbounds`, - # which will subsequently taint `:consistent`-cy if this function is called from another + # which will subsequently taint `:consistent` if this function is called from another # function that uses `@inbounds`. However, if this `:boundscheck` is itself within an # `@inbounds` region, its value depends on `--check-bounds`, so we need to taint # `:consistent`-cy here also. merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; noinbounds=false, - consistent = (get_curr_ssaflag(sv) & IR_FLAG_INBOUNDS) != 0 ? ALWAYS_FALSE : ALWAYS_TRUE)) + consistent = iszero(get_curr_ssaflag(sv) & IR_FLAG_INBOUNDS) ? ALWAYS_TRUE : ALWAYS_FALSE)) end - @label delay_effects_analysis - rt = Bool + return Bool elseif head === :inbounds - @assert false && "Expected this to have been moved into flags" + @assert false "Expected `:inbounds` expression to have been moved into SSA flags" elseif head === :the_exception merge_effects!(interp, sv, Effects(EFFECTS_TOTAL; consistent=ALWAYS_FALSE)) + return Any end - return rt + return Any end function abstract_eval_special_value(interp::AbstractInterpreter, @nospecialize(e), vtypes::Union{VarTable,Nothing}, sv::AbsIntState) @@ -2361,7 +2365,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp t, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv)) ut = unwrap_unionall(t) consistent = ALWAYS_FALSE - nothrow = false + nothrow = noub = false if isa(ut, DataType) && !isabstracttype(ut) ismutable = ismutabletype(ut) fcount = datatype_fieldcount(ut) @@ -2369,14 +2373,15 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp if (fcount === nothing || (fcount > nargs && (let t = t any(i::Int -> !is_undefref_fieldtype(fieldtype(t, i)), (nargs+1):fcount) end))) - # allocation with undefined field leads to undefined behavior and should taint `:consistent`-cy - consistent = ALWAYS_FALSE + # allocation with undefined field leads to undefined behavior and should taint `:noub` elseif ismutable - # mutable object isn't `:consistent`, but we can still give the return - # type information a chance to refine this `:consistent`-cy later + # mutable object isn't `:consistent`, but we still have a chance that + # return type information later refines the `:consistent`-cy of the method consistent = CONSISTENT_IF_NOTRETURNED + noub = true else consistent = ALWAYS_TRUE + noub = true end if isconcretedispatch(t) nothrow = true @@ -2420,7 +2425,7 @@ function abstract_eval_statement_expr(interp::AbstractInterpreter, e::Expr, vtyp t = refine_partial_type(t) end end - effects = Effects(EFFECTS_TOTAL; consistent, nothrow) + effects = Effects(EFFECTS_TOTAL; consistent, nothrow, noub) elseif ehead === :splatnew t, isexact = instanceof_tfunc(abstract_eval_value(interp, e.args[1], vtypes, sv)) nothrow = false # TODO: More precision @@ -2563,15 +2568,14 @@ function abstract_eval_foreigncall(interp::AbstractInterpreter, e::Expr, vtypes: cconv = e.args[5] if isa(cconv, QuoteNode) && (v = cconv.value; isa(v, Tuple{Symbol, UInt8})) override = decode_effects_override(v[2]) - effects = Effects( - override.consistent ? ALWAYS_TRUE : effects.consistent, - override.effect_free ? ALWAYS_TRUE : effects.effect_free, - override.nothrow ? true : effects.nothrow, - override.terminates_globally ? true : effects.terminates, - override.notaskstate ? true : effects.notaskstate, - override.inaccessiblememonly ? ALWAYS_TRUE : effects.inaccessiblememonly, - effects.nonoverlayed, - effects.noinbounds) + effects = Effects(effects; + consistent = override.consistent ? ALWAYS_TRUE : effects.consistent, + effect_free = override.effect_free ? ALWAYS_TRUE : effects.effect_free, + nothrow = override.nothrow ? true : effects.nothrow, + terminates = override.terminates_globally ? true : effects.terminates, + notaskstate = override.notaskstate ? true : effects.notaskstate, + inaccessiblememonly = override.inaccessiblememonly ? ALWAYS_TRUE : effects.inaccessiblememonly, + noub = override.noub ? true : effects.noub) end return RTEffects(t, effects) end @@ -2606,8 +2610,8 @@ function abstract_eval_statement(interp::AbstractInterpreter, @nospecialize(e), # we ourselves don't read our parent's inbounds. effects = Effects(effects; noinbounds=true) end - if (get_curr_ssaflag(sv) & IR_FLAG_INBOUNDS) != 0 - effects = Effects(effects; consistent=ALWAYS_FALSE) + if !iszero(get_curr_ssaflag(sv) & IR_FLAG_INBOUNDS) + effects = Effects(effects; consistent=ALWAYS_FALSE, noub=false) end end merge_effects!(interp, sv, effects) diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index 7d09769e5b31b..a8f5596a0af9e 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -6,7 +6,7 @@ Represents computational effects of a method call. The effects are a composition of different effect bits that represent some program property of the method being analyzed. They are represented as `Bool` or `UInt8` bits with the following meanings: -- `effects.consistent::UInt8`: +- `consistent::UInt8`: * `ALWAYS_TRUE`: this method is guaranteed to return or terminate consistently. * `ALWAYS_FALSE`: this method may be not return or terminate consistently, and there is no need for further analysis with respect to this effect property as this conclusion @@ -38,6 +38,10 @@ following meanings: except that it may access or modify mutable memory pointed to by its call arguments. This may later be refined to `ALWAYS_TRUE` in a case when call arguments are known to be immutable. This state corresponds to LLVM's `inaccessiblemem_or_argmemonly` function attribute. +- `noub::Bool`: indicates that the method will not execute any undefined behavior (for any input). + Note that undefined behavior may technically cause the method to violate any other effect + assertions (such as `:consistent` or `:effect_free`) as well, but we do not model this, + and they assume the absence of undefined behavior. - `nonoverlayed::Bool`: indicates that any methods that may be called within this method are not defined in an [overlayed method table](@ref OverlayMethodTable). - `noinbounds::Bool`: If set, indicates that this method does not read the parent's `:inbounds` @@ -80,7 +84,10 @@ The output represents the state of different effect properties in the following - `+m` (green): `ALWAYS_TRUE` - `-m` (red): `ALWAYS_FALSE` - `?m` (yellow): `INACCESSIBLEMEM_OR_ARGMEMONLY` -7. `noinbounds` (`i`): +7. `noub` (`u`): + - `+u` (green): `true` + - `-u` (red): `false` +8. `noinbounds` (`i`): - `+i` (green): `true` - `-i` (red): `false` @@ -93,6 +100,7 @@ struct Effects terminates::Bool notaskstate::Bool inaccessiblememonly::UInt8 + noub::Bool nonoverlayed::Bool noinbounds::Bool function Effects( @@ -102,6 +110,7 @@ struct Effects terminates::Bool, notaskstate::Bool, inaccessiblememonly::UInt8, + noub::Bool, nonoverlayed::Bool, noinbounds::Bool) return new( @@ -111,6 +120,7 @@ struct Effects terminates, notaskstate, inaccessiblememonly, + noub, nonoverlayed, noinbounds) end @@ -129,20 +139,21 @@ const EFFECT_FREE_IF_INACCESSIBLEMEMONLY = 0x01 << 1 # :inaccessiblememonly bits const INACCESSIBLEMEM_OR_ARGMEMONLY = 0x01 << 1 -const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, true, true) -const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, true, true) -const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, true, true) # unknown mostly, but it's not overlayed and noinbounds at least (e.g. it's not a call) -const _EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, false, false) # unknown really +const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, true, true, true) +const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, true, true, true) +const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, false, true, true) # unknown mostly, but it's not overlayed and noinbounds at least (e.g. it's not a call) +const _EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, false, false, false) # unknown really -function Effects(e::Effects = _EFFECTS_UNKNOWN; - consistent::UInt8 = e.consistent, - effect_free::UInt8 = e.effect_free, - nothrow::Bool = e.nothrow, - terminates::Bool = e.terminates, - notaskstate::Bool = e.notaskstate, - inaccessiblememonly::UInt8 = e.inaccessiblememonly, - nonoverlayed::Bool = e.nonoverlayed, - noinbounds::Bool = e.noinbounds) +function Effects(effects::Effects = _EFFECTS_UNKNOWN; + consistent::UInt8 = effects.consistent, + effect_free::UInt8 = effects.effect_free, + nothrow::Bool = effects.nothrow, + terminates::Bool = effects.terminates, + notaskstate::Bool = effects.notaskstate, + inaccessiblememonly::UInt8 = effects.inaccessiblememonly, + noub::Bool = effects.noub, + nonoverlayed::Bool = effects.nonoverlayed, + noinbounds::Bool = effects.noinbounds) return Effects( consistent, effect_free, @@ -150,6 +161,7 @@ function Effects(e::Effects = _EFFECTS_UNKNOWN; terminates, notaskstate, inaccessiblememonly, + noub, nonoverlayed, noinbounds) end @@ -162,6 +174,7 @@ function merge_effects(old::Effects, new::Effects) merge_effectbits(old.terminates, new.terminates), merge_effectbits(old.notaskstate, new.notaskstate), merge_effectbits(old.inaccessiblememonly, new.inaccessiblememonly), + merge_effectbits(old.noub, new.noub), merge_effectbits(old.nonoverlayed, new.nonoverlayed), merge_effectbits(old.noinbounds, new.noinbounds)) end @@ -180,11 +193,13 @@ is_nothrow(effects::Effects) = effects.nothrow is_terminates(effects::Effects) = effects.terminates is_notaskstate(effects::Effects) = effects.notaskstate is_inaccessiblememonly(effects::Effects) = effects.inaccessiblememonly === ALWAYS_TRUE +is_noub(effects::Effects) = effects.noub is_nonoverlayed(effects::Effects) = effects.nonoverlayed # implies `is_notaskstate` & `is_inaccessiblememonly`, but not explicitly checked here is_foldable(effects::Effects) = is_consistent(effects) && + is_noub(effects) && is_effect_free(effects) && is_terminates(effects) @@ -192,6 +207,7 @@ is_foldable_nothrow(effects::Effects) = is_foldable(effects) && is_nothrow(effects) +# TODO add `is_noub` here? is_removable_if_unused(effects::Effects) = is_effect_free(effects) && is_terminates(effects) && @@ -209,14 +225,15 @@ is_effect_free_if_inaccessiblememonly(effects::Effects) = !iszero(effects.effect is_inaccessiblemem_or_argmemonly(effects::Effects) = effects.inaccessiblememonly === INACCESSIBLEMEM_OR_ARGMEMONLY function encode_effects(e::Effects) - return ((e.consistent % UInt32) << 0) | - ((e.effect_free % UInt32) << 3) | - ((e.nothrow % UInt32) << 5) | - ((e.terminates % UInt32) << 6) | - ((e.notaskstate % UInt32) << 7) | - ((e.inaccessiblememonly % UInt32) << 8) | - ((e.nonoverlayed % UInt32) << 10)| - ((e.noinbounds % UInt32) << 11) + return ((e.consistent % UInt32) << 0) | + ((e.effect_free % UInt32) << 3) | + ((e.nothrow % UInt32) << 5) | + ((e.terminates % UInt32) << 6) | + ((e.notaskstate % UInt32) << 7) | + ((e.inaccessiblememonly % UInt32) << 8) | + ((e.noub % UInt32) << 10) | + ((e.nonoverlayed % UInt32) << 11) | + ((e.noinbounds % UInt32) << 12) end function decode_effects(e::UInt32) @@ -228,7 +245,8 @@ function decode_effects(e::UInt32) _Bool((e >> 7) & 0x01), UInt8((e >> 8) & 0x03), _Bool((e >> 10) & 0x01), - _Bool((e >> 11) & 0x01)) + _Bool((e >> 11) & 0x01), + _Bool((e >> 12) & 0x01)) end struct EffectsOverride @@ -239,6 +257,7 @@ struct EffectsOverride terminates_locally::Bool notaskstate::Bool inaccessiblememonly::Bool + noub::Bool end function encode_effects_override(eo::EffectsOverride) @@ -250,6 +269,7 @@ function encode_effects_override(eo::EffectsOverride) eo.terminates_locally && (e |= (0x01 << 4)) eo.notaskstate && (e |= (0x01 << 5)) eo.inaccessiblememonly && (e |= (0x01 << 6)) + eo.noub && (e |= (0x01 << 7)) return e end @@ -261,5 +281,6 @@ function decode_effects_override(e::UInt8) (e & (0x01 << 3)) != 0x00, (e & (0x01 << 4)) != 0x00, (e & (0x01 << 5)) != 0x00, - (e & (0x01 << 6)) != 0x00) + (e & (0x01 << 6)) != 0x00, + (e & (0x01 << 7)) != 0x00) end diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 53924b235b8fa..86d6c046b4553 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -229,6 +229,7 @@ mutable struct InferenceState #= results =# result::InferenceResult # remember where to put the result + unreachable::BitSet # statements that were found to be statically unreachable valid_worlds::WorldRange bestguess #::Type ipo_effects::Effects @@ -278,6 +279,7 @@ mutable struct InferenceState end src.ssavaluetypes = ssavaluetypes = Any[ NOT_FOUND for i = 1:nssavalues ] + unreachable = BitSet() pclimitations = IdSet{InferenceState}() limitations = IdSet{InferenceState}() cycle_backedges = Vector{Tuple{InferenceState,Int}}() @@ -306,7 +308,7 @@ mutable struct InferenceState linfo, world, mod, sptypes, slottypes, src, cfg, method_info, currbb, currpc, ip, handler_at, ssavalue_uses, bb_vartables, ssavaluetypes, stmt_edges, stmt_info, pclimitations, limitations, cycle_backedges, callers_in_cycle, dont_work_on_me, parent, - result, valid_worlds, bestguess, ipo_effects, + result, unreachable, valid_worlds, bestguess, ipo_effects, restrict_abstract_call_sites, cached, insert_coverage, interp) end diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index f419e952b6f23..a4130b91d61ce 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -130,12 +130,15 @@ mutable struct OptimizationState{Interp<:AbstractInterpreter} slottypes::Vector{Any} inlining::InliningState{Interp} cfg::CFG + unreachable::BitSet + bb_vartables::Vector{Union{Nothing,VarTable}} insert_coverage::Bool end function OptimizationState(sv::InferenceState, interp::AbstractInterpreter) inlining = InliningState(sv, interp) return OptimizationState(sv.linfo, sv.src, nothing, sv.stmt_info, sv.mod, - sv.sptypes, sv.slottypes, inlining, sv.cfg, sv.insert_coverage) + sv.sptypes, sv.slottypes, inlining, sv.cfg, + sv.unreachable, sv.bb_vartables, sv.insert_coverage) end function OptimizationState(linfo::MethodInstance, src::CodeInfo, interp::AbstractInterpreter) # prepare src for running optimization passes if it isn't already @@ -159,7 +162,15 @@ function OptimizationState(linfo::MethodInstance, src::CodeInfo, interp::Abstrac # This method is mostly used for unit testing the optimizer inlining = InliningState(interp) cfg = compute_basic_blocks(src.code) - return OptimizationState(linfo, src, nothing, stmt_info, mod, sptypes, slottypes, inlining, cfg, false) + unreachable = BitSet() + bb_vartables = Union{VarTable,Nothing}[] + for block = 1:length(cfg.blocks) + push!(bb_vartables, VarState[ + VarState(slottypes[slot], src.slotflags[slot] & SLOT_USEDUNDEF != 0) + for slot = 1:nslots + ]) + end + return OptimizationState(linfo, src, nothing, stmt_info, mod, sptypes, slottypes, inlining, cfg, unreachable, bb_vartables, false) end function OptimizationState(linfo::MethodInstance, interp::AbstractInterpreter) world = get_world_counter(interp) @@ -168,7 +179,6 @@ function OptimizationState(linfo::MethodInstance, interp::AbstractInterpreter) return OptimizationState(linfo, src, interp) end - include("compiler/ssair/driver.jl") function ir_to_codeinf!(opt::OptimizationState) @@ -328,7 +338,7 @@ function stmt_effect_flags(๐•ƒโ‚’::AbstractLattice, @nospecialize(stmt), @nospe return (false, false, false) end end - isa(stmt, UnoptSlot) && error("unexpected IR elements") + isa(stmt, SlotNumber) && error("unexpected IR elements") return (true, true, true) end @@ -363,8 +373,6 @@ function argextype( @assert false elseif isa(x, SlotNumber) return slottypes[x.id] - elseif isa(x, TypedSlot) - return x.typ elseif isa(x, SSAValue) return abstract_eval_ssavalue(x, src) elseif isa(x, Argument) @@ -511,7 +519,7 @@ function run_passes( @pass "ADCE" ir = adce_pass!(ir, sv.inlining) @pass "compact 3" ir = compact!(ir) if JLOptions().debug_level == 2 - @timeit "verify 3" (verify_ir(ir); verify_linetable(ir.linetable)) + @timeit "verify 3" (verify_ir(ir, true, false, optimizer_lattice(sv.inlining.interp)); verify_linetable(ir.linetable)) end @label __done__ # used by @pass return ir @@ -523,12 +531,39 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState) linetable = collect(LineInfoNode, linetable::Vector{Any})::Vector{LineInfoNode} end + # Update control-flow to reflect any unreachable branches. + ssavaluetypes = ci.ssavaluetypes::Vector{Any} + code = copy_exprargs(ci.code) + for i = 1:length(code) + expr = code[i] + if !(i in sv.unreachable) && isa(expr, GotoIfNot) + # Replace this live GotoIfNot with: + # - no-op if :nothrow and the branch target is unreachable + # - cond if :nothrow and both targets are unreachable + # - typeassert if must-throw + if widenconst(argextype(expr.cond, ci, sv.sptypes)) === Bool + block = block_for_inst(sv.cfg, i) + if i + 1 in sv.unreachable + cfg_delete_edge!(sv.cfg, block, block + 1) + expr = GotoNode(expr.dest) + elseif expr.dest in sv.unreachable + cfg_delete_edge!(sv.cfg, block, block_for_inst(sv.cfg, expr.dest)) + expr = nothing + end + elseif ssavaluetypes[i] === Bottom + block = block_for_inst(sv.cfg, i) + cfg_delete_edge!(sv.cfg, block, block + 1) + cfg_delete_edge!(sv.cfg, block, block_for_inst(sv.cfg, expr.dest)) + expr = Expr(:call, Core.typeassert, expr.cond, Bool) + end + code[i] = expr + end + end + # Go through and add an unreachable node after every # Union{} call. Then reindex labels. - code = copy_exprargs(ci.code) stmtinfo = sv.stmt_info codelocs = ci.codelocs - ssavaluetypes = ci.ssavaluetypes::Vector{Any} ssaflags = ci.ssaflags meta = Expr[] idx = 1 @@ -562,8 +597,8 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState) idx += 1 prevloc = codeloc end - if ssavaluetypes[idx] === Union{} && !(code[idx] isa Core.Const) - # Type inference should have converted any must-throw terminators to an equivalent w/o control-flow edges + if ssavaluetypes[idx] === Union{} && !(oldidx in sv.unreachable) + # We should have converted any must-throw terminators to an equivalent w/o control-flow edges @assert !isterminator(code[idx]) block = block_for_inst(sv.cfg, oldidx) @@ -589,8 +624,8 @@ function convert_to_ircode(ci::CodeInfo, sv::OptimizationState) # Verify that type-inference did its job if JLOptions().debug_level == 2 - for i = (idx + 1):(block_end - 1) - @assert (code[i] isa Core.Const) || is_meta_expr(code[i]) + for i = (oldidx + 1):last(sv.cfg.blocks[block].stmts) + @assert i in sv.unreachable end end @@ -659,7 +694,7 @@ function slot2reg(ir::IRCode, ci::CodeInfo, sv::OptimizationState) @timeit "domtree 1" domtree = construct_domtree(ir.cfg.blocks) defuse_insts = scan_slot_def_use(nargs, ci, ir.stmts.stmt) ๐•ƒโ‚’ = optimizer_lattice(sv.inlining.interp) - @timeit "construct_ssa" ir = construct_ssa!(ci, ir, domtree, defuse_insts, sv.slottypes, ๐•ƒโ‚’) # consumes `ir` + @timeit "construct_ssa" ir = construct_ssa!(ci, ir, sv, domtree, defuse_insts, ๐•ƒโ‚’) # consumes `ir` # NOTE now we have converted `ir` to the SSA form and eliminated slots # let's resize `argtypes` now and remove unnecessary types for the eliminated slots resize!(ir.argtypes, nargs) @@ -888,10 +923,7 @@ function renumber_ir_elements!(body::Vector{Any}, ssachangemap::Vector{Int}, lab end function renumber_cfg_stmts!(cfg::CFG, blockchangemap::Vector{Int}) - any_change = cumsum_ssamap!(blockchangemap) - any_change || return - - last_end = 0 + cumsum_ssamap!(blockchangemap) || return for i = 1:length(cfg.blocks) old_range = cfg.blocks[i].stmts new_range = StmtRange(first(old_range) + ((i > 1) ? blockchangemap[i - 1] : 0), diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index d7b996bdc2bfd..5343daa2646c1 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -805,8 +805,8 @@ end function compileable_specialization(mi::MethodInstance, effects::Effects, et::InliningEdgeTracker, @nospecialize(info::CallInfo); compilesig_invokes::Bool=true) mi_invoke = mi + method, atype, sparams = mi.def::Method, mi.specTypes, mi.sparam_vals if compilesig_invokes - method, atype, sparams = mi.def::Method, mi.specTypes, mi.sparam_vals new_atype = get_compileable_sig(method, atype, sparams) new_atype === nothing && return nothing if atype !== new_atype @@ -824,7 +824,8 @@ function compileable_specialization(mi::MethodInstance, effects::Effects, return nothing end end - add_inlining_backedge!(et, mi) + add_inlining_backedge!(et, mi) # to the dispatch lookup + push!(et.edges, method.sig, mi_invoke) # add_inlining_backedge to the invoke call return InvokeCase(mi_invoke, effects, info) end diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 25eb4a54b5145..e972dd4af1720 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -952,15 +952,36 @@ function pattern_match_typeof(compact::IncrementalCompact, typ::DataType, fidx:: end function refine_new_effects!(๐•ƒโ‚’::AbstractLattice, compact::IncrementalCompact, idx::Int, stmt::Expr) + inst = compact[SSAValue(idx)] + if !(iszero(inst[:flag] & IR_FLAG_NOTHROW) && iszero(inst[:flag] & IR_FLAG_EFFECT_FREE)) + return # already accurate + end (consistent, effect_free_and_nothrow, nothrow) = new_expr_effect_flags(๐•ƒโ‚’, stmt.args, compact, pattern_match_typeof) if consistent - compact[SSAValue(idx)][:flag] |= IR_FLAG_CONSISTENT + inst[:flag] |= IR_FLAG_CONSISTENT end if effect_free_and_nothrow - compact[SSAValue(idx)][:flag] |= IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW + inst[:flag] |= IR_FLAG_EFFECT_FREE | IR_FLAG_NOTHROW elseif nothrow - compact[SSAValue(idx)][:flag] |= IR_FLAG_NOTHROW + inst[:flag] |= IR_FLAG_NOTHROW end + return nothing +end + +function fold_ifelse!(compact::IncrementalCompact, idx::Int, stmt::Expr) + length(stmt.args) == 4 || return false + condarg = stmt.args[2] + condtyp = argextype(condarg, compact) + if isa(condtyp, Const) + if condtyp.val === true + compact[idx] = stmt.args[3] + return true + elseif condtyp.val === false + compact[idx] = stmt.args[4] + return true + end + end + return false end # NOTE we use `IdSet{Int}` instead of `BitSet` for in these passes since they work on IR after inlining, @@ -1092,7 +1113,9 @@ function sroa_pass!(ir::IRCode, inlining::Union{Nothing,InliningState}=nothing) lift_comparison!(===, compact, idx, stmt, lifting_cache, ๐•ƒโ‚’) elseif is_known_call(stmt, isa, compact) lift_comparison!(isa, compact, idx, stmt, lifting_cache, ๐•ƒโ‚’) - elseif isexpr(stmt, :new) && (compact[SSAValue(idx)][:flag] & IR_FLAG_NOTHROW) == 0x00 + elseif is_known_call(stmt, Core.ifelse, compact) + fold_ifelse!(compact, idx, stmt) + elseif isexpr(stmt, :new) refine_new_effects!(๐•ƒโ‚’, compact, idx, stmt) end continue diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index 0f460c5676df9..1b1bdaf7e9e9e 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -75,10 +75,6 @@ function print_stmt(io::IO, idx::Int, @nospecialize(stmt), used::BitSet, maxleng show_unquoted_phinode(io, stmt, indent, "#") elseif stmt isa GotoIfNot show_unquoted_gotoifnot(io, stmt, indent, "#") - elseif stmt isa TypedSlot - # call `show` with the type set to Any so it will not be shown, since - # we will show the type ourselves. - show_unquoted(io, SlotNumber(stmt.id), indent, show_type ? prec_decl : 0) # everything else in the IR, defer to the generic AST printer else show_unquoted(io, stmt, indent, show_type ? prec_decl : 0) @@ -1020,6 +1016,8 @@ function Base.show(io::IO, e::Effects) print(io, ',') printstyled(io, effectbits_letter(e, :inaccessiblememonly, 'm'); color=effectbits_color(e, :inaccessiblememonly)) print(io, ',') + printstyled(io, effectbits_letter(e, :noub, 'u'); color=effectbits_color(e, :noub)) + print(io, ',') printstyled(io, effectbits_letter(e, :noinbounds, 'i'); color=effectbits_color(e, :noinbounds)) print(io, ')') e.nonoverlayed || printstyled(io, 'โ€ฒ'; color=:red) diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index 3966dde7fbbd9..4d5bc20403d98 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -1,13 +1,5 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -struct TypedSlot - id::Int - typ - TypedSlot(id::Int, @nospecialize(typ)) = new(id, typ) -end - -const UnoptSlot = Union{SlotNumber, TypedSlot} - mutable struct SlotInfo defs::Vector{Int} uses::Vector{Int} @@ -29,13 +21,13 @@ function scan_entry!(result::Vector{SlotInfo}, idx::Int, @nospecialize(stmt)) end stmt = stmt.args[2] end - if isa(stmt, UnoptSlot) + if isa(stmt, SlotNumber) push!(result[slot_id(stmt)].uses, idx) return end for op in userefs(stmt) val = op[] - if isa(val, UnoptSlot) + if isa(val, SlotNumber) push!(result[slot_id(val)].uses, idx) end end @@ -89,7 +81,7 @@ function new_to_regular(@nospecialize(stmt), new_offset::Int) return urs[] end -function fixup_slot!(ir::IRCode, ci::CodeInfo, idx::Int, slot::Int, stmt::UnoptSlot, @nospecialize(ssa), @nospecialize(def_ssa)) +function fixup_slot!(ir::IRCode, ci::CodeInfo, idx::Int, slot::Int, @nospecialize(ssa), @nospecialize(def_ssa)) # We don't really have the information here to get rid of these. # We'll do so later if ssa === UNDEF_TOKEN @@ -100,17 +92,12 @@ function fixup_slot!(ir::IRCode, ci::CodeInfo, idx::Int, slot::Int, stmt::UnoptS insert_node!(ir, idx, NewInstruction( Expr(:throw_undef_if_not, ci.slotnames[slot], def_ssa), Any)) end - if isa(stmt, SlotNumber) - return ssa - elseif isa(stmt, TypedSlot) - return NewSSAValue(insert_node!(ir, idx, NewInstruction(PiNode(ssa, stmt.typ), stmt.typ)).id - length(ir.stmts)) - end - @assert false # unreachable + return ssa end function fixemup!(@specialize(slot_filter), @specialize(rename_slot), ir::IRCode, ci::CodeInfo, idx::Int, @nospecialize(stmt)) - if isa(stmt, UnoptSlot) && slot_filter(stmt) - return fixup_slot!(ir, ci, idx, slot_id(stmt), stmt, rename_slot(stmt)...) + if isa(stmt, SlotNumber) && slot_filter(stmt) + return fixup_slot!(ir, ci, idx, slot_id(stmt), rename_slot(stmt)...) end if isexpr(stmt, :(=)) stmt.args[2] = fixemup!(slot_filter, rename_slot, ir, ci, idx, stmt.args[2]) @@ -120,37 +107,27 @@ function fixemup!(@specialize(slot_filter), @specialize(rename_slot), ir::IRCode for i = 1:length(stmt.edges) isassigned(stmt.values, i) || continue val = stmt.values[i] - isa(val, UnoptSlot) || continue + isa(val, SlotNumber) || continue slot_filter(val) || continue bb_idx = block_for_inst(ir.cfg, Int(stmt.edges[i])) from_bb_terminator = last(ir.cfg.blocks[bb_idx].stmts) - stmt.values[i] = fixup_slot!(ir, ci, from_bb_terminator, slot_id(val), val, rename_slot(val)...) + stmt.values[i] = fixup_slot!(ir, ci, from_bb_terminator, slot_id(val), rename_slot(val)...) end return stmt end if isexpr(stmt, :isdefined) val = stmt.args[1] - if isa(val, UnoptSlot) - slot = slot_id(val) - if (ci.slotflags[slot] & SLOT_USEDUNDEF) == 0 - return true - else - ssa, undef_ssa = rename_slot(val) - if ssa === UNDEF_TOKEN - return false - elseif !isa(ssa, SSAValue) && !isa(ssa, NewSSAValue) - return true - end - return undef_ssa - end + if isa(val, SlotNumber) + ssa, undef_ssa = rename_slot(val) + return undef_ssa end return stmt end urs = userefs(stmt) for op in urs val = op[] - if isa(val, UnoptSlot) && slot_filter(val) - x = fixup_slot!(ir, ci, idx, slot_id(val), val, rename_slot(val)...) + if isa(val, SlotNumber) && slot_filter(val) + x = fixup_slot!(ir, ci, idx, slot_id(val), rename_slot(val)...) # We inserted an undef error node. Delete subsequent statement # to avoid confusing the optimizer if x === UNDEF_TOKEN @@ -158,10 +135,11 @@ function fixemup!(@specialize(slot_filter), @specialize(rename_slot), ir::IRCode end op[] = x elseif isa(val, GlobalRef) && !(isdefined(val.mod, val.name) && isconst(val.mod, val.name)) - op[] = NewSSAValue(insert_node!(ir, idx, - NewInstruction(val, typ_for_val(val, ci, ir.sptypes, idx, Any[]))).id - length(ir.stmts)) + typ = typ_for_val(val, ci, ir, idx, Any[]) + new_inst = NewInstruction(val, typ) + op[] = NewSSAValue(insert_node!(ir, idx, new_inst).id - length(ir.stmts)) elseif isexpr(val, :static_parameter) - ty = typ_for_val(val, ci, ir.sptypes, idx, Any[]) + ty = typ_for_val(val, ci, ir, idx, Any[]) if isa(ty, Const) inst = NewInstruction(quoted(ty.val), ty) else @@ -175,12 +153,12 @@ end function fixup_uses!(ir::IRCode, ci::CodeInfo, code::Vector{Any}, uses::Vector{Int}, slot::Int, @nospecialize(ssa)) for use in uses - code[use] = fixemup!(x::UnoptSlot->slot_id(x)==slot, stmt::UnoptSlot->(ssa, true), ir, ci, use, code[use]) + code[use] = fixemup!(x::SlotNumber->slot_id(x)==slot, stmt::SlotNumber->(ssa, true), ir, ci, use, code[use]) end end function rename_uses!(ir::IRCode, ci::CodeInfo, idx::Int, @nospecialize(stmt), renames::Vector{Pair{Any, Any}}) - return fixemup!(stmt::UnoptSlot->true, stmt::UnoptSlot->renames[slot_id(stmt)], ir, ci, idx, stmt) + return fixemup!(stmt::SlotNumber->true, stmt::SlotNumber->renames[slot_id(stmt)], ir, ci, idx, stmt) end function strip_trailing_junk!(ci::CodeInfo, cfg::CFG, code::Vector{Any}, info::Vector{CallInfo}) @@ -217,28 +195,24 @@ function strip_trailing_junk!(ci::CodeInfo, cfg::CFG, code::Vector{Any}, info::V nothing end -struct DelayedTyp - phi::NewSSAValue -end - # maybe use expr_type? -function typ_for_val(@nospecialize(x), ci::CodeInfo, sptypes::Vector{VarState}, idx::Int, slottypes::Vector{Any}) +function typ_for_val(@nospecialize(x), ci::CodeInfo, ir::IRCode, idx::Int, slottypes::Vector{Any}) if isa(x, Expr) if x.head === :static_parameter - return sptypes[x.args[1]::Int].typ + return ir.sptypes[x.args[1]::Int].typ elseif x.head === :boundscheck return Bool elseif x.head === :copyast - return typ_for_val(x.args[1], ci, sptypes, idx, slottypes) + return typ_for_val(x.args[1], ci, ir, idx, slottypes) end return (ci.ssavaluetypes::Vector{Any})[idx] end isa(x, GlobalRef) && return abstract_eval_globalref(x) isa(x, SSAValue) && return (ci.ssavaluetypes::Vector{Any})[x.id] isa(x, Argument) && return slottypes[x.n] - isa(x, NewSSAValue) && return DelayedTyp(x) + isa(x, NewSSAValue) && return types(ir)[new_to_regular(x, length(ir.stmts))] isa(x, QuoteNode) && return Const(x.value) - isa(x, Union{Symbol, PiNode, PhiNode, UnoptSlot}) && error("unexpected val type") + isa(x, Union{Symbol, PiNode, PhiNode, SlotNumber}) && error("unexpected val type") return Const(x) end @@ -575,22 +549,6 @@ function compute_live_ins(cfg::CFG, defs::Vector{Int}, uses::Vector{Int}) BlockLiveness(bb_defs, bb_uses) end -function recompute_type(node::Union{PhiNode, PhiCNode}, ci::CodeInfo, ir::IRCode, - sptypes::Vector{VarState}, slottypes::Vector{Any}, nstmts::Int, ๐•ƒโ‚’::AbstractLattice) - new_typ = Union{} - for i = 1:length(node.values) - if isa(node, PhiNode) && !isassigned(node.values, i) - continue - end - typ = typ_for_val(node.values[i], ci, sptypes, -1, slottypes) - while isa(typ, DelayedTyp) - typ = types(ir)[new_to_regular(typ.phi::NewSSAValue, nstmts)] - end - new_typ = tmerge(๐•ƒโ‚’, new_typ, typ) - end - return new_typ -end - struct TryCatchRegion enter_block::Int leave_block::Int @@ -598,7 +556,7 @@ end struct NewSlotPhi{Phi} ssaval::NewSSAValue node::Phi - undef_ssaval::Union{NewSSAValue, Nothing} + undef_ssaval::Union{NewSSAValue, Bool} undef_node::Union{Phi, Nothing} end @@ -609,8 +567,8 @@ struct NewPhiCNode2 insert::NewSlotPhi{PhiCNode} end -function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, - defuses::Vector{SlotInfo}, slottypes::Vector{Any}, +function construct_ssa!(ci::CodeInfo, ir::IRCode, sv::OptimizationState, + domtree::DomTree, defuses::Vector{SlotInfo}, ๐•ƒโ‚’::AbstractLattice) code = ir.stmts.stmt cfg = ir.cfg @@ -647,7 +605,7 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, # TODO: Restore this optimization if false # length(slot.defs) == 1 && slot.any_newvar if slot.defs[] == 0 - typ = slottypes[idx] + typ = sv.slottypes[idx] ssaval = Argument(idx) fixup_uses!(ir, ci, code, slot.uses, idx, ssaval) elseif isa(code[slot.defs[]], NewvarNode) @@ -660,7 +618,7 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, fixup_uses!(ir, ci, code, slot.uses, idx, nothing) else val = code[slot.defs[]].args[2] - typ = typ_for_val(val, ci, ir.sptypes, slot.defs[], slottypes) + typ = typ_for_val(val, ci, ir, slot.defs[], sv.slottypes) ssaval = make_ssa!(ci, code, slot.defs[], typ) fixup_uses!(ir, ci, code, slot.uses, idx, ssaval) end @@ -675,11 +633,18 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, # an upsilon node in the corresponding enter block node = PhiCNode(Any[]) insertpoint = first_insert_for_bb(code, cfg, li) + varstate = sv.bb_vartables[li] + @assert varstate !== nothing + vt = varstate[idx] phic_ssa = NewSSAValue( insert_node!(ir, insertpoint, - NewInstruction(node, Union{})).id - length(ir.stmts)) + NewInstruction(node, vt.typ)).id - length(ir.stmts)) undef_node = undef_ssaval = nothing - if (ci.slotflags[idx] & SLOT_USEDUNDEF) != 0 + if vt.typ === Union{} + undef_ssaval = false + elseif !vt.undef + undef_ssaval = true + else undef_node = PhiCNode(Any[]) undef_ssaval = NewSSAValue(insert_node!(ir, insertpoint, NewInstruction(undef_node, Bool)).id - length(ir.stmts)) @@ -696,10 +661,17 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, for block in phiblocks push!(phi_slots[block], idx) node = PhiNode() + varstate = sv.bb_vartables[block] + @assert varstate !== nothing + vt = varstate[idx] ssaval = NewSSAValue(insert_node!(ir, - first_insert_for_bb(code, cfg, block), NewInstruction(node, Union{})).id - length(ir.stmts)) + first_insert_for_bb(code, cfg, block), NewInstruction(node, vt.typ)).id - length(ir.stmts)) undef_node = undef_ssaval = nothing - if (ci.slotflags[idx] & SLOT_USEDUNDEF) != 0 + if vt.typ === Union{} + undef_ssaval = false + elseif !vt.undef + undef_ssaval = true + else undef_node = PhiNode() undef_ssaval = NewSSAValue(insert_node!(ir, first_insert_for_bb(code, cfg, block), NewInstruction(undef_node, Bool)).id - length(ir.stmts)) @@ -719,7 +691,6 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, ] worklist = Tuple{Int, Int, Vector{Pair{Any, Any}}}[(1, 0, initial_incoming_vals)] visited = BitSet() - type_refine_phi = BitSet() new_nodes = ir.new_nodes @timeit "SSA Rename" while !isempty(worklist) (item::Int, pred, incoming_vals) = pop!(worklist) @@ -754,33 +725,38 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, else push!(node.values, incoming_val) end - outgoing_def = true - if (ci.slotflags[slot] & SLOT_USEDUNDEF) != 0 + if undef_node !== nothing push!(undef_node.edges, pred) push!(undef_node.values, incoming_def) - outgoing_def = undef_ssaval end - # TODO: Remove the next line, it shouldn't be necessary - push!(type_refine_phi, ssaval.id) - if isa(incoming_val, NewSSAValue) - push!(type_refine_phi, ssaval.id) - end - typ = incoming_val === UNDEF_TOKEN ? Union{} : typ_for_val(incoming_val, ci, ir.sptypes, -1, slottypes) - old_entry = new_nodes.stmts[ssaval.id] - if isa(typ, DelayedTyp) - push!(type_refine_phi, ssaval.id) - end - new_typ = isa(typ, DelayedTyp) ? Union{} : tmerge(๐•ƒโ‚’, old_entry[:type], typ) - old_entry[:type] = new_typ - old_entry[:stmt] = node - incoming_vals[slot] = Pair{Any, Any}(ssaval, outgoing_def) + + incoming_vals[slot] = Pair{Any, Any}(ssaval, undef_ssaval) end (item in visited) && continue # Record phi_C nodes if necessary if haskey(new_phic_nodes, item) for (; slot, insert) in new_phic_nodes[item] (; ssaval, undef_ssaval) = insert - incoming_vals[slot_id(slot)] = Pair{Any, Any}(ssaval, undef_ssaval === nothing ? true : undef_ssaval) + incoming_vals[slot_id(slot)] = Pair{Any, Any}(ssaval, undef_ssaval) + end + end + # Record Pi nodes if necessary + has_pinode = fill(false, length(sv.slottypes)) + for slot in 1:length(sv.slottypes) + (ival, idef) = incoming_vals[slot] + (ival === SSAValue(-1)) && continue + (ival === SSAValue(-2)) && continue + (ival === UNDEF_TOKEN) && continue + + varstate = sv.bb_vartables[item] + @assert varstate !== nothing + typ = varstate[slot].typ + if !โŠ‘(๐•ƒโ‚’, sv.slottypes[slot], typ) + node = PiNode(ival, typ) + ival = NewSSAValue(insert_node!(ir, + first_insert_for_bb(code, cfg, item), NewInstruction(node, typ)).id - length(ir.stmts)) + incoming_vals[slot] = Pair{Any, Any}(ival, idef) + has_pinode[slot] = true end end # Record initial upsilon nodes if necessary @@ -791,7 +767,7 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, (ival, idef) = incoming_vals[slot_id(slot)] ivalundef = ival === UNDEF_TOKEN ฮฅ = NewInstruction(ivalundef ? UpsilonNode() : UpsilonNode(ival), - ivalundef ? Union{} : typ_for_val(ival, ci, ir.sptypes, -1, slottypes)) + ivalundef ? Union{} : typ_for_val(ival, ci, ir, -1, sv.slottypes)) insertpos = first_insert_for_bb(code, cfg, item) # insert `UpsilonNode` immediately before the `:enter` expression ฮฅssa = insert_node!(ir, insertpos, ฮฅ) @@ -809,6 +785,7 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, (isa(stmt, PhiNode) || (isexpr(stmt, :(=)) && isa(stmt.args[2], PhiNode))) && continue if isa(stmt, NewvarNode) incoming_vals[slot_id(stmt.slot)] = Pair{Any, Any}(UNDEF_TOKEN, false) + has_pinode[slot_id(stmt.slot)] = false code[idx] = nothing else stmt = rename_uses!(ir, ci, idx, stmt, incoming_vals) @@ -823,7 +800,7 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, if isa(arg1, SlotNumber) id = slot_id(arg1) val = stmt.args[2] - typ = typ_for_val(val, ci, ir.sptypes, idx, slottypes) + typ = typ_for_val(val, ci, ir, idx, sv.slottypes) # Having UNDEF_TOKEN appear on the RHS is possible if we're on a dead branch. # Do something reasonable here, by marking the LHS as undef as well. if val !== UNDEF_TOKEN @@ -835,6 +812,7 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, thisdef = false end incoming_vals[id] = Pair{Any, Any}(thisval, thisdef) + has_pinode[id] = false enter_block = item while haskey(exc_handlers, enter_block) (; enter_block, leave_block) = exc_handlers[enter_block] @@ -858,6 +836,14 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, end end end + # Unwrap any PiNodes before continuing, since they weren't considered during our + # dominance frontier calculation and so have to be used locally in each BB. + for (i, (ival, idef)) in enumerate(incoming_vals) + if has_pinode[i] + stmt = ir[new_to_regular(ival::NewSSAValue, length(ir.stmts))][:stmt] + incoming_vals[i] = Pair{Any, Any}(stmt.val, idef) + end + end for succ in cfg.blocks[item].succs push!(worklist, (succ, item, copy(incoming_vals))) end @@ -877,7 +863,6 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, nstmts = length(ir.stmts) new_code = Vector{Any}(undef, nstmts) ssavalmap = fill(SSAValue(-1), length(ssavaluetypes) + 1) - result_types = Any[Any for _ in 1:nstmts] # Detect statement positions for assignments and construct array for (bb, idx) in bbidxiter(ir) stmt = code[idx] @@ -900,7 +885,6 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, new_code[idx] = stmt else ssavalmap[idx] = SSAValue(idx) - result_types[idx] = ssavaluetypes[idx] if isa(stmt, PhiNode) edges = Int32[edge == 0 ? 0 : block_for_inst(cfg, Int(edge)) for edge in stmt.edges] new_code[idx] = PhiNode(edges, stmt.values) @@ -909,60 +893,13 @@ function construct_ssa!(ci::CodeInfo, ir::IRCode, domtree::DomTree, end end end - for (_, nodes) in new_phic_nodes - for (; insert) in nodes - (; node, ssaval) = insert - new_typ = Union{} - # TODO: This could just be the ones that depend on other phis - push!(type_refine_phi, ssaval.id) - new_idx = ssaval.id - node = new_nodes.stmts[new_idx] - phic_values = (node[:stmt]::PhiCNode).values - for i = 1:length(phic_values) - orig_typ = typ = typ_for_val(phic_values[i], ci, ir.sptypes, -1, slottypes) - while isa(typ, DelayedTyp) - typ = types(ir)[new_to_regular(typ.phi::NewSSAValue, nstmts)] - end - new_typ = tmerge(๐•ƒโ‚’, new_typ, typ) - end - node[:type] = new_typ - end - end - # This is a bit awkward, because it basically duplicates what type - # inference does. Ideally, we'd just use this representation earlier - # to make sure phi nodes have accurate types - changed = true - while changed - changed = false - for new_idx in type_refine_phi - node = new_nodes.stmts[new_idx] - new_typ = recompute_type(node[:stmt]::Union{PhiNode,PhiCNode}, ci, ir, ir.sptypes, slottypes, nstmts, ๐•ƒโ‚’) - if !โŠ‘(๐•ƒโ‚’, node[:type], new_typ) || !โŠ‘(๐•ƒโ‚’, new_typ, node[:type]) - node[:type] = new_typ - changed = true - end - end - end - for i in 1:length(result_types) - rt_i = result_types[i] - if rt_i isa DelayedTyp - result_types[i] = types(ir)[new_to_regular(rt_i.phi::NewSSAValue, nstmts)] - end - end - for i = 1:length(new_nodes) - local node = new_nodes.stmts[i] - local typ = node[:type] - if isa(typ, DelayedTyp) - node[:type] = types(ir)[new_to_regular(typ.phi::NewSSAValue, nstmts)] - end - end # Renumber SSA values @assert isempty(ir.stmts.type) resize!(ir.stmts.type, nstmts) for i in 1:nstmts local node = ir.stmts[i] node[:stmt] = new_to_regular(renumber_ssa!(new_code[i], ssavalmap), nstmts) - node[:type] = result_types[i] + node[:type] = ssavaluetypes[i] end for i = 1:length(new_nodes) local node = new_nodes.stmts[i] diff --git a/base/compiler/ssair/verify.jl b/base/compiler/ssair/verify.jl index c9cef58f3566d..11a0144529453 100644 --- a/base/compiler/ssair/verify.jl +++ b/base/compiler/ssair/verify.jl @@ -70,7 +70,7 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int, elseif isa(op, Union{OldSSAValue, NewSSAValue}) @verify_error "Left over SSA marker" error("") - elseif isa(op, UnoptSlot) + elseif isa(op, SlotNumber) @verify_error "Left over slot detected in converted IR" error("") end diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 55b231e004e3e..20996be4faaed 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -2303,33 +2303,42 @@ end function getfield_effects(๐•ƒ::AbstractLattice, arginfo::ArgInfo, @nospecialize(rt)) (;argtypes) = arginfo - # consistent if the argtype is immutable length(argtypes) < 3 && return EFFECTS_THROWS obj = argtypes[2] - isvarargtype(obj) && return Effects(EFFECTS_THROWS; consistent=ALWAYS_FALSE) + if isvarargtype(obj) + return Effects(EFFECTS_TOTAL; + consistent=CONSISTENT_IF_INACCESSIBLEMEMONLY, + nothrow=false, + inaccessiblememonly=ALWAYS_FALSE, + noub=false) + end + # :consistent if the argtype is immutable consistent = (is_immutable_argtype(obj) || is_mutation_free_argtype(obj)) ? ALWAYS_TRUE : CONSISTENT_IF_INACCESSIBLEMEMONLY - # access to `isbitstype`-field initialized with undefined value leads to undefined behavior - # so should taint `:consistent`-cy while access to uninitialized non-`isbitstype` field - # throws `UndefRefError` so doesn't need to taint it + # taint `:consistent` if accessing `isbitstype`-type object field that may be initialized + # with undefined value: note that we don't need to taint `:consistent` if accessing + # uninitialized non-`isbitstype` field since it will simply throw `UndefRefError` # NOTE `getfield_notundefined` conservatively checks if this field is never initialized - # with undefined value so that we don't taint `:consistent`-cy too aggressively here + # with undefined value to avoid tainting `:consistent` too aggressively + # TODO this should probably taint `:noub`, however, it would hinder concrete eval for + # `REPLInterpreter` that can ignore `:consistent-cy`, causing worse completions if !(length(argtypes) โ‰ฅ 3 && getfield_notundefined(obj, argtypes[3])) consistent = ALWAYS_FALSE end + noub = true bcheck = getfield_boundscheck(arginfo) nothrow = getfield_nothrow(๐•ƒ, arginfo, bcheck) if !nothrow if !(bcheck === :on || bcheck === :boundscheck) - # If we cannot independently prove inboundsness, taint consistency. - # The inbounds-ness assertion requires dynamic reachability, while - # :consistent needs to be true for all input values. + # If we cannot independently prove inboundsness, taint `:noub`. + # The inbounds-ness assertion requires dynamic reachability, + # while `:noub` needs to be true for all input values. # However, as a special exception, we do allow literal `:boundscheck`. - # `:consistent`-cy will be tainted in any caller using `@inbounds` based - # on the `:noinbounds` effect. - # N.B. We do not taint for `--check-bounds=no` here. That is handled - # in concrete evaluation. - consistent = ALWAYS_FALSE + # `:noub` will be tainted in any caller using `@inbounds` + # based on the `:noinbounds` effect. + # N.B. We do not taint for `--check-bounds=no` here. + # That is handled in concrete evaluation. + noub = false end end if hasintersect(widenconst(obj), Module) @@ -2339,7 +2348,7 @@ function getfield_effects(๐•ƒ::AbstractLattice, arginfo::ArgInfo, @nospecialize else inaccessiblememonly = INACCESSIBLEMEM_OR_ARGMEMONLY end - return Effects(EFFECTS_TOTAL; consistent, nothrow, inaccessiblememonly) + return Effects(EFFECTS_TOTAL; consistent, nothrow, inaccessiblememonly, noub) end function getglobal_effects(argtypes::Vector{Any}, @nospecialize(rt)) @@ -2371,8 +2380,15 @@ function builtin_effects(๐•ƒ::AbstractLattice, @nospecialize(f::Builtin), argin if f === getfield return getfield_effects(๐•ƒ, arginfo, rt) end - argtypes = arginfo.argtypes[2:end] + # TODO taint `:noub` for `arrayref` and `arrayset` here + + # if this builtin call deterministically throws, + # don't bother to taint the other effects other than :nothrow: + # note this is safe only if we accounted for :noub already + rt === Bottom && return EFFECTS_THROWS + + argtypes = arginfo.argtypes[2:end] if f === isdefined return isdefined_effects(๐•ƒ, argtypes) elseif f === getglobal @@ -2622,13 +2638,15 @@ function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, s if length(argtypes) == 3 aft = widenslotwrapper(argtypes[2]) - if !isa(aft, Const) && !(isType(aft) && !has_free_typevars(aft)) && - !(isconcretetype(aft) && !(aft <: Builtin)) - return UNKNOWN - end argtypes_vec = Any[aft, af_argtype.parameters...] else argtypes_vec = Any[af_argtype.parameters...] + isempty(argtypes_vec) && push!(argtypes_vec, Union{}) + aft = argtypes_vec[1] + end + if !(isa(aft, Const) || (isType(aft) && !has_free_typevars(aft)) || + (isconcretetype(aft) && !(aft <: Builtin) && !iskindtype(aft))) + return UNKNOWN end if contains_is(argtypes_vec, Union{}) @@ -2661,8 +2679,7 @@ function return_type_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, s # in two ways: both as being a subtype of this, and # because of LimitedAccuracy causes return CallMeta(Type{<:rt}, EFFECTS_TOTAL, info) - elseif (isa(tt, Const) || isconstType(tt)) && - (isa(aft, Const) || isconstType(aft)) + elseif isa(tt, Const) || isconstType(tt) # input arguments were known for certain # XXX: this doesn't imply we know anything about rt return CallMeta(Const(rt), EFFECTS_TOTAL, info) @@ -2759,7 +2776,6 @@ function _hasmethod_tfunc(interp::AbstractInterpreter, argtypes::Vector{Any}, sv return CallMeta(rt, EFFECTS_TOTAL, NoCallInfo()) end - # N.B.: typename maps type equivalence classes to a single value function typename_static(@nospecialize(t)) t isa Const && return _typename(t.val) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 1342722f53206..0cd14c2507a41 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -497,6 +497,9 @@ function adjust_effects(sv::InferenceState) if is_effect_overridden(override, :inaccessiblememonly) ipo_effects = Effects(ipo_effects; inaccessiblememonly=ALWAYS_TRUE) end + if is_effect_overridden(override, :noub) + ipo_effects = Effects(ipo_effects; noub=true) + end end return ipo_effects @@ -550,8 +553,8 @@ function finish(me::InferenceState, interp::AbstractInterpreter) else # annotate fulltree with type information, # either because we are the outermost code, or we might use this later + type_annotate!(interp, me) doopt = (me.cached || me.parent !== nothing) - type_annotate!(interp, me, doopt) if doopt && may_optimize(interp) me.result.src = OptimizationState(me, interp) else @@ -625,46 +628,6 @@ function record_bestguess!(sv::InferenceState) return nothing end -function annotate_slot_load!(interp::AbstractInterpreter, undefs::Vector{Bool}, idx::Int, sv::InferenceState, @nospecialize x) - if isa(x, SlotNumber) - id = slot_id(x) - pc = find_dominating_assignment(id, idx, sv) - if pc === nothing - block = block_for_inst(sv.cfg, idx) - state = sv.bb_vartables[block]::VarTable - vt = state[id] - undefs[id] |= vt.undef - typ = widenslotwrapper(ignorelimited(vt.typ)) - else - typ = sv.ssavaluetypes[pc] - @assert typ !== NOT_FOUND "active slot in unreached region" - end - # add type annotations where needed - if !โŠ‘(typeinf_lattice(interp), sv.slottypes[id], typ) - return TypedSlot(id, typ) - end - return x - elseif isa(x, Expr) - head = x.head - i0 = 1 - if is_meta_expr_head(head) || head === :const - return x - end - if head === :(=) || head === :method - i0 = 2 - end - for i = i0:length(x.args) - x.args[i] = annotate_slot_load!(interp, undefs, idx, sv, x.args[i]) - end - return x - elseif isa(x, ReturnNode) && isdefined(x, :val) - return ReturnNode(annotate_slot_load!(interp, undefs, idx, sv, x.val)) - elseif isa(x, GotoIfNot) - return GotoIfNot(annotate_slot_load!(interp, undefs, idx, sv, x.cond), x.dest) - end - return x -end - # find the dominating assignment to the slot `id` in the block containing statement `idx`, # returns `nothing` otherwise function find_dominating_assignment(id::Int, idx::Int, sv::InferenceState) @@ -682,7 +645,7 @@ function find_dominating_assignment(id::Int, idx::Int, sv::InferenceState) end # annotate types of all symbols in AST, preparing for optimization -function type_annotate!(interp::AbstractInterpreter, sv::InferenceState, run_optimizer::Bool) +function type_annotate!(interp::AbstractInterpreter, sv::InferenceState) # widen `Conditional`s from `slottypes` slottypes = sv.slottypes for i = 1:length(slottypes) @@ -696,72 +659,42 @@ function type_annotate!(interp::AbstractInterpreter, sv::InferenceState, run_opt record_bestguess!(sv) # annotate variables load types - # remove dead code optimization - # and compute which variables may be used undef src = sv.src stmts = src.code nstmt = length(stmts) ssavaluetypes = sv.ssavaluetypes - slotflags = src.slotflags - nslots = length(slotflags) - undefs = fill(false, nslots) - any_unreachable = false - - # this statement traversal does five things: - # 1. introduce temporary `TypedSlot`s that are supposed to be replaced with ฯ€-nodes later - # 2. mark used-undef slots (required by the `slot2reg` conversion) - # 3. mark unreached statements for a bulk code deletion (see issue #7836) - # 4. widen slot wrappers (`Conditional` and `MustAlias`) and remove `NOT_FOUND` from `ssavaluetypes` - # NOTE because of this, `was_reached` will no longer be available after this point - # 5. eliminate GotoIfNot if either or both branches are statically unreachable - changemap = nothing # initialized if there is any dead region + nslots = length(src.slotflags) + + # widen slot wrappers (`Conditional` and `MustAlias`) and remove `NOT_FOUND` from `ssavaluetypes` + # and mark any unreachable statements by wrapping them in Const(...), to distinguish them from + # must-throw statements which also have type Bottom for i = 1:nstmt - expr = stmts[i] if was_reached(sv, i) - if run_optimizer - if isa(expr, GotoIfNot) - # 5: replace this live GotoIfNot with: - # - no-op if :nothrow and the branch target is unreachable - # - cond if :nothrow and both targets are unreachable - # - typeassert if must-throw - if widenconst(argextype(expr.cond, src, sv.sptypes)) === Bool - block = block_for_inst(sv.cfg, i) - if !was_reached(sv, i+1) - cfg_delete_edge!(sv.cfg, block, block + 1) - expr = GotoNode(expr.dest) - elseif !was_reached(sv, expr.dest) - cfg_delete_edge!(sv.cfg, block, block_for_inst(sv.cfg, expr.dest)) - expr = nothing - end - elseif ssavaluetypes[i] === Bottom - block = block_for_inst(sv.cfg, i) - cfg_delete_edge!(sv.cfg, block, block + 1) - cfg_delete_edge!(sv.cfg, block, block_for_inst(sv.cfg, expr.dest)) - expr = Expr(:call, Core.typeassert, expr.cond, Bool) - end - end - end - stmts[i] = annotate_slot_load!(interp, undefs, i, sv, expr) # 1&2 - ssavaluetypes[i] = widenslotwrapper(ssavaluetypes[i]) # 4 + ssavaluetypes[i] = widenslotwrapper(ssavaluetypes[i]) # 3 else # i.e. any runtime execution will never reach this statement - any_unreachable = true - if is_meta_expr(expr) # keep any lexically scoped expressions - ssavaluetypes[i] = Any # 4 + push!(sv.unreachable, i) + if is_meta_expr(stmts[i]) # keep any lexically scoped expressions + ssavaluetypes[i] = Any # 3 else - ssavaluetypes[i] = Bottom # 4 - stmts[i] = Const(expr) # annotate that this statement actually is dead + ssavaluetypes[i] = Bottom # 3 + # annotate that this statement actually is dead + stmts[i] = Const(stmts[i]) end end end - # finish marking used-undef variables - for j = 1:nslots - if undefs[j] - slotflags[j] |= SLOT_USEDUNDEF | SLOT_STATICUNDEF + # widen slot wrappers (`Conditional` and `MustAlias`) in `bb_vartables` + for varstate in sv.bb_vartables + if varstate !== nothing + for slot in 1:nslots + vt = varstate[slot] + widened_type = widenslotwrapper(ignorelimited(vt.typ)) + varstate[slot] = VarState(widened_type, vt.undef) + end end end - return any_unreachable + return nothing end # at the end, all items in b's cycle diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index 4552c4f815337..d1e08dd1e1676 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -676,7 +676,7 @@ end return tmerge(wl, typea, typeb) end -@nospecializeinfer function tmerge(::JLTypeLattice, @nospecialize(typea::Type), @nospecialize(typeb::Type)) +@nospecializeinfer function tmerge(lattice::JLTypeLattice, @nospecialize(typea::Type), @nospecialize(typeb::Type)) # it's always ok to form a Union of two concrete types act = isconcretetype(typea) bct = isconcretetype(typeb) @@ -687,8 +687,8 @@ end if (act || isType(typea)) && (bct || isType(typeb)) return Union{typea, typeb} end - typea <: typeb && return typeb - typeb <: typea && return typea + u = tmerge_fast_path(lattice, typea, typeb) + u === nothing || return u return tmerge_types_slow(typea, typeb) end diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index f3c5694535ce6..e85605b55b6eb 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -489,8 +489,7 @@ end # using a function to ensure we can infer this @inline function slot_id(s) isa(s, SlotNumber) && return s.id - isa(s, Argument) && return s.n - return (s::TypedSlot).id + return (s::Argument).n end ########### diff --git a/base/compiler/validation.jl b/base/compiler/validation.jl index 68eb2ab15c59d..4f407475b7190 100644 --- a/base/compiler/validation.jl +++ b/base/compiler/validation.jl @@ -230,10 +230,10 @@ end validate_code(args...) = validate_code!(Vector{InvalidCodeError}(), args...) -is_valid_lvalue(@nospecialize(x)) = isa(x, UnoptSlot) || isa(x, GlobalRef) +is_valid_lvalue(@nospecialize(x)) = isa(x, SlotNumber) || isa(x, GlobalRef) function is_valid_argument(@nospecialize(x)) - if isa(x, UnoptSlot) || isa(x, Argument) || isa(x, SSAValue) || + if isa(x, SlotNumber) || isa(x, Argument) || isa(x, SSAValue) || isa(x, GlobalRef) || isa(x, QuoteNode) || isexpr(x, (:static_parameter, :boundscheck)) || isa(x, Number) || isa(x, AbstractString) || isa(x, AbstractChar) || isa(x, Tuple) || isa(x, Type) || isa(x, Core.Box) || isa(x, Module) || x === nothing diff --git a/base/essentials.jl b/base/essentials.jl index b652d62628336..0d32116514052 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -208,7 +208,8 @@ macro _total_meta() #=:terminates_globally=#true, #=:terminates_locally=#false, #=:notaskstate=#true, - #=:inaccessiblememonly=#true)) + #=:inaccessiblememonly=#true, + #=:noub=#true)) end # can be used in place of `@assume_effects :foldable` (supposed to be used for bootstrapping) macro _foldable_meta() @@ -219,7 +220,8 @@ macro _foldable_meta() #=:terminates_globally=#true, #=:terminates_locally=#false, #=:notaskstate=#false, - #=:inaccessiblememonly=#true)) + #=:inaccessiblememonly=#true, + #=:noub=#true)) end # can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping) macro _nothrow_meta() @@ -230,7 +232,8 @@ macro _nothrow_meta() #=:terminates_globally=#false, #=:terminates_locally=#false, #=:notaskstate=#false, - #=:inaccessiblememonly=#false)) + #=:inaccessiblememonly=#false, + #=:noub=#false)) end # can be used in place of `@assume_effects :terminates_locally` (supposed to be used for bootstrapping) macro _terminates_locally_meta() @@ -241,7 +244,8 @@ macro _terminates_locally_meta() #=:terminates_globally=#false, #=:terminates_locally=#true, #=:notaskstate=#false, - #=:inaccessiblememonly=#false)) + #=:inaccessiblememonly=#false, + #=:noub=#false)) end # can be used in place of `@assume_effects :effect_free :terminates_locally` (supposed to be used for bootstrapping) macro _effect_free_terminates_locally_meta() @@ -252,7 +256,8 @@ macro _effect_free_terminates_locally_meta() #=:terminates_globally=#false, #=:terminates_locally=#true, #=:notaskstate=#false, - #=:inaccessiblememonly=#false)) + #=:inaccessiblememonly=#false, + #=:noub=#false)) end # another version of inlining that propagates an inbounds context @@ -933,9 +938,10 @@ Determine whether a collection is empty (has no elements). !!! warning `isempty(itr)` may consume the next element of a stateful iterator `itr` - unless an appropriate `Base.isdone(itr)` or `isempty` method is defined. - Use of `isempty` should therefore be avoided when writing generic - code which should support any iterator type. + unless an appropriate [`Base.isdone(itr)`](@ref) method is defined. + Stateful iterators *should* implement `isdone`, but you may want to avoid + using `isempty` when writing generic code which should support any iterator + type. # Examples ```jldoctest @@ -1044,17 +1050,21 @@ end # Iteration """ - isdone(itr, state...) -> Union{Bool, Missing} + isdone(itr, [state]) -> Union{Bool, Missing} This function provides a fast-path hint for iterator completion. -This is useful for mutable iterators that want to avoid having elements -consumed, if they are not going to be exposed to the user (e.g. to check -for done-ness in `isempty` or `zip`). Mutable iterators that want to -opt into this feature should define an isdone method that returns -true/false depending on whether the iterator is done or not. Stateless -iterators need not implement this function. If the result is `missing`, -callers may go ahead and compute `iterate(x, state...) === nothing` to -compute a definite answer. +This is useful for stateful iterators that want to avoid having elements +consumed if they are not going to be exposed to the user (e.g. when checking +for done-ness in `isempty` or `zip`). + +Stateful iterators that want to opt into this feature should define an `isdone` +method that returns true/false depending on whether the iterator is done or +not. Stateless iterators need not implement this function. + +If the result is `missing`, callers may go ahead and compute +`iterate(x, state) === nothing` to compute a definite answer. + +See also [`iterate`](@ref), [`isempty`](@ref) """ isdone(itr, state...) = missing diff --git a/base/experimental.jl b/base/experimental.jl index d2f5fc6095b02..af67d9e019378 100644 --- a/base/experimental.jl +++ b/base/experimental.jl @@ -9,7 +9,7 @@ """ module Experimental -using Base: Threads, sync_varname +using Base: Threads, sync_varname, is_function_def using Base.Meta """ @@ -334,21 +334,25 @@ Define a method and add it to the method table `mt` instead of to the global met This can be used to implement a method override mechanism. Regular compilation will not consider these methods, and you should customize the compilation flow to look in these method tables (e.g., using [`Core.Compiler.OverlayMethodTable`](@ref)). - """ macro overlay(mt, def) def = macroexpand(__module__, def) # to expand @inline, @generated, etc - if !isexpr(def, [:function, :(=)]) - error("@overlay requires a function Expr") - end - if isexpr(def.args[1], :call) - def.args[1].args[1] = Expr(:overlay, mt, def.args[1].args[1]) - elseif isexpr(def.args[1], :where) - def.args[1].args[1].args[1] = Expr(:overlay, mt, def.args[1].args[1].args[1]) + is_function_def(def) || error("@overlay requires a function definition") + return esc(overlay_def!(mt, def)) +end + +function overlay_def!(mt, @nospecialize ex) + arg1 = ex.args[1] + if isexpr(arg1, :call) + arg1.args[1] = Expr(:overlay, mt, arg1.args[1]) + elseif isexpr(arg1, :(::)) + overlay_def!(mt, arg1) + elseif isexpr(arg1, :where) + overlay_def!(mt, arg1) else - error("@overlay requires a function Expr") + error("@overlay requires a function definition") end - esc(def) + return ex end let new_mt(name::Symbol, mod::Module) = begin diff --git a/base/expr.jl b/base/expr.jl index b1bf938ec7fac..82e88f4ba9749 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -478,6 +478,7 @@ The following `setting`s are supported. - `:terminates_locally` - `:notaskstate` - `:inaccessiblememonly` +- `:noub` - `:foldable` - `:removable` - `:total` @@ -515,13 +516,6 @@ The `:consistent` setting asserts that for egal (`===`) inputs: even for the same world age (e.g. because one ran in the interpreter, while the other was optimized). -!!! note - The `:consistent`-cy assertion currently includes the assertion that the function - will not execute any undefined behavior (for any input). Note that undefined behavior - may technically cause the function to violate other effect assertions (such as - `:nothrow` or `:effect_free`) as well, but we do not model this, and all effects - except `:consistent` assume the absence of undefined behavior. - !!! note If `:consistent` functions terminate by throwing an exception, that exception itself is not required to meet the egality requirement specified above. @@ -641,6 +635,14 @@ global state or mutable memory pointed to by its arguments. !!! note This `:inaccessiblememonly` assertion covers any other methods called by the annotated method. +--- +## `:noub` + +The `:noub` setting asserts that the method will not execute any undefined behavior +(for any input). Note that undefined behavior may technically cause the method to violate +any other effect assertions (such as `:consistent` or `:effect_free`) as well, but we do +not model this, and they assume the absence of undefined behavior. + --- ## `:foldable` @@ -650,6 +652,7 @@ currently equivalent to the following `setting`s: - `:consistent` - `:effect_free` - `:terminates_globally` +- `:noub` !!! note This list in particular does not include `:nothrow`. The compiler will still @@ -682,6 +685,7 @@ the following other `setting`s: - `:terminates_globally` - `:notaskstate` - `:inaccessiblememonly` +- `:noub` !!! warning `:total` is a very strong assertion and will likely gain additional semantics @@ -712,8 +716,8 @@ macro assume_effects(args...) ex = nothing idx = length(args) end - (consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly) = - (false, false, false, false, false, false, false, false) + (consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly, noub) = + (false, false, false, false, false, false, false, false, false) for org_setting in args[1:idx] (setting, val) = compute_assumed_setting(org_setting) if setting === :consistent @@ -730,28 +734,29 @@ macro assume_effects(args...) notaskstate = val elseif setting === :inaccessiblememonly inaccessiblememonly = val + elseif setting === :noub + noub = val elseif setting === :foldable - consistent = effect_free = terminates_globally = val + consistent = effect_free = terminates_globally = noub = val elseif setting === :removable effect_free = nothrow = terminates_globally = val elseif setting === :total - consistent = effect_free = nothrow = terminates_globally = notaskstate = inaccessiblememonly = val + consistent = effect_free = nothrow = terminates_globally = notaskstate = inaccessiblememonly = noub = val else throw(ArgumentError("@assume_effects $org_setting not supported")) end end if is_function_def(inner) return esc(pushmeta!(ex, :purity, - consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly)) + consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly, noub)) elseif isexpr(ex, :macrocall) && ex.args[1] === Symbol("@ccall") ex.args[1] = GlobalRef(Base, Symbol("@ccall_effects")) insert!(ex.args, 3, Core.Compiler.encode_effects_override(Core.Compiler.EffectsOverride( - consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly, - ))) + consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly, noub))) return esc(ex) else # anonymous function case return Expr(:meta, Expr(:purity, - consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly)) + consistent, effect_free, nothrow, terminates_globally, terminates_locally, notaskstate, inaccessiblememonly, noub)) end end diff --git a/base/float.jl b/base/float.jl index 3a17d517bbb89..597a9d6e43234 100644 --- a/base/float.jl +++ b/base/float.jl @@ -137,6 +137,68 @@ i.e. the maximum integer value representable by [`exponent_bits(T)`](@ref) bits. """ function exponent_raw_max end +""" +IEEE 754 definition of the minimum exponent. +""" +ieee754_exponent_min(::Type{T}) where {T<:IEEEFloat} = Int(1 - exponent_max(T))::Int + +exponent_min(::Type{Float16}) = ieee754_exponent_min(Float16) +exponent_min(::Type{Float32}) = ieee754_exponent_min(Float32) +exponent_min(::Type{Float64}) = ieee754_exponent_min(Float64) + +function ieee754_representation( + ::Type{F}, sign_bit::Bool, exponent_field::Integer, significand_field::Integer +) where {F<:IEEEFloat} + T = uinttype(F) + ret::T = sign_bit + ret <<= exponent_bits(F) + ret |= exponent_field + ret <<= significand_bits(F) + ret |= significand_field +end + +# ยฑfloatmax(T) +function ieee754_representation( + ::Type{F}, sign_bit::Bool, ::Val{:omega} +) where {F<:IEEEFloat} + ieee754_representation(F, sign_bit, exponent_raw_max(F) - 1, significand_mask(F)) +end + +# NaN or an infinity +function ieee754_representation( + ::Type{F}, sign_bit::Bool, significand_field::Integer, ::Val{:nan} +) where {F<:IEEEFloat} + ieee754_representation(F, sign_bit, exponent_raw_max(F), significand_field) +end + +# NaN with default payload +function ieee754_representation( + ::Type{F}, sign_bit::Bool, ::Val{:nan} +) where {F<:IEEEFloat} + ieee754_representation(F, sign_bit, one(uinttype(F)) << (significand_bits(F) - 1), Val(:nan)) +end + +# Infinity +function ieee754_representation( + ::Type{F}, sign_bit::Bool, ::Val{:inf} +) where {F<:IEEEFloat} + ieee754_representation(F, sign_bit, false, Val(:nan)) +end + +# Subnormal or zero +function ieee754_representation( + ::Type{F}, sign_bit::Bool, significand_field::Integer, ::Val{:subnormal} +) where {F<:IEEEFloat} + ieee754_representation(F, sign_bit, false, significand_field) +end + +# Zero +function ieee754_representation( + ::Type{F}, sign_bit::Bool, ::Val{:zero} +) where {F<:IEEEFloat} + ieee754_representation(F, sign_bit, false, Val(:subnormal)) +end + """ uabs(x::Integer) diff --git a/base/initdefs.jl b/base/initdefs.jl index 2eb05269eebd9..c04a97971eff2 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -247,8 +247,8 @@ end function load_path_expand(env::AbstractString)::Union{String, Nothing} # named environment? if startswith(env, '@') - # `@` in JULIA_LOAD_PATH is expanded early (at startup time) - # if you put a `@` in LOAD_PATH manually, it's expanded late + # `@.` in JULIA_LOAD_PATH is expanded early (at startup time) + # if you put a `@.` in LOAD_PATH manually, it's expanded late env == "@" && return active_project(false) env == "@." && return current_project() env == "@stdlib" && return Sys.STDLIB diff --git a/base/int.jl b/base/int.jl index a0bfe24af89ec..05457d9cf55c3 100644 --- a/base/int.jl +++ b/base/int.jl @@ -754,6 +754,15 @@ julia> big"_" ERROR: ArgumentError: invalid number format _ for BigInt or BigFloat [...] ``` + +!!! warning + Using `@big_str` for constructing [`BigFloat`](@ref) values may not result + in the behavior that might be naively expected: as a macro, `@big_str` + obeys the global precision ([`setprecision`](@ref)) and rounding mode + ([`setrounding`](@ref)) settings as they are at *load time*. Thus, a + function like `() -> precision(big"0.3")` returns a constant whose value + depends on the value of the precision at the point when the function is + defined, **not** at the precision at the time when the function is called. """ macro big_str(s) message = "invalid number format $s for BigInt or BigFloat" diff --git a/base/mpfr.jl b/base/mpfr.jl index 7987c12857025..b349f405cda93 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -17,11 +17,15 @@ import cbrt, typemax, typemin, unsafe_trunc, floatmin, floatmax, rounding, setrounding, maxintfloat, widen, significand, frexp, tryparse, iszero, isone, big, _string_n, decompose, minmax, - sinpi, cospi, sincospi, tanpi, sind, cosd, tand, asind, acosd, atand + sinpi, cospi, sincospi, tanpi, sind, cosd, tand, asind, acosd, atand, + uinttype, exponent_max, exponent_min, ieee754_representation, significand_mask, + RawBigIntRoundingIncrementHelper, truncated, RawBigInt using .Base.Libc -import ..Rounding: rounding_raw, setrounding_raw +import ..Rounding: + rounding_raw, setrounding_raw, rounds_to_nearest, rounds_away_from_zero, + tie_breaker_is_to_even, correct_rounding_requires_increment import ..GMP: ClongMax, CulongMax, CdoubleMax, Limb, libgmp @@ -89,6 +93,21 @@ function convert(::Type{RoundingMode}, r::MPFRRoundingMode) end end +rounds_to_nearest(m::MPFRRoundingMode) = m == MPFRRoundNearest +function rounds_away_from_zero(m::MPFRRoundingMode, sign_bit::Bool) + if m == MPFRRoundToZero + false + elseif m == MPFRRoundUp + !sign_bit + elseif m == MPFRRoundDown + sign_bit + else + # Assuming `m == MPFRRoundFromZero` + true + end +end +tie_breaker_is_to_even(::MPFRRoundingMode) = true + const ROUNDING_MODE = Ref{MPFRRoundingMode}(MPFRRoundNearest) const DEFAULT_PRECISION = Ref{Clong}(256) @@ -136,6 +155,9 @@ mutable struct BigFloat <: AbstractFloat end end +# The rounding mode here shouldn't matter. +significand_limb_count(x::BigFloat) = div(sizeof(x._d), sizeof(Limb), RoundToZero) + rounding_raw(::Type{BigFloat}) = ROUNDING_MODE[] setrounding_raw(::Type{BigFloat}, r::MPFRRoundingMode) = ROUNDING_MODE[]=r @@ -386,35 +408,69 @@ function (::Type{T})(x::BigFloat) where T<:Integer trunc(T,x) end -## BigFloat -> AbstractFloat -_cpynansgn(x::AbstractFloat, y::BigFloat) = isnan(x) && signbit(x) != signbit(y) ? -x : x - -Float64(x::BigFloat, r::MPFRRoundingMode=ROUNDING_MODE[]) = - _cpynansgn(ccall((:mpfr_get_d,libmpfr), Float64, (Ref{BigFloat}, MPFRRoundingMode), x, r), x) -Float64(x::BigFloat, r::RoundingMode) = Float64(x, convert(MPFRRoundingMode, r)) - -Float32(x::BigFloat, r::MPFRRoundingMode=ROUNDING_MODE[]) = - _cpynansgn(ccall((:mpfr_get_flt,libmpfr), Float32, (Ref{BigFloat}, MPFRRoundingMode), x, r), x) -Float32(x::BigFloat, r::RoundingMode) = Float32(x, convert(MPFRRoundingMode, r)) - -function Float16(x::BigFloat) :: Float16 - res = Float32(x) - resi = reinterpret(UInt32, res) - if (resi&0x7fffffff) < 0x38800000 # if Float16(res) is subnormal - #shift so that the mantissa lines up where it would for normal Float16 - shift = 113-((resi & 0x7f800000)>>23) - if shift<23 - resi |= 0x0080_0000 # set implicit bit - resi >>= shift +function to_ieee754(::Type{T}, x::BigFloat, rm) where {T<:AbstractFloat} + sb = signbit(x) + is_zero = iszero(x) + is_inf = isinf(x) + is_nan = isnan(x) + is_regular = !is_zero & !is_inf & !is_nan + ieee_exp = Int(x.exp) - 1 + ieee_precision = precision(T) + ieee_exp_max = exponent_max(T) + ieee_exp_min = exponent_min(T) + exp_diff = ieee_exp - ieee_exp_min + is_normal = 0 โ‰ค exp_diff + (rm_is_to_zero, rm_is_from_zero) = if rounds_to_nearest(rm) + (false, false) + else + let from = rounds_away_from_zero(rm, sb) + (!from, from) end - end - if (resi & 0x1fff == 0x1000) # if we are halfway between 2 Float16 values - # adjust the value by 1 ULP in the direction that will make Float16(res) give the right answer - res = nextfloat(res, cmp(x, res)) - end - return res + end::NTuple{2,Bool} + exp_is_huge_p = ieee_exp_max < ieee_exp + exp_is_huge_n = signbit(exp_diff + ieee_precision) + rounds_to_inf = is_regular & exp_is_huge_p & !rm_is_to_zero + rounds_to_zero = is_regular & exp_is_huge_n & !rm_is_from_zero + U = uinttype(T) + + ret_u = if is_regular & !rounds_to_inf & !rounds_to_zero + if !exp_is_huge_p + # significand + v = RawBigInt(x.d, significand_limb_count(x)) + len = max(ieee_precision + min(exp_diff, 0), 0)::Int + signif = truncated(U, v, len) & significand_mask(T) + + # round up if necessary + rh = RawBigIntRoundingIncrementHelper(v, len) + incr = correct_rounding_requires_increment(rh, rm, sb) + + # exponent + exp_field = max(exp_diff, 0) + is_normal + + ieee754_representation(T, sb, exp_field, signif) + incr + else + ieee754_representation(T, sb, Val(:omega)) + end + else + if is_zero | rounds_to_zero + ieee754_representation(T, sb, Val(:zero)) + elseif is_inf | rounds_to_inf + ieee754_representation(T, sb, Val(:inf)) + else + ieee754_representation(T, sb, Val(:nan)) + end + end::U + + reinterpret(T, ret_u) end +Float16(x::BigFloat, r::MPFRRoundingMode=ROUNDING_MODE[]) = to_ieee754(Float16, x, r) +Float32(x::BigFloat, r::MPFRRoundingMode=ROUNDING_MODE[]) = to_ieee754(Float32, x, r) +Float64(x::BigFloat, r::MPFRRoundingMode=ROUNDING_MODE[]) = to_ieee754(Float64, x, r) +Float16(x::BigFloat, r::RoundingMode) = to_ieee754(Float16, x, r) +Float32(x::BigFloat, r::RoundingMode) = to_ieee754(Float32, x, r) +Float64(x::BigFloat, r::RoundingMode) = to_ieee754(Float64, x, r) + promote_rule(::Type{BigFloat}, ::Type{<:Real}) = BigFloat promote_rule(::Type{BigInt}, ::Type{<:AbstractFloat}) = BigFloat promote_rule(::Type{BigFloat}, ::Type{<:AbstractFloat}) = BigFloat diff --git a/base/operators.jl b/base/operators.jl index 3f0f8bc49b164..9f116c07ad14c 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -1287,17 +1287,22 @@ used to implement specialized methods. """ in(x) = Fix2(in, x) -function in(x, itr) - anymissing = false - for y in itr - v = (y == x) - if ismissing(v) - anymissing = true - elseif v - return true +for ItrT = (Tuple,Any) + # define a generic method and a specialized version for `Tuple`, + # whose method bodies are identical, while giving better effects to the later + @eval function in(x, itr::$ItrT) + $(ItrT === Tuple ? :(@_terminates_locally_meta) : :nothing) + anymissing = false + for y in itr + v = (y == x) + if ismissing(v) + anymissing = true + elseif v + return true + end end + return anymissing ? missing : false end - return anymissing ? missing : false end const โˆˆ = in diff --git a/base/partr.jl b/base/partr.jl index 295be17e69f3f..8c95e3668ee74 100644 --- a/base/partr.jl +++ b/base/partr.jl @@ -88,6 +88,7 @@ end function multiq_insert(task::Task, priority::UInt16) tpid = ccall(:jl_get_task_threadpoolid, Int8, (Any,), task) + @assert tpid > -1 heap_p = multiq_size(tpid) tp = tpid + 1 @@ -124,6 +125,9 @@ function multiq_deletemin() tid = Threads.threadid() tp = ccall(:jl_threadpoolid, Int8, (Int16,), tid-1) + 1 + if tp == 0 # Foreign thread + return nothing + end tpheaps = heaps[tp] @label retry @@ -175,6 +179,9 @@ end function multiq_check_empty() tid = Threads.threadid() tp = ccall(:jl_threadpoolid, Int8, (Int16,), tid-1) + 1 + if tp == 0 # Foreign thread + return true + end for i = UInt32(1):length(heaps[tp]) if heaps[tp][i].ntasks != 0 return false diff --git a/base/rawbigints.jl b/base/rawbigints.jl new file mode 100644 index 0000000000000..5fe47891ffb07 --- /dev/null +++ b/base/rawbigints.jl @@ -0,0 +1,149 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" +Segment of raw words of bits interpreted as a big integer. Less +significant words come first. Each word is in machine-native bit-order. +""" +struct RawBigInt{T<:Unsigned} + d::Ptr{T} + word_count::Int + + function RawBigInt{T}(d::Ptr{T}, word_count::Int) where {T<:Unsigned} + new{T}(d, word_count) + end +end + +RawBigInt(d::Ptr{T}, word_count::Int) where {T<:Unsigned} = RawBigInt{T}(d, word_count) +elem_count(x::RawBigInt, ::Val{:words}) = x.word_count +elem_count(x::Unsigned, ::Val{:bits}) = sizeof(x) * 8 +word_length(::RawBigInt{T}) where {T} = elem_count(zero(T), Val(:bits)) +elem_count(x::RawBigInt{T}, ::Val{:bits}) where {T} = word_length(x) * elem_count(x, Val(:words)) +reversed_index(n::Int, i::Int) = n - i - 1 +reversed_index(x, i::Int, v::Val) = reversed_index(elem_count(x, v), i)::Int +split_bit_index(x::RawBigInt, i::Int) = divrem(i, word_length(x), RoundToZero) + +""" +`i` is the zero-based index of the wanted word in `x`, starting from +the less significant words. +""" +function get_elem(x::RawBigInt, i::Int, ::Val{:words}, ::Val{:ascending}) + unsafe_load(x.d, i + 1) +end + +function get_elem(x, i::Int, v::Val, ::Val{:descending}) + j = reversed_index(x, i, v) + get_elem(x, j, v, Val(:ascending)) +end + +word_is_nonzero(x::RawBigInt, i::Int, v::Val) = !iszero(get_elem(x, i, Val(:words), v)) + +word_is_nonzero(x::RawBigInt, v::Val) = let x = x + i -> word_is_nonzero(x, i, v) +end + +""" +Returns a `Bool` indicating whether the `len` least significant words +of `x` are nonzero. +""" +function tail_is_nonzero(x::RawBigInt, len::Int, ::Val{:words}) + any(word_is_nonzero(x, Val(:ascending)), 0:(len - 1)) +end + +""" +Returns a `Bool` indicating whether the `len` least significant bits of +the `i`-th (zero-based index) word of `x` are nonzero. +""" +function tail_is_nonzero(x::RawBigInt, len::Int, i::Int, ::Val{:word}) + !iszero(len) && + !iszero(get_elem(x, i, Val(:words), Val(:ascending)) << (word_length(x) - len)) +end + +""" +Returns a `Bool` indicating whether the `len` least significant bits of +`x` are nonzero. +""" +function tail_is_nonzero(x::RawBigInt, len::Int, ::Val{:bits}) + if 0 < len + word_count, bit_count_in_word = split_bit_index(x, len) + tail_is_nonzero(x, bit_count_in_word, word_count, Val(:word)) || + tail_is_nonzero(x, word_count, Val(:words)) + else + false + end::Bool +end + +""" +Returns a `Bool` that is the `i`-th (zero-based index) bit of `x`. +""" +function get_elem(x::Unsigned, i::Int, ::Val{:bits}, ::Val{:ascending}) + (x >>> i) % Bool +end + +""" +Returns a `Bool` that is the `i`-th (zero-based index) bit of `x`. +""" +function get_elem(x::RawBigInt, i::Int, ::Val{:bits}, v::Val{:ascending}) + vb = Val(:bits) + if 0 โ‰ค i < elem_count(x, vb) + word_index, bit_index_in_word = split_bit_index(x, i) + word = get_elem(x, word_index, Val(:words), v) + get_elem(word, bit_index_in_word, vb, v) + else + false + end::Bool +end + +""" +Returns an integer of type `R`, consisting of the `len` most +significant bits of `x`. +""" +function truncated(::Type{R}, x::RawBigInt, len::Int) where {R<:Integer} + ret = zero(R) + if 0 < len + word_count, bit_count_in_word = split_bit_index(x, len) + k = word_length(x) + vals = (Val(:words), Val(:descending)) + + for w โˆˆ 0:(word_count - 1) + ret <<= k + word = get_elem(x, w, vals...) + ret |= R(word) + end + + if !iszero(bit_count_in_word) + ret <<= bit_count_in_word + wrd = get_elem(x, word_count, vals...) + ret |= R(wrd >>> (k - bit_count_in_word)) + end + end + ret::R +end + +struct RawBigIntRoundingIncrementHelper{T<:Unsigned} + n::RawBigInt{T} + trunc_len::Int + + final_bit::Bool + round_bit::Bool + + function RawBigIntRoundingIncrementHelper{T}(n::RawBigInt{T}, len::Int) where {T<:Unsigned} + vals = (Val(:bits), Val(:descending)) + f = get_elem(n, len - 1, vals...) + r = get_elem(n, len , vals...) + new{T}(n, len, f, r) + end +end + +function RawBigIntRoundingIncrementHelper(n::RawBigInt{T}, len::Int) where {T<:Unsigned} + RawBigIntRoundingIncrementHelper{T}(n, len) +end + +(h::RawBigIntRoundingIncrementHelper)(::Rounding.FinalBit) = h.final_bit + +(h::RawBigIntRoundingIncrementHelper)(::Rounding.RoundBit) = h.round_bit + +function (h::RawBigIntRoundingIncrementHelper)(::Rounding.StickyBit) + v = Val(:bits) + n = h.n + tail_is_nonzero(n, elem_count(n, v) - h.trunc_len - 1, v) +end diff --git a/base/reduce.jl b/base/reduce.jl index 61a0f466b2902..d98b237e4997c 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -1214,17 +1214,22 @@ false """ any(f, itr) = _any(f, itr, :) -function _any(f, itr, ::Colon) - anymissing = false - for x in itr - v = f(x) - if ismissing(v) - anymissing = true - elseif v - return true +for ItrT = (Tuple,Any) + # define a generic method and a specialized version for `Tuple`, + # whose method bodies are identical, while giving better effects to the later + @eval function _any(f, itr::$ItrT, ::Colon) + $(ItrT === Tuple ? :(@_terminates_locally_meta) : :nothing) + anymissing = false + for x in itr + v = f(x) + if ismissing(v) + anymissing = true + else + v && return true + end end + return anymissing ? missing : false end - return anymissing ? missing : false end # Specialized versions of any(f, ::Tuple) @@ -1282,20 +1287,22 @@ true """ all(f, itr) = _all(f, itr, :) -function _all(f, itr, ::Colon) - anymissing = false - for x in itr - v = f(x) - if ismissing(v) - anymissing = true - # this syntax allows throwing a TypeError for non-Bool, for consistency with any - elseif v - continue - else - return false +for ItrT = (Tuple,Any) + # define a generic method and a specialized version for `Tuple`, + # whose method bodies are identical, while giving better effects to the later + @eval function _all(f, itr::$ItrT, ::Colon) + $(ItrT === Tuple ? :(@_terminates_locally_meta) : :nothing) + anymissing = false + for x in itr + v = f(x) + if ismissing(v) + anymissing = true + else + v || return false + end end + return anymissing ? missing : true end - return anymissing ? missing : true end # Specialized versions of all(f, ::Tuple), diff --git a/base/refpointer.jl b/base/refpointer.jl index ad74763ff8286..b89b474e8714c 100644 --- a/base/refpointer.jl +++ b/base/refpointer.jl @@ -42,9 +42,18 @@ A `C_NULL` instance of `Ptr` can be passed to a `ccall` `Ref` argument to initia # Examples ```jldoctest -julia> Ref(5) +julia> r = Ref(5) # Create a Ref with an initial value Base.RefValue{Int64}(5) +julia> r[] # Getting a value from a Ref +5 + +julia> r[] = 7 # Storing a new value in a Ref +7 + +julia> r # The Ref now contains 7 +Base.RefValue{Int64}(7) + julia> isa.(Ref([1,2,3]), [Array, Dict, Int]) # Treat reference values as scalar during broadcasting 3-element BitVector: 1 @@ -65,9 +74,6 @@ julia> Ref{Int64}()[]; # A reference to a bitstype refers to an undetermined val julia> isassigned(Ref{Int64}()) # A reference to a bitstype is always assigned true - -julia> Ref{Int64}(0)[] == 0 # Explicitly give a value for a bitstype reference -true ``` """ Ref diff --git a/base/rounding.jl b/base/rounding.jl index 77493b777876b..ddbc5fbc7809b 100644 --- a/base/rounding.jl +++ b/base/rounding.jl @@ -109,6 +109,65 @@ Rounds to nearest integer, with ties rounded toward positive infinity (Java/Java """ const RoundNearestTiesUp = RoundingMode{:NearestTiesUp}() +# Rounding mode predicates. TODO: better names + +# Overload these for other rounding modes +rounds_to_nearest(::RoundingMode) = false +rounds_to_nearest(::RoundingMode{:Nearest}) = true +rounds_to_nearest(::RoundingMode{:NearestTiesUp}) = true +rounds_to_nearest(::RoundingMode{:NearestTiesAway}) = true +rounds_away_from_zero(::RoundingMode{:Up}, sign_bit::Bool) = !sign_bit +rounds_away_from_zero(::RoundingMode{:Down}, sign_bit::Bool) = sign_bit +rounds_away_from_zero(::RoundingMode{:FromZero}, ::Bool) = true +rounds_away_from_zero(::RoundingMode{:ToZero}, ::Bool) = false +tie_breaker_is_to_even(::RoundingMode{:Nearest}) = true +tie_breaker_is_to_even(::RoundingMode{:NearestTiesUp}) = false +tie_breaker_is_to_even(::RoundingMode{:NearestTiesAway}) = false +tie_breaker_rounds_away_from_zero(::RoundingMode{:NearestTiesUp}, sign_bit::Bool) = !sign_bit +tie_breaker_rounds_away_from_zero(::RoundingMode{:NearestTiesAway}, ::Bool) = true + +rounds_to_nearest(t::Tuple{Any,Bool}) = rounds_to_nearest(first(t)) +rounds_away_from_zero(t::Tuple{Any,Bool}) = rounds_away_from_zero(t...) +tie_breaker_is_to_even(t::Tuple{Any,Bool}) = tie_breaker_is_to_even(first(t)) +tie_breaker_rounds_away_from_zero(t::Tuple{Any,Bool}) = tie_breaker_rounds_away_from_zero(t...) + +abstract type RoundingIncrementHelper end +struct FinalBit <: RoundingIncrementHelper end +struct RoundBit <: RoundingIncrementHelper end +struct StickyBit <: RoundingIncrementHelper end + +function correct_rounding_requires_increment(x, rounding_mode, sign_bit::Bool) + r = (rounding_mode, sign_bit) + f = let y = x + (z::RoundingIncrementHelper) -> y(z)::Bool + end + if rounds_to_nearest(r) + if f(RoundBit()) + if f(StickyBit()) + true + else + if tie_breaker_is_to_even(r) + f(FinalBit()) + else + tie_breaker_rounds_away_from_zero(r)::Bool + end + end + else + false + end + else + if rounds_away_from_zero(r) + if f(RoundBit()) + true + else + f(StickyBit()) + end + else + false + end + end::Bool +end + to_fenv(::RoundingMode{:Nearest}) = JL_FE_TONEAREST to_fenv(::RoundingMode{:ToZero}) = JL_FE_TOWARDZERO to_fenv(::RoundingMode{:Up}) = JL_FE_UPWARD diff --git a/base/show.jl b/base/show.jl index 3c42648e23457..f80b9643ed0a3 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1424,9 +1424,7 @@ show(io::IO, s::Symbol) = show_unquoted_quote_expr(io, s, 0, 0, 0) # eval(Meta.parse("Set{Int64}([2,3,1])")) # ==> An actual set # While this isnโ€™t true of ALL show methods, it is of all ASTs. -using Core.Compiler: TypedSlot, UnoptSlot - -const ExprNode = Union{Expr, QuoteNode, UnoptSlot, LineNumberNode, SSAValue, +const ExprNode = Union{Expr, QuoteNode, SlotNumber, LineNumberNode, SSAValue, GotoNode, GotoIfNot, GlobalRef, PhiNode, PhiCNode, UpsilonNode, ReturnNode} # Operators have precedence levels from 1-N, and show_unquoted defaults to a @@ -1777,8 +1775,7 @@ function show_globalref(io::IO, ex::GlobalRef; allow_macroname=false) nothing end -function show_unquoted(io::IO, ex::UnoptSlot, ::Int, ::Int) - typ = isa(ex, TypedSlot) ? ex.typ : Any +function show_unquoted(io::IO, ex::SlotNumber, ::Int, ::Int) slotid = ex.id slotnames = get(io, :SOURCE_SLOTNAMES, false) if (isa(slotnames, Vector{String}) && @@ -1787,9 +1784,6 @@ function show_unquoted(io::IO, ex::UnoptSlot, ::Int, ::Int) else print(io, "_", slotid) end - if typ !== Any && isa(ex, TypedSlot) - print(io, "::", typ) - end end function show_unquoted(io::IO, ex::QuoteNode, indent::Int, prec::Int) @@ -2769,7 +2763,7 @@ module IRShow const Compiler = Core.Compiler using Core.IR import ..Base - import .Compiler: IRCode, TypedSlot, CFG, scan_ssa_use!, + import .Compiler: IRCode, CFG, scan_ssa_use!, isexpr, compute_basic_blocks, block_for_inst, IncrementalCompact, Effects, ALWAYS_TRUE, ALWAYS_FALSE Base.getindex(r::Compiler.StmtRange, ind::Integer) = Compiler.getindex(r, ind) diff --git a/base/special/exp.jl b/base/special/exp.jl index 9cca6f568305f..8e940a4d85ad9 100644 --- a/base/special/exp.jl +++ b/base/special/exp.jl @@ -460,7 +460,7 @@ function expm1(x::Float32) end x = Float64(x) N_float = round(x*Ln2INV(Float64)) - N = unsafe_trunc(UInt64, N_float) + N = unsafe_trunc(Int64, N_float) r = muladd(N_float, Ln2(Float64), x) hi = evalpoly(r, (1.0, .5, 0.16666667546642386, 0.041666183019487026, 0.008332997481506921, 0.0013966479175977883, 0.0002004037059220124)) @@ -477,7 +477,7 @@ function expm1(x::Float16) return Float16(x*evalpoly(x, (1f0, .5f0, 0.16666628f0, 0.04166785f0, 0.008351848f0, 0.0013675707f0))) end N_float = round(x*Ln2INV(Float32)) - N = unsafe_trunc(UInt32, N_float) + N = unsafe_trunc(Int32, N_float) r = muladd(N_float, Ln2(Float32), x) hi = evalpoly(r, (1f0, .5f0, 0.16666667f0, 0.041665863f0, 0.008333111f0, 0.0013981499f0, 0.00019983904f0)) small_part = r*hi diff --git a/base/special/log.jl b/base/special/log.jl index 5d7f1c8118724..e2db746caecee 100644 --- a/base/special/log.jl +++ b/base/special/log.jl @@ -155,10 +155,10 @@ logbU(::Type{Float64},::Val{10}) = 0.4342944819032518 logbL(::Type{Float64},::Val{10}) = 1.098319650216765e-17 # Procedure 1 -# XXX we want to mark :consistent-cy here so that this function can be concrete-folded, +# XXX we want to mark :noub here so that this function can be concrete-folded, # because the effect analysis currently can't prove it in the presence of `@inbounds` or # `:boundscheck`, but still the access to `t_log_Float64` is really safe here -Base.@assume_effects :consistent @inline function log_proc1(y::Float64,mf::Float64,F::Float64,f::Float64,base=Val(:โ„ฏ)) +Base.@assume_effects :consistent :noub @inline function log_proc1(y::Float64,mf::Float64,F::Float64,f::Float64,base=Val(:โ„ฏ)) jp = unsafe_trunc(Int,128.0*F)-127 ## Steps 1 and 2 @@ -216,10 +216,10 @@ end end # Procedure 1 -# XXX we want to mark :consistent-cy here so that this function can be concrete-folded, +# XXX we want to mark :noub here so that this function can be concrete-folded, # because the effect analysis currently can't prove it in the presence of `@inbounds` or # `:boundscheck`, but still the access to `t_log_Float32` is really safe here -Base.@assume_effects :consistent @inline function log_proc1(y::Float32,mf::Float32,F::Float32,f::Float32,base=Val(:โ„ฏ)) +Base.@assume_effects :consistent :noub @inline function log_proc1(y::Float32,mf::Float32,F::Float32,f::Float32,base=Val(:โ„ฏ)) jp = unsafe_trunc(Int,128.0f0*F)-127 ## Steps 1 and 2 diff --git a/base/special/rem_pio2.jl b/base/special/rem_pio2.jl index de5c4151df2d0..3086f3ebc02c9 100644 --- a/base/special/rem_pio2.jl +++ b/base/special/rem_pio2.jl @@ -126,10 +126,10 @@ function fromfraction(f::Int128) return (z1,z2) end -# XXX we want to mark :consistent-cy here so that this function can be concrete-folded, +# XXX we want to mark :noub here so that this function can be concrete-folded, # because the effect analysis currently can't prove it in the presence of `@inbounds` or # `:boundscheck`, but still the accesses to `INV_2PI` are really safe here -Base.@assume_effects :consistent function paynehanek(x::Float64) +Base.@assume_effects :consistent :noub function paynehanek(x::Float64) # 1. Convert to form # # x = X * 2^k, diff --git a/base/task.jl b/base/task.jl index 7a09935e93635..09b40f19f5913 100644 --- a/base/task.jl +++ b/base/task.jl @@ -798,7 +798,7 @@ function enq_work(t::Task) else @label not_sticky tp = Threads.threadpool(t) - if Threads.threadpoolsize(tp) == 1 + if tp === :foreign || Threads.threadpoolsize(tp) == 1 # There's only one thread in the task's assigned thread pool; # use its work queue. tid = (tp === :interactive) ? 1 : Threads.threadpoolsize(:interactive)+1 diff --git a/base/terminfo.jl b/base/terminfo.jl new file mode 100644 index 0000000000000..c2844d189a3e5 --- /dev/null +++ b/base/terminfo.jl @@ -0,0 +1,303 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +include("terminfo_data.jl") + +""" + struct TermInfoRaw + +A structured representation of a terminfo file, without any knowledge of +particular capabilities, solely based on `term(5)`. + +!!! warning + This is not part of the public API, and thus subject to change without notice. + +# Fields + +- `names::Vector{String}`: The names this terminal is known by. +- `flags::BitVector`: A list of 0โ€“$(length(TERM_FLAGS)) flag values. +- `numbers::Union{Vector{UInt16}, Vector{UInt32}}`: A list of 0โ€“$(length(TERM_NUMBERS)) + number values. A value of `typemax(eltype(numbers))` is used to skip over + unspecified capabilities while ensuring value indices are correct. +- `strings::Vector{Union{String, Nothing}}`: A list of 0โ€“$(length(TERM_STRINGS)) + string values. A value of `nothing` is used to skip over unspecified + capabilities while ensuring value indices are correct. +- `extended::Union{Nothing, Dict{Symbol, Union{Bool, Int, String}}}`: Should an + extended info section exist, this gives the entire extended info as a + dictionary. Otherwise `nothing`. + +See also: `TermInfo` and `TermCapability`. +""" +struct TermInfoRaw + names::Vector{String} + flags::BitVector + numbers::Union{Vector{UInt16}, Vector{UInt32}} + strings::Vector{Union{String, Nothing}} + extended::Union{Nothing, Dict{Symbol, Union{Bool, Int, String}}} +end + +""" + struct TermInfo + +A parsed terminfo paired with capability information. + +!!! warning + This is not part of the public API, and thus subject to change without notice. + +# Fields + +- `names::Vector{String}`: The names this terminal is known by. +- `flags::Int`: The number of flags specified. +- `numbers::BitVector`: A mask indicating which of `TERM_NUMBERS` have been + specified. +- `strings::BitVector`: A mask indicating which of `TERM_STRINGS` have been + specified. +- `extensions::Vector{Symbol}`: A list of extended capability variable names. +- `capabilities::Dict{Symbol, Union{Bool, Int, String}}`: The capability values + themselves. + +See also: `TermInfoRaw` and `TermCapability`. +""" +struct TermInfo + names::Vector{String} + flags::Int + numbers::BitVector + strings::BitVector + extensions::Vector{Symbol} + capabilities::Dict{Symbol, Union{Bool, Int, String}} +end + +TermInfo() = TermInfo([], 0, [], [], [], Dict()) + +function read(data::IO, ::Type{TermInfoRaw}) + # Parse according to `term(5)` + # Header + magic = read(data, UInt16) |> ltoh + NumInt = if magic == 0o0432 + UInt16 + elseif magic == 0o01036 + UInt32 + else + throw(ArgumentError("Terminfo data did not start with the magic number 0o0432 or 0o01036")) + end + name_bytes = read(data, UInt16) |> ltoh + flag_bytes = read(data, UInt16) |> ltoh + numbers_count = read(data, UInt16) |> ltoh + string_count = read(data, UInt16) |> ltoh + table_bytes = read(data, UInt16) |> ltoh + # Terminal Names + term_names = split(String(read(data, name_bytes - 1)), '|') .|> String + 0x00 == read(data, UInt8) || + throw(ArgumentError("Terminfo data did not contain a null byte after the terminal names section")) + # Boolean Flags + flags = read(data, flag_bytes) .== 0x01 + if position(data) % 2 != 0 + 0x00 == read(data, UInt8) || + throw(ArgumentError("Terminfo did not contain a null byte after the flag section, expected to position the start of the numbers section on an even byte")) + end + # Numbers, Strings, Table + numbers = reinterpret(NumInt, read(data, numbers_count * sizeof(NumInt))) .|> ltoh + string_indices = reinterpret(UInt16, read(data, string_count * sizeof(UInt16))) .|> ltoh + strings_table = read(data, table_bytes) + strings = map(string_indices) do idx + if idx โˆ‰ (0xffff, 0xfffe) + len = findfirst(==(0x00), view(strings_table, 1+idx:length(strings_table))) + !isnothing(len) || + throw(ArgumentError("Terminfo string table entry does not terminate with a null byte")) + String(strings_table[1+idx:idx+len-1]) + end + end + TermInfoRaw(term_names, flags, numbers, strings, + if !eof(data) extendedterminfo(data; NumInt) end) +end + +""" + extendedterminfo(data::IO; NumInt::Union{Type{UInt16}, Type{UInt32}}) + +Read an extended terminfo section from `data`, with `NumInt` as the numbers type. + +This will accept any terminfo content that conforms with `term(5)`. + +See also: `read(::IO, ::Type{TermInfoRaw})` +""" +function extendedterminfo(data::IO; NumInt::Union{Type{UInt16}, Type{UInt32}}) + # Extended info + if position(data) % 2 != 0 + 0x00 == read(data, UInt8) || + throw(ArgumentError("Terminfo did not contain a null byte before the extended section, expected to position the start on an even byte")) + end + # Extended header + flag_bytes = read(data, UInt16) |> ltoh + numbers_count = read(data, UInt16) |> ltoh + string_count = read(data, UInt16) |> ltoh + table_count = read(data, UInt16) |> ltoh + table_bytes = read(data, UInt16) |> ltoh + # Extended flags/numbers/strings + flags = read(data, flag_bytes) .== 0x01 + if flag_bytes % 2 != 0 + 0x00 == read(data, UInt8) || + throw(ArgumentError("Terminfo did not contain a null byte after the extended flag section, expected to position the start of the numbers section on an even byte")) + end + numbers = reinterpret(NumInt, read(data, numbers_count * sizeof(NumInt))) .|> ltoh + table_indices = reinterpret(UInt16, read(data, table_count * sizeof(UInt16))) .|> ltoh + table_strings = [String(readuntil(data, 0x00)) for _ in 1:length(table_indices)] + strings = table_strings[1:string_count] + labels = Symbol.(table_strings[string_count+1:end]) + Dict{Symbol, Union{Bool, Int, String}}( + labels .=> vcat(flags, numbers, strings)) +end + +""" + TermInfo(raw::TermInfoRaw) + +Construct a `TermInfo` from `raw`, using known terminal capabilities (as of +NCurses 6.3, see `TERM_FLAGS`, `TERM_NUMBERS`, and `TERM_STRINGS`). +""" +function TermInfo(raw::TermInfoRaw) + capabilities = Dict{Symbol, Union{Bool, Int, String}}() + sizehint!(capabilities, 2 * (length(raw.flags) + length(raw.numbers) + length(raw.strings))) + for (flag, value) in zip(TERM_FLAGS, raw.flags) + capabilities[flag.short] = value + capabilities[flag.long] = value + end + for (num, value) in zip(TERM_NUMBERS, raw.numbers) + if value != typemax(eltype(raw.numbers)) + capabilities[num.short] = Int(value) + capabilities[num.long] = Int(value) + end + end + for (str, value) in zip(TERM_STRINGS, raw.strings) + if !isnothing(value) + capabilities[str.short] = value + capabilities[str.long] = value + end + end + extensions = if !isnothing(raw.extended) + capabilities = merge(capabilities, raw.extended) + keys(raw.extended) |> collect + else + Symbol[] + end + TermInfo(raw.names, length(raw.flags), + raw.numbers .!= typemax(eltype(raw.numbers)), + map(!isnothing, raw.strings), + extensions, capabilities) +end + +getindex(ti::TermInfo, key::Symbol) = ti.capabilities[key] +get(ti::TermInfo, key::Symbol, default::D) where D<:Union{Bool, Int, String} = + get(ti.capabilities, key, default)::D +get(ti::TermInfo, key::Symbol, default) = get(ti.capabilities, key, default) +keys(ti::TermInfo) = keys(ti.capabilities) +haskey(ti::TermInfo, key::Symbol) = haskey(ti.capabilities, key) + +function show(io::IO, ::MIME"text/plain", ti::TermInfo) + print(io, "TermInfo(", ti.names, "; ", ti.flags, " flags, ", + sum(ti.numbers), " numbers, ", sum(ti.strings), " strings") + !isempty(ti.extensions) > 0 && + print(io, ", ", length(ti.extensions), " extended capabilities") + print(io, ')') +end + +""" + find_terminfo_file(term::String) + +Locate the terminfo file for `term`, return `nothing` if none could be found. + +The lookup policy is described in `terminfo(5)` "Fetching Compiled +Descriptions". +""" +function find_terminfo_file(term::String) + isempty(term) && return + chr, chrcode = string(first(term)), string(Int(first(term)), base=16) + terminfo_dirs = if haskey(ENV, "TERMINFO") + [ENV["TERMINFO"]] + elseif isdir(joinpath(homedir(), ".terminfo")) + [joinpath(homedir(), ".terminfo")] + elseif haskey(ENV, "TERMINFO_DIRS") + split(ENV["TERMINFO_DIRS"], ':') + elseif Sys.isunix() + ["/usr/share/terminfo"] + else + String[] + end + for dir in terminfo_dirs + if isfile(joinpath(dir, chr, term)) + return joinpath(dir, chr, term) + elseif isfile(joinpath(dir, chrcode, term)) + return joinpath(dir, chrcode, term) + end + end +end + +""" + load_terminfo(term::String) + +Load the `TermInfo` for `term`, falling back on a blank `TermInfo`. +""" +function load_terminfo(term::String) + file = find_terminfo_file(term) + isnothing(file) && return TermInfo() + try + TermInfo(read(file, TermInfoRaw)) + catch err + if err isa ArgumentError || err isa IOError + TermInfo() + else + rethrow() + end + end +end + +""" +The terminfo of the current terminal. +""" +current_terminfo::TermInfo = TermInfo() + +# Legacy/TTY methods and the `:color` parameter + +if Sys.iswindows() + ttyhascolor(term_type = nothing) = true +else + function ttyhascolor(term_type = get(ENV, "TERM", "")) + startswith(term_type, "xterm") || + haskey(current_terminfo, :setaf) + end +end + +""" + ttyhastruecolor() + +Return a boolean signifying whether the current terminal supports 24-bit colors. + +This uses the `COLORTERM` environment variable if possible, returning true if it +is set to either `"truecolor"` or `"24bit"`. + +As a fallback, first on unix systems the `colors` terminal capability is checked +โ€” should more than 256 colors be reported, this is taken to signify 24-bit +support. +""" +function ttyhastruecolor() + get(ENV, "COLORTERM", "") โˆˆ ("truecolor", "24bit") || + @static if Sys.isunix() + get(current_terminfo, :colors, 0) > 256 + else + false + end +end + +function get_have_color() + global have_color + have_color === nothing && (have_color = ttyhascolor()) + return have_color::Bool +end + +function get_have_truecolor() + global have_truecolor + have_truecolor === nothing && (have_truecolor = ttyhastruecolor()) + return have_truecolor::Bool +end + +in(key_value::Pair{Symbol,Bool}, ::TTY) = key_value.first === :color && key_value.second === get_have_color() +haskey(::TTY, key::Symbol) = key === :color +getindex(::TTY, key::Symbol) = key === :color ? get_have_color() : throw(KeyError(key)) +get(::TTY, key::Symbol, default) = key === :color ? get_have_color() : default diff --git a/base/terminfo_data.jl b/base/terminfo_data.jl new file mode 100644 index 0000000000000..38c058f414f07 --- /dev/null +++ b/base/terminfo_data.jl @@ -0,0 +1,540 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + struct TermCapability + +Specification of a single terminal capability. + +!!! warning + This is not part of the public API, and thus subject to change without notice. + +# Fields + +- `short::Symbol`: The *Cap-name* of the capability +- `long::Symbol`: The name of the terminfo capability variable +- `description::String`: A description of the purpose of the capability + +See also: `TermInfo`, `TERM_FLAGS`, `TERM_NUMBERS`, and `TERM_STRINGS`. +""" +struct TermCapability + short::Symbol + long::Symbol + description::String +end + +# Terminfo Capabilities as of NCurses 6.3 + +""" +Ordered list of known terminal capability flag fields, as of NCurses 6.3. +""" +const TERM_FLAGS = [ + TermCapability(:bw, :auto_left_margin, "cub1 wraps from column 0 to last column"), + TermCapability(:am, :auto_right_margin, "terminal has automatic margins"), + TermCapability(:xsb, :no_esc_ctlc, "beehive (f1=escape, f2=ctrl C)"), + TermCapability(:xhp, :ceol_standout_glitch, "standout not erased by overwriting (hp)"), + TermCapability(:xenl, :eat_newline_glitch, "newline ignored after 80 cols (concept)"), + TermCapability(:eo, :erase_overstrike, "can erase overstrikes with a blank"), + TermCapability(:gn, :generic_type, "generic line type"), + TermCapability(:hc, :hard_copy, "hardcopy terminal"), + TermCapability(:km, :has_meta_key, "Has a meta key (i.e., sets 8th-bit)"), + TermCapability(:hs, :has_status_line, "has extra status line"), + TermCapability(:in, :insert_null_glitch, "insert mode distinguishes nulls"), + TermCapability(:db, :memory_below, "display may be retained below the screen"), + TermCapability(:da, :memory_above, "display may be retained above the screen"), + TermCapability(:mir, :move_insert_mode, "safe to move while in insert mode"), + TermCapability(:msgr, :move_standout_mode, "safe to move while in standout mode"), + TermCapability(:os, :over_strike, "terminal can overstrike"), + TermCapability(:eslok, :status_line_esc_ok, "escape can be used on the status line"), + TermCapability(:xt, :dest_tabs_magic_smso, "tabs destructive, magic so char (t1061)"), + TermCapability(:hz, :tilde_glitch, "cannot print ~'s (Hazeltine)"), + TermCapability(:ul, :transparent_underline, "underline character overstrikes"), + TermCapability(:xon, :xon_xoff, "terminal uses xon/xoff handshaking"), + TermCapability(:nxon, :needs_xon_xoff, "padding will not work, xon/xoff required"), + TermCapability(:mc5i, :prtr_silent, "printer will not echo on screen"), + TermCapability(:chts, :hard_cursor, "cursor is hard to see"), + TermCapability(:nrrmc, :non_rev_rmcup, "smcup does not reverse rmcup"), + TermCapability(:npc, :no_pad_char, "pad character does not exist"), + TermCapability(:ndscr, :non_dest_scroll_region, "scrolling region is non-destructive"), + TermCapability(:ccc, :can_change, "terminal can re-define existing colors"), + TermCapability(:bce, :back_color_erase, "screen erased with background color"), + TermCapability(:hls, :hue_lightness_saturation, "terminal uses only HLS color notation (Tektronix)"), + TermCapability(:xhpa, :col_addr_glitch, "only positive motion for hpa/mhpa caps"), + TermCapability(:crxm, :cr_cancels_micro_mode, "using cr turns off micro mode"), + TermCapability(:daisy, :has_print_wheel, "printer needs operator to change character set"), + TermCapability(:xvpa, :row_addr_glitch, "only positive motion for vpa/mvpa caps"), + TermCapability(:sam, :semi_auto_right_margin, "printing in last column causes cr"), + TermCapability(:cpix, :cpi_changes_res, "changing character pitch changes resolution"), + TermCapability(:lpix, :lpi_changes_res, "changing line pitch changes resolution"), + TermCapability(:OTbs, :backspaces_with_bs, "uses ^H to move left"), + TermCapability(:OTns, :crt_no_scrolling, "crt cannot scroll"), + TermCapability(:OTnc, :no_correctly_working_cr, "no way to go to start of line"), + TermCapability(:OTMT, :gnu_has_meta_key, "has meta key"), + TermCapability(:OTNL, :linefeed_is_newline, "move down with \n"), + TermCapability(:OTpt, :has_hardware_tabs, "has 8-char tabs invoked with ^I"), + TermCapability(:OTxr, :return_does_clr_eol, "return clears the line"), +] + +""" +Ordered list of known terminal capability number fields, as of NCurses 6.3. +""" +const TERM_NUMBERS = [ + TermCapability(:cols, :columns, "number of columns in a line"), + TermCapability(:it, :init_tabs, "tabs initially every # spaces"), + TermCapability(:lines, :lines, "number of lines on screen or page"), + TermCapability(:lm, :lines_of_memory, "lines of memory if > line. 0 means varies"), + TermCapability(:xmc, :magic_cookie_glitch, "number of blank characters left by smso or rmso"), + TermCapability(:pb, :padding_baud_rate, "lowest baud rate where padding needed"), + TermCapability(:vt, :virtual_terminal, "virtual terminal number (CB/unix)"), + TermCapability(:wsl, :width_status_line, "number of columns in status line"), + TermCapability(:nlab, :num_labels, "number of labels on screen"), + TermCapability(:lh, :label_height, "rows in each label"), + TermCapability(:lw, :label_width, "columns in each label"), + TermCapability(:ma, :max_attributes, "maximum combined attributes terminal can handle"), + TermCapability(:wnum, :maximum_windows, "maximum number of definable windows"), + TermCapability(:colors, :max_colors, "maximum number of colors on screen"), + TermCapability(:pairs, :max_pairs, "maximum number of color-pairs on the screen"), + TermCapability(:ncv, :no_color_video, "video attributes that cannot be used with colors"), + TermCapability(:bufsz, :buffer_capacity, "numbers of bytes buffered before printing"), + TermCapability(:spinv, :dot_vert_spacing, "spacing of pins vertically in pins per inch"), + TermCapability(:spinh, :dot_horz_spacing, "spacing of dots horizontally in dots per inch"), + TermCapability(:maddr, :max_micro_address, "maximum value in micro_..._address"), + TermCapability(:mjump, :max_micro_jump, "maximum value in parm_..._micro"), + TermCapability(:mcs, :micro_col_size, "character step size when in micro mode"), + TermCapability(:mls, :micro_line_size, "line step size when in micro mode"), + TermCapability(:npins, :number_of_pins, "numbers of pins in print-head"), + TermCapability(:orc, :output_res_char, "horizontal resolution in units per line"), + TermCapability(:orl, :output_res_line, "vertical resolution in units per line"), + TermCapability(:orhi, :output_res_horz_inch, "horizontal resolution in units per inch"), + TermCapability(:orvi, :output_res_vert_inch, "vertical resolution in units per inch"), + TermCapability(:cps, :print_rate, "print rate in characters per second"), + TermCapability(:widcs, :wide_char_size, "character step size when in double wide mode"), + TermCapability(:btns, :buttons, "number of buttons on mouse"), + TermCapability(:bitwin, :bit_image_entwining, "number of passes for each bit-image row"), + TermCapability(:bitype, :bit_image_type, "type of bit-image device"), + TermCapability(:OTug, :magic_cookie_glitch_ul, "number of blanks left by ul"), + TermCapability(:OTdC, :carriage_return_delay, "pad needed for CR"), + TermCapability(:OTdN, :new_line_delay, "pad needed for LF"), + TermCapability(:OTdB, :backspace_delay, "padding required for ^H"), + TermCapability(:OTdT, :horizontal_tab_delay, "padding required for ^I"), + TermCapability(:OTkn, :number_of_function_keys, "count of function keys"), +] + +""" +Ordered list of known terminal capability string fields, as of NCurses 6.3. +""" +const TERM_STRINGS = [ + TermCapability(:cbt, :back_tab, "back tab (P)"), + TermCapability(:bel, :bell, "audible signal (bell) (P)"), + TermCapability(:cr, :carriage_return, "carriage return (P*) (P*)"), + TermCapability(:csr, :change_scroll_region, "change region to line #1 to line #2 (P)"), + TermCapability(:tbc, :clear_all_tabs, "clear all tab stops (P)"), + TermCapability(:clear, :clear_screen, "clear screen and home cursor (P*)"), + TermCapability(:el, :clr_eol, "clear to end of line (P)"), + TermCapability(:ed, :clr_eos, "clear to end of screen (P*)"), + TermCapability(:hpa, :column_address, "horizontal position #1, absolute (P)"), + TermCapability(:cmdch, :command_character, "terminal settable cmd character in prototype !?"), + TermCapability(:cup, :cursor_address, "move to row #1 columns #2"), + TermCapability(:cud1, :cursor_down, "down one line"), + TermCapability(:home, :cursor_home, "home cursor (if no cup)"), + TermCapability(:civis, :cursor_invisible, "make cursor invisible"), + TermCapability(:cub1, :cursor_left, "move left one space"), + TermCapability(:mrcup, :cursor_mem_address, "memory relative cursor addressing, move to row #1 columns #2"), + TermCapability(:cnorm, :cursor_normal, "make cursor appear normal (undo civis/cvvis)"), + TermCapability(:cuf1, :cursor_right, "non-destructive space (move right one space)"), + TermCapability(:ll, :cursor_to_ll, "last line, first column (if no cup)"), + TermCapability(:cuu1, :cursor_up, "up one line"), + TermCapability(:cvvis, :cursor_visible, "make cursor very visible"), + TermCapability(:dch1, :delete_character, "delete character (P*)"), + TermCapability(:dl1, :delete_line, "delete line (P*)"), + TermCapability(:dsl, :dis_status_line, "disable status line"), + TermCapability(:hd, :down_half_line, "half a line down"), + TermCapability(:smacs, :enter_alt_charset_mode, "start alternate character set (P)"), + TermCapability(:blink, :enter_blink_mode, "turn on blinking"), + TermCapability(:bold, :enter_bold_mode, "turn on bold (extra bright) mode"), + TermCapability(:smcup, :enter_ca_mode, "string to start programs using cup"), + TermCapability(:smdc, :enter_delete_mode, "enter delete mode"), + TermCapability(:dim, :enter_dim_mode, "turn on half-bright mode"), + TermCapability(:smir, :enter_insert_mode, "enter insert mode"), + TermCapability(:invis, :enter_secure_mode, "turn on blank mode (characters invisible)"), + TermCapability(:prot, :enter_protected_mode, "turn on protected mode"), + TermCapability(:rev, :enter_reverse_mode, "turn on reverse video mode"), + TermCapability(:smso, :enter_standout_mode, "begin standout mode"), + TermCapability(:smul, :enter_underline_mode, "begin underline mode"), + TermCapability(:ech, :erase_chars, "erase #1 characters (P)"), + TermCapability(:rmacs, :exit_alt_charset_mode, "end alternate character set (P)"), + TermCapability(:sgr0, :exit_attribute_mode, "turn off all attributes"), + TermCapability(:rmcup, :exit_ca_mode, "strings to end programs using cup"), + TermCapability(:rmdc, :exit_delete_mode, "end delete mode"), + TermCapability(:rmir, :exit_insert_mode, "exit insert mode"), + TermCapability(:rmso, :exit_standout_mode, "exit standout mode"), + TermCapability(:rmul, :exit_underline_mode, "exit underline mode"), + TermCapability(:flash, :flash_screen, "visible bell (may not move cursor)"), + TermCapability(:ff, :form_feed, "hardcopy terminal page eject (P*)"), + TermCapability(:fsl, :from_status_line, "return from status line"), + TermCapability(:is1, :init_1string, "initialization string"), + TermCapability(:is2, :init_2string, "initialization string"), + TermCapability(:is3, :init_3string, "initialization string"), + TermCapability(:if, :init_file, "name of initialization file"), + TermCapability(:ich1, :insert_character, "insert character (P)"), + TermCapability(:il1, :insert_line, "insert line (P*)"), + TermCapability(:ip, :insert_padding, "insert padding after inserted character"), + TermCapability(:kbs, :key_backspace, "backspace key"), + TermCapability(:ktbc, :key_catab, "clear-all-tabs key"), + TermCapability(:kclr, :key_clear, "clear-screen or erase key"), + TermCapability(:kctab, :key_ctab, "clear-tab key"), + TermCapability(:kdch1, :key_dc, "delete-character key"), + TermCapability(:kdl1, :key_dl, "delete-line key"), + TermCapability(:kcud1, :key_down, "down-arrow key"), + TermCapability(:krmir, :key_eic, "sent by rmir or smir in insert mode"), + TermCapability(:kel, :key_eol, "clear-to-end-of-line key"), + TermCapability(:ked, :key_eos, "clear-to-end-of-screen key"), + TermCapability(:kf0, :key_f0, "F0 function key"), + TermCapability(:kf1, :key_f1, "F1 function key"), + TermCapability(:kf10, :key_f10, "F10 function key"), + TermCapability(:kf2, :key_f2, "F2 function key"), + TermCapability(:kf3, :key_f3, "F3 function key"), + TermCapability(:kf4, :key_f4, "F4 function key"), + TermCapability(:kf5, :key_f5, "F5 function key"), + TermCapability(:kf6, :key_f6, "F6 function key"), + TermCapability(:kf7, :key_f7, "F7 function key"), + TermCapability(:kf8, :key_f8, "F8 function key"), + TermCapability(:kf9, :key_f9, "F9 function key"), + TermCapability(:khome, :key_home, "home key"), + TermCapability(:kich1, :key_ic, "insert-character key"), + TermCapability(:kil1, :key_il, "insert-line key"), + TermCapability(:kcub1, :key_left, "left-arrow key"), + TermCapability(:kll, :key_ll, "lower-left key (home down)"), + TermCapability(:knp, :key_npage, "next-page key"), + TermCapability(:kpp, :key_ppage, "previous-page key"), + TermCapability(:kcuf1, :key_right, "right-arrow key"), + TermCapability(:kind, :key_sf, "scroll-forward key"), + TermCapability(:kri, :key_sr, "scroll-backward key"), + TermCapability(:khts, :key_stab, "set-tab key"), + TermCapability(:kcuu1, :key_up, "up-arrow key"), + TermCapability(:rmkx, :keypad_local, "leave 'keyboard_transmit' mode"), + TermCapability(:smkx, :keypad_xmit, "enter 'keyboard_transmit' mode"), + TermCapability(:lf0, :lab_f0, "label on function key f0 if not f0"), + TermCapability(:lf1, :lab_f1, "label on function key f1 if not f1"), + TermCapability(:lf10, :lab_f10, "label on function key f10 if not f10"), + TermCapability(:lf2, :lab_f2, "label on function key f2 if not f2"), + TermCapability(:lf3, :lab_f3, "label on function key f3 if not f3"), + TermCapability(:lf4, :lab_f4, "label on function key f4 if not f4"), + TermCapability(:lf5, :lab_f5, "label on function key f5 if not f5"), + TermCapability(:lf6, :lab_f6, "label on function key f6 if not f6"), + TermCapability(:lf7, :lab_f7, "label on function key f7 if not f7"), + TermCapability(:lf8, :lab_f8, "label on function key f8 if not f8"), + TermCapability(:lf9, :lab_f9, "label on function key f9 if not f9"), + TermCapability(:rmm, :meta_off, "turn off meta mode"), + TermCapability(:smm, :meta_on, "turn on meta mode (8th-bit on)"), + TermCapability(:nel, :newline, "newline (behave like cr followed by lf)"), + TermCapability(:pad, :pad_char, "padding char (instead of null)"), + TermCapability(:dch, :parm_dch, "delete #1 characters (P*)"), + TermCapability(:dl, :parm_delete_line, "delete #1 lines (P*)"), + TermCapability(:cud, :parm_down_cursor, "down #1 lines (P*)"), + TermCapability(:ich, :parm_ich, "insert #1 characters (P*)"), + TermCapability(:indn, :parm_index, "scroll forward #1 lines (P)"), + TermCapability(:il, :parm_insert_line, "insert #1 lines (P*)"), + TermCapability(:cub, :parm_left_cursor, "move #1 characters to the left (P)"), + TermCapability(:cuf, :parm_right_cursor, "move #1 characters to the right (P*)"), + TermCapability(:rin, :parm_rindex, "scroll back #1 lines (P)"), + TermCapability(:cuu, :parm_up_cursor, "up #1 lines (P*)"), + TermCapability(:pfkey, :pkey_key, "program function key #1 to type string #2"), + TermCapability(:pfloc, :pkey_local, "program function key #1 to execute string #2"), + TermCapability(:pfx, :pkey_xmit, "program function key #1 to transmit string #2"), + TermCapability(:mc0, :print_screen, "print contents of screen"), + TermCapability(:mc4, :prtr_off, "turn off printer"), + TermCapability(:mc5, :prtr_on, "turn on printer"), + TermCapability(:rep, :repeat_char, "repeat char #1 #2 times (P*)"), + TermCapability(:rs1, :reset_1string, "reset string"), + TermCapability(:rs2, :reset_2string, "reset string"), + TermCapability(:rs3, :reset_3string, "reset string"), + TermCapability(:rf, :reset_file, "name of reset file"), + TermCapability(:rc, :restore_cursor, "restore cursor to position of last save_cursor"), + TermCapability(:vpa, :row_address, "vertical position #1 absolute (P)"), + TermCapability(:sc, :save_cursor, "save current cursor position (P)"), + TermCapability(:ind, :scroll_forward, "scroll text up (P)"), + TermCapability(:ri, :scroll_reverse, "scroll text down (P)"), + TermCapability(:sgr, :set_attributes, "define video attributes #1-#9 (PG9)"), + TermCapability(:hts, :set_tab, "set a tab in every row, current columns"), + TermCapability(:wind, :set_window, "current window is lines #1-#2 cols #3-#4"), + TermCapability(:ht, :tab, "tab to next 8-space hardware tab stop"), + TermCapability(:tsl, :to_status_line, "move to status line, column #1"), + TermCapability(:uc, :underline_char, "underline char and move past it"), + TermCapability(:hu, :up_half_line, "half a line up"), + TermCapability(:iprog, :init_prog, "path name of program for initialization"), + TermCapability(:ka1, :key_a1, "upper left of keypad"), + TermCapability(:ka3, :key_a3, "upper right of keypad"), + TermCapability(:kb2, :key_b2, "center of keypad"), + TermCapability(:kc1, :key_c1, "lower left of keypad"), + TermCapability(:kc3, :key_c3, "lower right of keypad"), + TermCapability(:mc5p, :prtr_non, "turn on printer for #1 bytes"), + TermCapability(:rmp, :char_padding, "like ip but when in insert mode"), + TermCapability(:acsc, :acs_chars, "graphics charset pairs, based on vt100"), + TermCapability(:pln, :plab_norm, "program label #1 to show string #2"), + TermCapability(:kcbt, :key_btab, "back-tab key"), + TermCapability(:smxon, :enter_xon_mode, "turn on xon/xoff handshaking"), + TermCapability(:rmxon, :exit_xon_mode, "turn off xon/xoff handshaking"), + TermCapability(:smam, :enter_am_mode, "turn on automatic margins"), + TermCapability(:rmam, :exit_am_mode, "turn off automatic margins"), + TermCapability(:xonc, :xon_character, "XON character"), + TermCapability(:xoffc, :xoff_character, "XOFF character"), + TermCapability(:enacs, :ena_acs, "enable alternate char set"), + TermCapability(:smln, :label_on, "turn on soft labels"), + TermCapability(:rmln, :label_off, "turn off soft labels"), + TermCapability(:kbeg, :key_beg, "begin key"), + TermCapability(:kcan, :key_cancel, "cancel key"), + TermCapability(:kclo, :key_close, "close key"), + TermCapability(:kcmd, :key_command, "command key"), + TermCapability(:kcpy, :key_copy, "copy key"), + TermCapability(:kcrt, :key_create, "create key"), + TermCapability(:kend, :key_end, "end key"), + TermCapability(:kent, :key_enter, "enter/send key"), + TermCapability(:kext, :key_exit, "exit key"), + TermCapability(:kfnd, :key_find, "find key"), + TermCapability(:khlp, :key_help, "help key"), + TermCapability(:kmrk, :key_mark, "mark key"), + TermCapability(:kmsg, :key_message, "message key"), + TermCapability(:kmov, :key_move, "move key"), + TermCapability(:knxt, :key_next, "next key"), + TermCapability(:kopn, :key_open, "open key"), + TermCapability(:kopt, :key_options, "options key"), + TermCapability(:kprv, :key_previous, "previous key"), + TermCapability(:kprt, :key_print, "print key"), + TermCapability(:krdo, :key_redo, "redo key"), + TermCapability(:kref, :key_reference, "reference key"), + TermCapability(:krfr, :key_refresh, "refresh key"), + TermCapability(:krpl, :key_replace, "replace key"), + TermCapability(:krst, :key_restart, "restart key"), + TermCapability(:kres, :key_resume, "resume key"), + TermCapability(:ksav, :key_save, "save key"), + TermCapability(:kspd, :key_suspend, "suspend key"), + TermCapability(:kund, :key_undo, "undo key"), + TermCapability(:kBEG, :key_sbeg, "shifted begin key"), + TermCapability(:kCAN, :key_scancel, "shifted cancel key"), + TermCapability(:kCMD, :key_scommand, "shifted command key"), + TermCapability(:kCPY, :key_scopy, "shifted copy key"), + TermCapability(:kCRT, :key_screate, "shifted create key"), + TermCapability(:kDC, :key_sdc, "shifted delete-character key"), + TermCapability(:kDL, :key_sdl, "shifted delete-line key"), + TermCapability(:kslt, :key_select, "select key"), + TermCapability(:kEND, :key_send, "shifted end key"), + TermCapability(:kEOL, :key_seol, "shifted clear-to-end-of-line key"), + TermCapability(:kEXT, :key_sexit, "shifted exit key"), + TermCapability(:kFND, :key_sfind, "shifted find key"), + TermCapability(:kHLP, :key_shelp, "shifted help key"), + TermCapability(:kHOM, :key_shome, "shifted home key"), + TermCapability(:kIC, :key_sic, "shifted insert-character key"), + TermCapability(:kLFT, :key_sleft, "shifted left-arrow key"), + TermCapability(:kMSG, :key_smessage, "shifted message key"), + TermCapability(:kMOV, :key_smove, "shifted move key"), + TermCapability(:kNXT, :key_snext, "shifted next key"), + TermCapability(:kOPT, :key_soptions, "shifted options key"), + TermCapability(:kPRV, :key_sprevious, "shifted previous key"), + TermCapability(:kPRT, :key_sprint, "shifted print key"), + TermCapability(:kRDO, :key_sredo, "shifted redo key"), + TermCapability(:kRPL, :key_sreplace, "shifted replace key"), + TermCapability(:kRIT, :key_sright, "shifted right-arrow key"), + TermCapability(:kRES, :key_srsume, "shifted resume key"), + TermCapability(:kSAV, :key_ssave, "shifted save key"), + TermCapability(:kSPD, :key_ssuspend, "shifted suspend key"), + TermCapability(:kUND, :key_sundo, "shifted undo key"), + TermCapability(:rfi, :req_for_input, "send next input char (for ptys)"), + TermCapability(:kf11, :key_f11, "F11 function key"), + TermCapability(:kf12, :key_f12, "F12 function key"), + TermCapability(:kf13, :key_f13, "F13 function key"), + TermCapability(:kf14, :key_f14, "F14 function key"), + TermCapability(:kf15, :key_f15, "F15 function key"), + TermCapability(:kf16, :key_f16, "F16 function key"), + TermCapability(:kf17, :key_f17, "F17 function key"), + TermCapability(:kf18, :key_f18, "F18 function key"), + TermCapability(:kf19, :key_f19, "F19 function key"), + TermCapability(:kf20, :key_f20, "F20 function key"), + TermCapability(:kf21, :key_f21, "F21 function key"), + TermCapability(:kf22, :key_f22, "F22 function key"), + TermCapability(:kf23, :key_f23, "F23 function key"), + TermCapability(:kf24, :key_f24, "F24 function key"), + TermCapability(:kf25, :key_f25, "F25 function key"), + TermCapability(:kf26, :key_f26, "F26 function key"), + TermCapability(:kf27, :key_f27, "F27 function key"), + TermCapability(:kf28, :key_f28, "F28 function key"), + TermCapability(:kf29, :key_f29, "F29 function key"), + TermCapability(:kf30, :key_f30, "F30 function key"), + TermCapability(:kf31, :key_f31, "F31 function key"), + TermCapability(:kf32, :key_f32, "F32 function key"), + TermCapability(:kf33, :key_f33, "F33 function key"), + TermCapability(:kf34, :key_f34, "F34 function key"), + TermCapability(:kf35, :key_f35, "F35 function key"), + TermCapability(:kf36, :key_f36, "F36 function key"), + TermCapability(:kf37, :key_f37, "F37 function key"), + TermCapability(:kf38, :key_f38, "F38 function key"), + TermCapability(:kf39, :key_f39, "F39 function key"), + TermCapability(:kf40, :key_f40, "F40 function key"), + TermCapability(:kf41, :key_f41, "F41 function key"), + TermCapability(:kf42, :key_f42, "F42 function key"), + TermCapability(:kf43, :key_f43, "F43 function key"), + TermCapability(:kf44, :key_f44, "F44 function key"), + TermCapability(:kf45, :key_f45, "F45 function key"), + TermCapability(:kf46, :key_f46, "F46 function key"), + TermCapability(:kf47, :key_f47, "F47 function key"), + TermCapability(:kf48, :key_f48, "F48 function key"), + TermCapability(:kf49, :key_f49, "F49 function key"), + TermCapability(:kf50, :key_f50, "F50 function key"), + TermCapability(:kf51, :key_f51, "F51 function key"), + TermCapability(:kf52, :key_f52, "F52 function key"), + TermCapability(:kf53, :key_f53, "F53 function key"), + TermCapability(:kf54, :key_f54, "F54 function key"), + TermCapability(:kf55, :key_f55, "F55 function key"), + TermCapability(:kf56, :key_f56, "F56 function key"), + TermCapability(:kf57, :key_f57, "F57 function key"), + TermCapability(:kf58, :key_f58, "F58 function key"), + TermCapability(:kf59, :key_f59, "F59 function key"), + TermCapability(:kf60, :key_f60, "F60 function key"), + TermCapability(:kf61, :key_f61, "F61 function key"), + TermCapability(:kf62, :key_f62, "F62 function key"), + TermCapability(:kf63, :key_f63, "F63 function key"), + TermCapability(:el1, :clr_bol, "Clear to beginning of line"), + TermCapability(:mgc, :clear_margins, "clear right and left soft margins"), + TermCapability(:smgl, :set_left_margin, "set left soft margin at current column. (ML is not in BSD termcap)."), + TermCapability(:smgr, :set_right_margin, "set right soft margin at current column"), + TermCapability(:fln, :label_format, "label format"), + TermCapability(:sclk, :set_clock, "set clock, #1 hrs #2 mins #3 secs"), + TermCapability(:dclk, :display_clock, "display clock"), + TermCapability(:rmclk, :remove_clock, "remove clock"), + TermCapability(:cwin, :create_window, "define a window #1 from #2, #3 to #4, #5"), + TermCapability(:wingo, :goto_window, "go to window #1"), + TermCapability(:hup, :hangup, "hang-up phone"), + TermCapability(:dial, :dial_phone, "dial number #1"), + TermCapability(:qdial, :quick_dial, "dial number #1 without checking"), + TermCapability(:tone, :tone, "select touch tone dialing"), + TermCapability(:pulse, :pulse, "select pulse dialing"), + TermCapability(:hook, :flash_hook, "flash switch hook"), + TermCapability(:pause, :fixed_pause, "pause for 2-3 seconds"), + TermCapability(:wait, :wait_tone, "wait for dial-tone"), + TermCapability(:u0, :user0, "User string #0"), + TermCapability(:u1, :user1, "User string #1"), + TermCapability(:u2, :user2, "User string #2"), + TermCapability(:u3, :user3, "User string #3"), + TermCapability(:u4, :user4, "User string #4"), + TermCapability(:u5, :user5, "User string #5"), + TermCapability(:u6, :user6, "User string #6"), + TermCapability(:u7, :user7, "User string #7"), + TermCapability(:u8, :user8, "User string #8"), + TermCapability(:u9, :user9, "User string #9"), + TermCapability(:op, :orig_pair, "Set default pair to its original value"), + TermCapability(:oc, :orig_colors, "Set all color pairs to the original ones"), + TermCapability(:initc, :initialize_color, "Initialize color #1 to (#2, #3, #4)"), + TermCapability(:initp, :initialize_pair, "Initialize color pair #1 to fg=(#2, #3, #4), bg=(#5,#6,#7)"), + TermCapability(:scp, :set_color_pair, "Set current color pair to #1"), + TermCapability(:setf, :set_foreground, "Set foreground color #1"), + TermCapability(:setb, :set_background, "Set background color #1"), + TermCapability(:cpi, :change_char_pitch, "Change number of characters per inch to #1"), + TermCapability(:lpi, :change_line_pitch, "Change number of lines per inch to #1"), + TermCapability(:chr, :change_res_horz, "Change horizontal resolution to #1"), + TermCapability(:cvr, :change_res_vert, "Change vertical resolution to #1"), + TermCapability(:defc, :define_char, "Define a character #1, #2 dots wide, descender #3"), + TermCapability(:swidm, :enter_doublewide_mode, "Enter double-wide mode"), + TermCapability(:sdrfq, :enter_draft_quality, "Enter draft-quality mode"), + TermCapability(:sitm, :enter_italics_mode, "Enter italic mode"), + TermCapability(:slm, :enter_leftward_mode, "Start leftward carriage motion"), + TermCapability(:smicm, :enter_micro_mode, "Start micro-motion mode"), + TermCapability(:snlq, :enter_near_letter_quality, "Enter NLQ mode"), + TermCapability(:snrmq, :enter_normal_quality, "Enter normal-quality mode"), + TermCapability(:sshm, :enter_shadow_mode, "Enter shadow-print mode"), + TermCapability(:ssubm, :enter_subscript_mode, "Enter subscript mode"), + TermCapability(:ssupm, :enter_superscript_mode, "Enter superscript mode"), + TermCapability(:sum, :enter_upward_mode, "Start upward carriage motion"), + TermCapability(:rwidm, :exit_doublewide_mode, "End double-wide mode"), + TermCapability(:ritm, :exit_italics_mode, "End italic mode"), + TermCapability(:rlm, :exit_leftward_mode, "End left-motion mode"), + TermCapability(:rmicm, :exit_micro_mode, "End micro-motion mode"), + TermCapability(:rshm, :exit_shadow_mode, "End shadow-print mode"), + TermCapability(:rsubm, :exit_subscript_mode, "End subscript mode"), + TermCapability(:rsupm, :exit_superscript_mode, "End superscript mode"), + TermCapability(:rum, :exit_upward_mode, "End reverse character motion"), + TermCapability(:mhpa, :micro_column_address, "Like column_address in micro mode"), + TermCapability(:mcud1, :micro_down, "Like cursor_down in micro mode"), + TermCapability(:mcub1, :micro_left, "Like cursor_left in micro mode"), + TermCapability(:mcuf1, :micro_right, "Like cursor_right in micro mode"), + TermCapability(:mvpa, :micro_row_address, "Like row_address #1 in micro mode"), + TermCapability(:mcuu1, :micro_up, "Like cursor_up in micro mode"), + TermCapability(:porder, :order_of_pins, "Match software bits to print-head pins"), + TermCapability(:mcud, :parm_down_micro, "Like parm_down_cursor in micro mode"), + TermCapability(:mcub, :parm_left_micro, "Like parm_left_cursor in micro mode"), + TermCapability(:mcuf, :parm_right_micro, "Like parm_right_cursor in micro mode"), + TermCapability(:mcuu, :parm_up_micro, "Like parm_up_cursor in micro mode"), + TermCapability(:scs, :select_char_set, "Select character set, #1"), + TermCapability(:smgb, :set_bottom_margin, "Set bottom margin at current line"), + TermCapability(:smgbp, :set_bottom_margin_parm, "Set bottom margin at line #1 or (if smgtp is not given) #2 lines from bottom"), + TermCapability(:smglp, :set_left_margin_parm, "Set left (right) margin at column #1"), + TermCapability(:smgrp, :set_right_margin_parm, "Set right margin at column #1"), + TermCapability(:smgt, :set_top_margin, "Set top margin at current line"), + TermCapability(:smgtp, :set_top_margin_parm, "Set top (bottom) margin at row #1"), + TermCapability(:sbim, :start_bit_image, "Start printing bit image graphics"), + TermCapability(:scsd, :start_char_set_def, "Start character set definition #1, with #2 characters in the set"), + TermCapability(:rbim, :stop_bit_image, "Stop printing bit image graphics"), + TermCapability(:rcsd, :stop_char_set_def, "End definition of character set #1"), + TermCapability(:subcs, :subscript_characters, "List of subscriptable characters"), + TermCapability(:supcs, :superscript_characters, "List of superscriptable characters"), + TermCapability(:docr, :these_cause_cr, "Printing any of these characters causes CR"), + TermCapability(:zerom, :zero_motion, "No motion for subsequent character"), + TermCapability(:csnm, :char_set_names, "Produce #1'th item from list of character set names"), + TermCapability(:kmous, :key_mouse, "Mouse event has occurred"), + TermCapability(:minfo, :mouse_info, "Mouse status information"), + TermCapability(:reqmp, :req_mouse_pos, "Request mouse position"), + TermCapability(:getm, :get_mouse, "Curses should get button events, parameter #1 not documented."), + TermCapability(:setaf, :set_a_foreground, "Set foreground color to #1, using ANSI escape"), + TermCapability(:setab, :set_a_background, "Set background color to #1, using ANSI escape"), + TermCapability(:pfxl, :pkey_plab, "Program function key #1 to type string #2 and show string #3"), + TermCapability(:devt, :device_type, "Indicate language/codeset support"), + TermCapability(:csin, :code_set_init, "Init sequence for multiple codesets"), + TermCapability(:s0ds, :set0_des_seq, "Shift to codeset 0 (EUC set 0, ASCII)"), + TermCapability(:s1ds, :set1_des_seq, "Shift to codeset 1"), + TermCapability(:s2ds, :set2_des_seq, "Shift to codeset 2"), + TermCapability(:s3ds, :set3_des_seq, "Shift to codeset 3"), + TermCapability(:smglr, :set_lr_margin, "Set both left and right margins to #1, #2. (ML is not in BSD termcap)."), + TermCapability(:smgtb, :set_tb_margin, "Sets both top and bottom margins to #1, #2"), + TermCapability(:birep, :bit_image_repeat, "Repeat bit image cell #1 #2 times"), + TermCapability(:binel, :bit_image_newline, "Move to next row of the bit image"), + TermCapability(:bicr, :bit_image_carriage_return, "Move to beginning of same row"), + TermCapability(:colornm, :color_names, "Give name for color #1"), + TermCapability(:defbi, :define_bit_image_region, "Define rectangular bit image region"), + TermCapability(:endbi, :end_bit_image_region, "End a bit-image region"), + TermCapability(:setcolor, :set_color_band, "Change to ribbon color #1"), + TermCapability(:slines, :set_page_length, "Set page length to #1 lines"), + TermCapability(:dispc, :display_pc_char, "Display PC character #1"), + TermCapability(:smpch, :enter_pc_charset_mode, "Enter PC character display mode"), + TermCapability(:rmpch, :exit_pc_charset_mode, "Exit PC character display mode"), + TermCapability(:smsc, :enter_scancode_mode, "Enter PC scancode mode"), + TermCapability(:rmsc, :exit_scancode_mode, "Exit PC scancode mode"), + TermCapability(:pctrm, :pc_term_options, "PC terminal options"), + TermCapability(:scesc, :scancode_escape, "Escape for scancode emulation"), + TermCapability(:scesa, :alt_scancode_esc, "Alternate escape for scancode emulation"), + TermCapability(:ehhlm, :enter_horizontal_hl_mode, "Enter horizontal highlight mode"), + TermCapability(:elhlm, :enter_left_hl_mode, "Enter left highlight mode"), + TermCapability(:elohlm, :enter_low_hl_mode, "Enter low highlight mode"), + TermCapability(:erhlm, :enter_right_hl_mode, "Enter right highlight mode"), + TermCapability(:ethlm, :enter_top_hl_mode, "Enter top highlight mode"), + TermCapability(:evhlm, :enter_vertical_hl_mode, "Enter vertical highlight mode"), + TermCapability(:sgr1, :set_a_attributes, "Define second set of video attributes #1-#6"), + TermCapability(:slength, :set_pglen_inch, "Set page length to #1 hundredth of an inch (some implementations use sL for termcap)."), + TermCapability(:OTi2, :termcap_init2, "secondary initialization string"), + TermCapability(:OTrs, :termcap_reset, "terminal reset string"), + TermCapability(:OTnl, :linefeed_if_not_lf, "use to move down"), + TermCapability(:OTbs, :backspaces_with_bs, "uses ^H to move left"), + TermCapability(:OTko, :other_non_function_keys, "list of self-mapped keycaps"), + TermCapability(:OTma, :arrow_key_map, "map motion-keys for vi version 2"), + TermCapability(:OTG2, :acs_ulcorner, "single upper left"), + TermCapability(:OTG3, :acs_llcorner, "single lower left"), + TermCapability(:OTG1, :acs_urcorner, "single upper right"), + TermCapability(:OTG4, :acs_lrcorner, "single lower right"), + TermCapability(:OTGR, :acs_ltee, "tee pointing right"), + TermCapability(:OTGL, :acs_rtee, "tee pointing left"), + TermCapability(:OTGU, :acs_btee, "tee pointing up"), + TermCapability(:OTGD, :acs_ttee, "tee pointing down"), + TermCapability(:OTGH, :acs_hline, "single horizontal line"), + TermCapability(:OTGV, :acs_vline, "single vertical line"), + TermCapability(:OTGC, :acs_plus, "single intersection"), + TermCapability(:meml, :memory_lock, "lock memory above cursor"), + TermCapability(:memu, :memory_unlock, "unlock memory"), + TermCapability(:box1, :box_chars_1, "box characters primary set"), +] diff --git a/base/threadingconstructs.jl b/base/threadingconstructs.jl index 7a70132a9dccc..a5a1294be049b 100644 --- a/base/threadingconstructs.jl +++ b/base/threadingconstructs.jl @@ -63,6 +63,8 @@ function _tpid_to_sym(tpid::Int8) return :interactive elseif tpid == 1 return :default + elseif tpid == -1 + return :foreign else throw(ArgumentError("Unrecognized threadpool id $tpid")) end @@ -73,6 +75,8 @@ function _sym_to_tpid(tp::Symbol) return Int8(0) elseif tp === :default return Int8(1) + elseif tp == :foreign + return Int8(-1) else throw(ArgumentError("Unrecognized threadpool name `$(repr(tp))`")) end @@ -81,7 +85,7 @@ end """ Threads.threadpool(tid = threadid()) -> Symbol -Returns the specified thread's threadpool; either `:default` or `:interactive`. +Returns the specified thread's threadpool; either `:default`, `:interactive`, or `:foreign`. """ function threadpool(tid = threadid()) tpid = ccall(:jl_threadpoolid, Int8, (Int16,), tid-1) @@ -108,6 +112,8 @@ See also: `BLAS.get_num_threads` and `BLAS.set_num_threads` in the function threadpoolsize(pool::Symbol = :default) if pool === :default || pool === :interactive tpid = _sym_to_tpid(pool) + elseif pool == :foreign + error("Threadpool size of `:foreign` is indeterminant") else error("invalid threadpool specified") end @@ -151,7 +157,7 @@ function threading_run(fun, static) else # TODO: this should be the current pool (except interactive) if there # are ever more than two pools. - ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, _sym_to_tpid(:default)) + @assert ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, _sym_to_tpid(:default)) == 1 end tasks[i] = t schedule(t) @@ -357,10 +363,10 @@ end function _spawn_set_thrpool(t::Task, tp::Symbol) tpid = _sym_to_tpid(tp) - if _nthreads_in_pool(tpid) == 0 + if tpid == -1 || _nthreads_in_pool(tpid) == 0 tpid = _sym_to_tpid(:default) end - ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, tpid) + @assert ccall(:jl_set_task_threadpoolid, Cint, (Any, Int8), t, tpid) == 1 nothing end diff --git a/base/ttyhascolor.jl b/base/ttyhascolor.jl deleted file mode 100644 index 5984dba6d592e..0000000000000 --- a/base/ttyhascolor.jl +++ /dev/null @@ -1,27 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -if Sys.iswindows() - ttyhascolor(term_type = nothing) = true -else - function ttyhascolor(term_type = get(ENV, "TERM", "")) - startswith(term_type, "xterm") && return true - try - @static if Sys.KERNEL === :FreeBSD - return success(`tput AF 0`) - else - return success(`tput setaf 0`) - end - catch e - return false - end - end -end -function get_have_color() - global have_color - have_color === nothing && (have_color = ttyhascolor()) - return have_color::Bool -end -in(key_value::Pair{Symbol,Bool}, ::TTY) = key_value.first === :color && key_value.second === get_have_color() -haskey(::TTY, key::Symbol) = key === :color -getindex(::TTY, key::Symbol) = key === :color ? get_have_color() : throw(KeyError(key)) -get(::TTY, key::Symbol, default) = key === :color ? get_have_color() : default diff --git a/deps/checksums/ArgTools-08b11b2707593d4d7f92e5f1b9dba7668285ff82.tar.gz/md5 b/deps/checksums/ArgTools-08b11b2707593d4d7f92e5f1b9dba7668285ff82.tar.gz/md5 deleted file mode 100644 index 915ee5c4bb6bf..0000000000000 --- a/deps/checksums/ArgTools-08b11b2707593d4d7f92e5f1b9dba7668285ff82.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -22c097ca7784442f1f10733db7961cc3 diff --git a/deps/checksums/ArgTools-08b11b2707593d4d7f92e5f1b9dba7668285ff82.tar.gz/sha512 b/deps/checksums/ArgTools-08b11b2707593d4d7f92e5f1b9dba7668285ff82.tar.gz/sha512 deleted file mode 100644 index b824dbcb73a08..0000000000000 --- a/deps/checksums/ArgTools-08b11b2707593d4d7f92e5f1b9dba7668285ff82.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -915791ab9837f09db428060bd128e182dda38c8dc10e13f32f059eb8e8b477548e8ae2cd691522f98c88c510b78b2693018264b62d9cc76d5005ea8104d1539a diff --git a/deps/checksums/ArgTools-4eccde45ddc27e4f7fc9094b2861c684e062adb2.tar.gz/md5 b/deps/checksums/ArgTools-4eccde45ddc27e4f7fc9094b2861c684e062adb2.tar.gz/md5 new file mode 100644 index 0000000000000..e0fdf5c76bcc5 --- /dev/null +++ b/deps/checksums/ArgTools-4eccde45ddc27e4f7fc9094b2861c684e062adb2.tar.gz/md5 @@ -0,0 +1 @@ +87d5afd4bf8c66b6e598da521dafad41 diff --git a/deps/checksums/ArgTools-4eccde45ddc27e4f7fc9094b2861c684e062adb2.tar.gz/sha512 b/deps/checksums/ArgTools-4eccde45ddc27e4f7fc9094b2861c684e062adb2.tar.gz/sha512 new file mode 100644 index 0000000000000..3339d6d582c3e --- /dev/null +++ b/deps/checksums/ArgTools-4eccde45ddc27e4f7fc9094b2861c684e062adb2.tar.gz/sha512 @@ -0,0 +1 @@ +0586684ecb31c68840643fa0006a3bb5c042845b89182ca9c67dd6c92863e73c045f5c5dabe9e2d758c9c42288c957935dab1b48c91820cfcf2b240b6902f015 diff --git a/deps/checksums/NetworkOptions-976e51a48abb4e09356f1979c3cde8fbc0852e2e.tar.gz/md5 b/deps/checksums/NetworkOptions-976e51a48abb4e09356f1979c3cde8fbc0852e2e.tar.gz/md5 new file mode 100644 index 0000000000000..ba3e8104143ee --- /dev/null +++ b/deps/checksums/NetworkOptions-976e51a48abb4e09356f1979c3cde8fbc0852e2e.tar.gz/md5 @@ -0,0 +1 @@ +38f8f1a27ede9a4abc3af629cdc5d703 diff --git a/deps/checksums/NetworkOptions-976e51a48abb4e09356f1979c3cde8fbc0852e2e.tar.gz/sha512 b/deps/checksums/NetworkOptions-976e51a48abb4e09356f1979c3cde8fbc0852e2e.tar.gz/sha512 new file mode 100644 index 0000000000000..05621b2734360 --- /dev/null +++ b/deps/checksums/NetworkOptions-976e51a48abb4e09356f1979c3cde8fbc0852e2e.tar.gz/sha512 @@ -0,0 +1 @@ +35da83adddb918efbe8571cc3bc9866e445a3cca59df3769a9707686e2e55c4932f50c92927b354afb3616940cb781134b575034971c23aa27e4b52ef148b5c9 diff --git a/deps/checksums/NetworkOptions-f7bbeb66f05fc651adb12758b650e8630a998fbd.tar.gz/md5 b/deps/checksums/NetworkOptions-f7bbeb66f05fc651adb12758b650e8630a998fbd.tar.gz/md5 deleted file mode 100644 index 9e91b76f9a3c8..0000000000000 --- a/deps/checksums/NetworkOptions-f7bbeb66f05fc651adb12758b650e8630a998fbd.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -16bc9f2eefa3021e19a09ffefc84159b diff --git a/deps/checksums/NetworkOptions-f7bbeb66f05fc651adb12758b650e8630a998fbd.tar.gz/sha512 b/deps/checksums/NetworkOptions-f7bbeb66f05fc651adb12758b650e8630a998fbd.tar.gz/sha512 deleted file mode 100644 index 551f7c8da347c..0000000000000 --- a/deps/checksums/NetworkOptions-f7bbeb66f05fc651adb12758b650e8630a998fbd.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -5b53c09343e25b5bde7ea12c2119da656040ca5f62ce934f00f57945ce73dfaf26522da6a9a007ba06ac6fd75a285cbcbdf5edaf9113faa7bba0398294fbd684 diff --git a/deps/checksums/Pkg-047734e4c7e2b5e99622b4803ec4ad0d49f1d1d3.tar.gz/md5 b/deps/checksums/Pkg-047734e4c7e2b5e99622b4803ec4ad0d49f1d1d3.tar.gz/md5 new file mode 100644 index 0000000000000..efe1cad94378c --- /dev/null +++ b/deps/checksums/Pkg-047734e4c7e2b5e99622b4803ec4ad0d49f1d1d3.tar.gz/md5 @@ -0,0 +1 @@ +1832c1a6660bb7727796bb51c775f8bd diff --git a/deps/checksums/Pkg-047734e4c7e2b5e99622b4803ec4ad0d49f1d1d3.tar.gz/sha512 b/deps/checksums/Pkg-047734e4c7e2b5e99622b4803ec4ad0d49f1d1d3.tar.gz/sha512 new file mode 100644 index 0000000000000..188b44842757e --- /dev/null +++ b/deps/checksums/Pkg-047734e4c7e2b5e99622b4803ec4ad0d49f1d1d3.tar.gz/sha512 @@ -0,0 +1 @@ +823faaa42e7b5611e96afe1e83ea7937db29abcfe88ac2930354686aeebc7a6fe2d72653ba7c4896b8dac8c79875ecc62a7314c871e609b9f77bc113bc1a7502 diff --git a/deps/checksums/Pkg-08e1eb6ed2354102e22181bad5cfe04c701656c0.tar.gz/md5 b/deps/checksums/Pkg-08e1eb6ed2354102e22181bad5cfe04c701656c0.tar.gz/md5 deleted file mode 100644 index 28a4977707505..0000000000000 --- a/deps/checksums/Pkg-08e1eb6ed2354102e22181bad5cfe04c701656c0.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -8809c330a1a7d98b7e5984b67c1a95d3 diff --git a/deps/checksums/Pkg-08e1eb6ed2354102e22181bad5cfe04c701656c0.tar.gz/sha512 b/deps/checksums/Pkg-08e1eb6ed2354102e22181bad5cfe04c701656c0.tar.gz/sha512 deleted file mode 100644 index cceadfad6a602..0000000000000 --- a/deps/checksums/Pkg-08e1eb6ed2354102e22181bad5cfe04c701656c0.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -65540a74bf84dec46f026d2798c9c074dace51a0f8d779bf3db097b8f9d4099895df2954e6d4394e27a9b90d2cc01a97853020ca8a219b13e0c08b3de0655087 diff --git a/deps/checksums/SHA-2d1f84e6f8417a1a368de48318640d948b023e7a.tar.gz/md5 b/deps/checksums/SHA-2d1f84e6f8417a1a368de48318640d948b023e7a.tar.gz/md5 deleted file mode 100644 index f682cf3518658..0000000000000 --- a/deps/checksums/SHA-2d1f84e6f8417a1a368de48318640d948b023e7a.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -de53629eb0b1ce98ac6b245bdbf14e9d diff --git a/deps/checksums/SHA-2d1f84e6f8417a1a368de48318640d948b023e7a.tar.gz/sha512 b/deps/checksums/SHA-2d1f84e6f8417a1a368de48318640d948b023e7a.tar.gz/sha512 deleted file mode 100644 index 870098ef7aada..0000000000000 --- a/deps/checksums/SHA-2d1f84e6f8417a1a368de48318640d948b023e7a.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -71cdc58b03cc4f42f8c4b9c2353d6f94d77b4ac5c9d374387d435c57ba85e966f3be4e8c8447b34e184cb8e665c42b3cd2c9d9742c86f7fb5c71a85df5087966 diff --git a/deps/checksums/SHA-aaf2df61ff8c3898196587a375d3cf213bd40b41.tar.gz/md5 b/deps/checksums/SHA-aaf2df61ff8c3898196587a375d3cf213bd40b41.tar.gz/md5 new file mode 100644 index 0000000000000..3b51189e187a3 --- /dev/null +++ b/deps/checksums/SHA-aaf2df61ff8c3898196587a375d3cf213bd40b41.tar.gz/md5 @@ -0,0 +1 @@ +dec1d21e890c88e57a0d4eb085633d57 diff --git a/deps/checksums/SHA-aaf2df61ff8c3898196587a375d3cf213bd40b41.tar.gz/sha512 b/deps/checksums/SHA-aaf2df61ff8c3898196587a375d3cf213bd40b41.tar.gz/sha512 new file mode 100644 index 0000000000000..cbe1ff2eea29e --- /dev/null +++ b/deps/checksums/SHA-aaf2df61ff8c3898196587a375d3cf213bd40b41.tar.gz/sha512 @@ -0,0 +1 @@ +fb611794a539c6725000ff6eda13e0af5dd3f82e22466bdff650ffa0e4edbba5ac4707195035531645a4161ecbb5f873f4f6b1040ce33e9b1adf9c1d34187718 diff --git a/deps/checksums/SparseArrays-54f4b39d1cd34a908ff4fdfcd052a38fe23f6555.tar.gz/md5 b/deps/checksums/SparseArrays-54f4b39d1cd34a908ff4fdfcd052a38fe23f6555.tar.gz/md5 new file mode 100644 index 0000000000000..a0fcfe6a3325b --- /dev/null +++ b/deps/checksums/SparseArrays-54f4b39d1cd34a908ff4fdfcd052a38fe23f6555.tar.gz/md5 @@ -0,0 +1 @@ +009f6c67609b62377f2a4f5f59621968 diff --git a/deps/checksums/SparseArrays-54f4b39d1cd34a908ff4fdfcd052a38fe23f6555.tar.gz/sha512 b/deps/checksums/SparseArrays-54f4b39d1cd34a908ff4fdfcd052a38fe23f6555.tar.gz/sha512 new file mode 100644 index 0000000000000..5d07d3ac99feb --- /dev/null +++ b/deps/checksums/SparseArrays-54f4b39d1cd34a908ff4fdfcd052a38fe23f6555.tar.gz/sha512 @@ -0,0 +1 @@ +ab6266aca1d6bfecee925cf7d24494e995b657c1530e825dcffcd518e013da64bae0d1df8e9cf9a4c54817fbf83144a77ec69a4e5805039a56c0fff5d795831a diff --git a/deps/checksums/SparseArrays-99c99b4521eb19a7973643d1aa4d7b1e4d50a6db.tar.gz/md5 b/deps/checksums/SparseArrays-99c99b4521eb19a7973643d1aa4d7b1e4d50a6db.tar.gz/md5 deleted file mode 100644 index 6509fe3ef2766..0000000000000 --- a/deps/checksums/SparseArrays-99c99b4521eb19a7973643d1aa4d7b1e4d50a6db.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -b2ddca51d8d39c2869555229575451f2 diff --git a/deps/checksums/SparseArrays-99c99b4521eb19a7973643d1aa4d7b1e4d50a6db.tar.gz/sha512 b/deps/checksums/SparseArrays-99c99b4521eb19a7973643d1aa4d7b1e4d50a6db.tar.gz/sha512 deleted file mode 100644 index 2855286b87ab3..0000000000000 --- a/deps/checksums/SparseArrays-99c99b4521eb19a7973643d1aa4d7b1e4d50a6db.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -cff167088056690a7c7453326bc1ad0ca0fcff9ffda85ebfd943ebad9684fc8134b0cda18dd70d8550de35493c6d9579b2fb51090b1c9718fa4290008c6d5f3f diff --git a/deps/checksums/Tar-81888a33704b233a2ad6f82f84456a1dd82c87f0.tar.gz/md5 b/deps/checksums/Tar-81888a33704b233a2ad6f82f84456a1dd82c87f0.tar.gz/md5 new file mode 100644 index 0000000000000..921ffb0a2561e --- /dev/null +++ b/deps/checksums/Tar-81888a33704b233a2ad6f82f84456a1dd82c87f0.tar.gz/md5 @@ -0,0 +1 @@ +b3d21b3f38cd106e64fa9d058d095651 diff --git a/deps/checksums/Tar-81888a33704b233a2ad6f82f84456a1dd82c87f0.tar.gz/sha512 b/deps/checksums/Tar-81888a33704b233a2ad6f82f84456a1dd82c87f0.tar.gz/sha512 new file mode 100644 index 0000000000000..cbf6ad4952258 --- /dev/null +++ b/deps/checksums/Tar-81888a33704b233a2ad6f82f84456a1dd82c87f0.tar.gz/sha512 @@ -0,0 +1 @@ +61bd3555de7a2cec265ae72d58b4635f84ec75b993b9dab2dc5be64375b6057972a2786337f90742ad3b91c57f5008372a3a4f8a5b589e2cf4d5cd1a8056e03c diff --git a/deps/checksums/Tar-ff55460f4d329949661a33e6c8168ce6d890676c.tar.gz/md5 b/deps/checksums/Tar-ff55460f4d329949661a33e6c8168ce6d890676c.tar.gz/md5 deleted file mode 100644 index 40d52c2803746..0000000000000 --- a/deps/checksums/Tar-ff55460f4d329949661a33e6c8168ce6d890676c.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -438818cad063d6808354a9b4aecd3001 diff --git a/deps/checksums/Tar-ff55460f4d329949661a33e6c8168ce6d890676c.tar.gz/sha512 b/deps/checksums/Tar-ff55460f4d329949661a33e6c8168ce6d890676c.tar.gz/sha512 deleted file mode 100644 index 27c57c5051212..0000000000000 --- a/deps/checksums/Tar-ff55460f4d329949661a33e6c8168ce6d890676c.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -f9a6e7757bbcca09a84d92ab3a2690a51612c318bdfd98bbb4ffcef56305b019029838e5f1483c9febafa7ecb5e735e68855bc82d04b593af04a446e32436145 diff --git a/deps/libsuitesparse.mk b/deps/libsuitesparse.mk index 16d4919031ca5..10161f08ecf5b 100644 --- a/deps/libsuitesparse.mk +++ b/deps/libsuitesparse.mk @@ -24,6 +24,10 @@ LIBSUITESPARSE_CMAKE_FLAGS := $(CMAKE_COMMON) \ -DLAPACK_LIBRARIES="$(build_shlibdir)/libblastrampoline.$(SHLIB_EXT)" \ -DLAPACK_LINKER_FLAGS="blastrampoline" +ifneq (,$(findstring $(OS),Linux FreeBSD)) +LIBSUITESPARSE_CMAKE_FLAGS += -DCMAKE_INSTALL_RPATH="\$$ORIGIN" +endif + $(SRCCACHE)/SuiteSparse-$(LIBSUITESPARSE_VER).tar.gz: | $(SRCCACHE) $(JLDOWNLOAD) $@ https://github.com/Wimmerer/SuiteSparse/archive/v$(LIBSUITESPARSE_VER).tar.gz @@ -60,7 +64,7 @@ $(BUILDDIR)/SuiteSparse-$(LIBSUITESPARSE_VER)/build-checked: $(BUILDDIR)/SuiteSp done echo 1 > $@ -UNINSTALL_suitesparse := $(LIBSUITESPARSE_VER) manual_suitesparse $(LIBSUITESPARSE_LIBS) +UNINSTALL_libsuitesparse := $(LIBSUITESPARSE_VER) manual_libsuitesparse $(LIBSUITESPARSE_LIBS) $(build_prefix)/manifest/libsuitesparse: $(BUILDDIR)/SuiteSparse-$(LIBSUITESPARSE_VER)/build-compiled | $(build_prefix)/manifest $(build_shlibdir) echo $(UNINSTALL_libsuitesparse) > $@ diff --git a/doc/src/base/collections.md b/doc/src/base/collections.md index 8c943d447e2dd..eab84708666d7 100644 --- a/doc/src/base/collections.md +++ b/doc/src/base/collections.md @@ -64,6 +64,7 @@ Base.LinRange ```@docs Base.isempty +Base.isdone Base.empty! Base.length Base.checked_length diff --git a/doc/src/base/parallel.md b/doc/src/base/parallel.md index c9f24429fd0e5..c3106b8caf8c7 100644 --- a/doc/src/base/parallel.md +++ b/doc/src/base/parallel.md @@ -50,6 +50,7 @@ Base.unlock Base.trylock Base.islocked Base.ReentrantLock +Base.@lock ``` ## Channels diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index 1a11a5918d091..4bd3959cf0ff6 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -254,11 +254,6 @@ types exist in lowered form: Identifies arguments and local variables by consecutive numbering. It has an integer-valued `id` field giving the slot index. The types of these slots can be found in the `slottypes` field of their `CodeInfo` object. - When a slot has different types at different uses and thus requires per-use type annotations, - they are converted to temporary `Core.Compiler.TypedSlot` object. This object has an - additional `typ` field as well as the `id` field. Note that `Core.Compiler.TypedSlot` - only appears in an unoptimized lowered form that is scheduled for optimization, - and it never appears elsewhere. * `Argument` diff --git a/doc/src/devdocs/external_profilers.md b/doc/src/devdocs/external_profilers.md index 956d66508fc89..d711f3fc753ee 100644 --- a/doc/src/devdocs/external_profilers.md +++ b/doc/src/devdocs/external_profilers.md @@ -39,7 +39,13 @@ run(TracyProfiler_jll.tracy()) !!! note On macOS, you may want to set the `TRACY_DPI_SCALE` environment variable to `1.0` if the UI elements in the profiler appear excessively large. -To run a "headless" instance that saves the trace to disk, use `TracyProfiler_jll.capture() -o mytracefile.tracy` instead. +To run a "headless" instance that saves the trace to disk, use + +```julia +run(`$(TracyProfiler_jll.capture()) -o mytracefile.tracy`) +``` + +instead. For information on using the Tracy UI, refer to the Tracy manual. diff --git a/doc/src/manual/command-line-interface.md b/doc/src/manual/command-line-interface.md index d52ff1bd499f7..69763d14a7668 100644 --- a/doc/src/manual/command-line-interface.md +++ b/doc/src/manual/command-line-interface.md @@ -39,6 +39,58 @@ $ julia --color=yes -O -- script.jl arg1 arg2.. See also [Scripting](@ref man-scripting) for more information on writing Julia scripts. +## The `Main.main` entry point + +At the conclusion of executing a script or expression, `julia` will attempt to execute the function +`Main.main(ARGS)` (if such a function has been defined). This feature is intended to aid in the unification +of compiled and interactive workflows. In compiled workflows, loading the code that defines the `main` +function may be spatially and temporally separated from the invocation. However, for interactive workflows, +the behavior is equivalent to explicitly calling `exit(main(ARGS))` at the end of the evaluated script or +expression. + +!!! compat "Julia 1.11" + The special entry point `Main.main` was added in Julia 1.11. For compatibility with prior julia versions, + add an explicit `VERSION < v"1.11" && exit(main(ARGS))` at the end of your scripts. + +To see this feature in action, consider the following definition, which will execute the print function despite there being no explicit call to `main`: + +``` +$ julia -e 'main(ARGS) = println("Hello World!")' +Hello World! +$ +``` + +Only the `main` binding in the `Main`, module has this special behavior. For example, using `hello` +instead of `main` will result in the `hello` function not executing: + +``` +$ julia -e 'hello(ARGS) = println("Hello World!")' +$ +``` + +The `main` binding may be imported from a package. A hello package defined as + +``` +module Hello + +export main +main(ARGS) = println("Hello from the package!") + +end +``` + +may be used as: + +``` +$ julia -e 'using Hello' +Hello from the package! +$ julia -e 'import Hello' # N.B.: Execution depends on the binding not whether the package is loaded +$ +``` + +However, note that the current best practice recommendation is to not mix application and reusable library +code in the same package. Helper applications may be distributed as separate pacakges or as scripts with +separate `main` entry points in a package's `bin` folder. ## Parallel mode @@ -101,7 +153,7 @@ The following is a complete list of command-line switches available when launchi |`--startup-file={yes*\|no}` |Load `JULIA_DEPOT_PATH/config/startup.jl`; if `JULIA_DEPOT_PATH` environment variable is unset, load `~/.julia/config/startup.jl`| |`--handle-signals={yes*\|no}` |Enable or disable Julia's default signal handlers| |`--sysimage-native-code={yes*\|no}` |Use native code from system image if available| -|`--compiled-modules={yes*\|no|existing}` |Enable or disable incremental precompilation of modules. The `existing` option allows use of existing compiled modules that were previously precompiled, but disallows creation of new precompile files.| +|`--compiled-modules={yes*\|no\|existing}` |Enable or disable incremental precompilation of modules. The `existing` option allows use of existing compiled modules that were previously precompiled, but disallows creation of new precompile files.| |`--pkgimages={yes*\|no}` |Enable or disable usage of native code caching in the form of pkgimages| |`-e`, `--eval ` |Evaluate ``| |`-E`, `--print ` |Evaluate `` and display the result| diff --git a/doc/src/manual/integers-and-floating-point-numbers.md b/doc/src/manual/integers-and-floating-point-numbers.md index 173ca7847616e..8b45288f38efa 100644 --- a/doc/src/manual/integers-and-floating-point-numbers.md +++ b/doc/src/manual/integers-and-floating-point-numbers.md @@ -653,6 +653,13 @@ julia> setprecision(40) do 1.1000000000004 ``` +!!! warning + The relation between [`setprecision`](@ref) or [`setrounding`](@ref) and + [`@big_str`](@ref), the macro used for `big` string literals (such as + `big"0.3"`), might not be intuitive, as a consequence of the fact that + `@big_str` is a macro. See the [`@big_str`](@ref) documentation for + details. + ## [Numeric Literal Coefficients](@id man-numeric-literal-coefficients) To make common numeric formulae and expressions clearer, Julia allows variables to be immediately diff --git a/doc/src/manual/interfaces.md b/doc/src/manual/interfaces.md index ad916ab32e7d8..6cad44f12a089 100644 --- a/doc/src/manual/interfaces.md +++ b/doc/src/manual/interfaces.md @@ -7,44 +7,24 @@ to generically build upon those behaviors. ## [Iteration](@id man-interface-iteration) -### Required methods - -| Method | Brief description | -|:---------------------- |:---------------------------------------------------------------------------------------- | -| `iterate(iter)` | Returns either a tuple of the first item and initial state or [`nothing`](@ref) if empty | -| `iterate(iter, state)` | Returns either a tuple of the next item and next state or `nothing` if no items remain | - -Depending on the definition of `Base.IteratorSize(IterType)`, you may need to define additional methods: - -| Value returned by `Base.IteratorSize(IterType)` | Required Methods | -|:----------------------------------------------- |:---------------------------------------------- | -| `Base.HasLength()` | [`length(iter)`](@ref) | -| `Base.HasShape{N}()` | `length(iter)` and [`size(iter, [dim])`](@ref) | -| `Base.IsInfinite()` | (*none*) | -| `Base.SizeUnknown()` | (*none*) | - -Because the default definition of `Base.IteratorSize(IterType)` is `Base.HasLength()`, you will need to define at least one of `Base.IteratorSize(IterType)` and `length(iter)`. - -| Method | Default definition | Brief description | -|:---------------------------- |:------------------ |:------------------------------------------------------------------------------------------------------------ | -| `Base.IteratorSize(IterType)`| `Base.HasLength()` | One of `Base.HasLength()`, `Base.HasShape{N}()`, `Base.IsInfinite()`, or `Base.SizeUnknown()` as appropriate | -| `length(iter)` | (*undefined*) | The number of items, if known | -| `size(iter, [dim])` | (*undefined*) | The number of items in each dimension, if known | - -### Optional methods - -| Method | Default definition | Brief description | -|:------------------------------- |:------------------ |:-------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `Base.IteratorEltype(IterType)` | `Base.HasEltype()` | Either `Base.EltypeUnknown()` or `Base.HasEltype()` as appropriate | -| `eltype(IterType)` | `Any` | The type of the first entry of the tuple returned by `iterate()` | -| `Base.isdone(iter[, state])` | `missing` | Fast-path hint for iterator completion. Should be defined for stateful iterators, or else `isempty(iter)` may call `iterate(iter[, state])` and mutate the iterator. | - -| Value returned by `Base.IteratorEltype(IterType)` | Required Methods | -|:------------------------------------------------- |:------------------ | -| `Base.HasEltype()` | `eltype(IterType)` | -| `Base.EltypeUnknown()` | (*none*) | - -### Description +There are two methods that are always required: + +| Required method | Brief description | +|:----------------------- |:---------------------------------------------------------------------------------------- | +| [`iterate(iter)`](@ref) | Returns either a tuple of the first item and initial state or [`nothing`](@ref) if empty | +| `iterate(iter, state)` | Returns either a tuple of the next item and next state or `nothing` if no items remain | + +There are several more methods that should be defined in some circumstances. +Please note that you should always define at least one of `Base.IteratorSize(IterType)` and `length(iter)` because the default definition of `Base.IteratorSize(IterType)` is `Base.HasLength()`. + +| Method | When should this method be defined? | Default definition | Brief description | +|:--- |:--- |:--- |:--- | +| [`Base.IteratorSize(IterType)`](@ref) | If default is not appropriate | `Base.HasLength()` | One of `Base.HasLength()`, `Base.HasShape{N}()`, `Base.IsInfinite()`, or `Base.SizeUnknown()` as appropriate | +| [`length(iter)`](@ref) | If `Base.IteratorSize()` returns `Base.HasLength()` or `Base.HasShape{N}()` | (*undefined*) | The number of items, if known | +| [`size(iter, [dim])`](@ref) | If `Base.IteratorSize()` returns `Base.HasShape{N}()` | (*undefined*) | The number of items in each dimension, if known | +| [`Base.IteratorEltype(IterType)`](@ref) | If default is not appropriate | `Base.HasEltype()` | Either `Base.EltypeUnknown()` or `Base.HasEltype()` as appropriate | +| [`eltype(IterType)`](@ref) | If default is not appropriate | `Any` | The type of the first entry of the tuple returned by `iterate()` | +| [`Base.isdone(iter, [state])`](@ref) | **Must** be defined if iterator is stateful | `missing` | Fast-path hint for iterator completion. If not defined for a stateful iterator then functions that check for done-ness, like `isempty()` and `zip()`, may mutate the iterator and cause buggy behaviour! | Sequential iteration is implemented by the [`iterate`](@ref) function. Instead of mutating objects as they are iterated over, Julia iterators may keep track diff --git a/doc/src/manual/multi-threading.md b/doc/src/manual/multi-threading.md index 33ef385ce1de9..152b73941013f 100644 --- a/doc/src/manual/multi-threading.md +++ b/doc/src/manual/multi-threading.md @@ -165,6 +165,8 @@ julia> begin unlock(lk) end end + +julia> @lock lk use(a) ``` where `lk` is a lock (e.g. `ReentrantLock()`) and `a` data. diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index 069c3b2d424e5..f2ec811ee1583 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -1035,6 +1035,20 @@ some judgment may be required. However, for "vectorized" (element-wise) function syntax `x .= f.(y)` can be used for in-place operations with fused loops and no temporary arrays (see the [dot syntax for vectorizing functions](@ref man-vectorized)). +## [Use `MutableArithmetics` for more control over allocation for mutable arithmetic types](@id man-perftips-mutablearithmetics) + +Some [`Number`](@ref) subtypes, such as [`BigInt`](@ref) or [`BigFloat`](@ref), may +be implemented as [`mutable struct`](@ref) types, or they may have mutable +components. The arithmetic interfaces in Julia `Base` usually opt for convenience +over efficiency in such cases, so using them in a naive manner may result in +suboptimal performance. The abstractions of the +[`MutableArithmetics`](https://juliahub.com/ui/Packages/General/MutableArithmetics) +package, on the other hand, make it possible to exploit the mutability of such types +for writing fast code that allocates only as much as necessary. `MutableArithmetics` +also makes it possible to copy values of mutable arithmetic types explicitly when +necessary. `MutableArithmetics` is a user package and is not affiliated with the +Julia project. + ## More dots: Fuse vectorized operations Julia has a special [dot syntax](@ref man-vectorized) that converts diff --git a/src/builtins.c b/src/builtins.c index 4f75fd79eadcc..81afa8bae482a 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1694,36 +1694,48 @@ static int equiv_field_types(jl_value_t *old, jl_value_t *ft) // inline it. The only way fields can reference this type (due to // syntax-enforced restrictions) is via being passed as a type parameter. Thus // we can conservatively check this by examining only the parameters of the -// dependent types. -// affects_layout is a hack introduced by #35275 to workaround a problem -// introduced by #34223: it checks whether we will potentially need to -// compute the layout of the object before we have fully computed the types of -// the fields during recursion over the allocation of the parameters for the -// field types (of the concrete subtypes) -static int references_name(jl_value_t *p, jl_typename_t *name, int affects_layout) JL_NOTSAFEPOINT -{ - if (jl_is_uniontype(p)) - return references_name(((jl_uniontype_t*)p)->a, name, affects_layout) || - references_name(((jl_uniontype_t*)p)->b, name, affects_layout); - if (jl_is_unionall(p)) - return references_name((jl_value_t*)((jl_unionall_t*)p)->var->lb, name, 0) || - references_name((jl_value_t*)((jl_unionall_t*)p)->var->ub, name, 0) || - references_name(((jl_unionall_t*)p)->body, name, affects_layout); +// dependent types. Additionally, a field might have already observed this +// object for layout purposes before we got around to deciding if inlining +// would be possible, so we cannot change the layout now if so. +// affects_layout is a (conservative) analysis of layout_uses_free_typevars +// freevars is a (conservative) analysis of what calling jl_has_bound_typevars from name->wrapper gives (TODO: just call this instead?) +static int references_name(jl_value_t *p, jl_typename_t *name, int affects_layout, int freevars) JL_NOTSAFEPOINT +{ + if (freevars && !jl_has_free_typevars(p)) + freevars = 0; + while (jl_is_unionall(p)) { + if (references_name((jl_value_t*)((jl_unionall_t*)p)->var->lb, name, 0, freevars) || + references_name((jl_value_t*)((jl_unionall_t*)p)->var->ub, name, 0, freevars)) + return 1; + p = ((jl_unionall_t*)p)->body; + } + if (jl_is_uniontype(p)) { + return references_name(((jl_uniontype_t*)p)->a, name, affects_layout, freevars) || + references_name(((jl_uniontype_t*)p)->b, name, affects_layout, freevars); + } if (jl_is_typevar(p)) return 0; // already checked by unionall, if applicable if (jl_is_datatype(p)) { jl_datatype_t *dp = (jl_datatype_t*)p; if (affects_layout && dp->name == name) return 1; - // affects_layout checks whether we will need to attempt to layout this - // type (based on whether all copies of it have the same layout) in - // that case, we still need to check the recursive parameters for - // layout recursion happening also, but we know it won't itself cause - // problems for the layout computation affects_layout = ((jl_datatype_t*)jl_unwrap_unionall(dp->name->wrapper))->layout == NULL; + // and even if it has a layout, the fields themselves might trigger layouts if they use tparam i + // rather than checking this for each field, we just assume it applies + if (!affects_layout && freevars && jl_field_names(dp) != jl_emptysvec) { + jl_svec_t *types = ((jl_datatype_t*)jl_unwrap_unionall(dp->name->wrapper))->types; + size_t i, l = jl_svec_len(types); + for (i = 0; i < l; i++) { + jl_value_t *ft = jl_svecref(types, i); + if (!jl_is_typevar(ft) && jl_has_free_typevars(ft)) { + affects_layout = 1; + break; + } + } + } size_t i, l = jl_nparams(p); for (i = 0; i < l; i++) { - if (references_name(jl_tparam(p, i), name, affects_layout)) + if (references_name(jl_tparam(p, i), name, affects_layout, freevars)) return 1; } } @@ -1759,12 +1771,12 @@ JL_CALLABLE(jl_f__typebody) // able to compute the layout of the object before needing to // publish it, so we must assume it cannot be inlined, if that // check passes, then we also still need to check the fields too. - if (!dt->name->mutabl && (nf == 0 || !references_name((jl_value_t*)dt->super, dt->name, 1))) { + if (!dt->name->mutabl && (nf == 0 || !references_name((jl_value_t*)dt->super, dt->name, 0, 1))) { int mayinlinealloc = 1; size_t i; for (i = 0; i < nf; i++) { jl_value_t *fld = jl_svecref(ft, i); - if (references_name(fld, dt->name, 1)) { + if (references_name(fld, dt->name, 1, 1)) { mayinlinealloc = 0; break; } diff --git a/src/codegen.cpp b/src/codegen.cpp index e3bf3ad4e0d49..a582a1e7aeaa4 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -2322,8 +2322,8 @@ static jl_cgval_t convert_julia_type(jl_codectx_t &ctx, const jl_cgval_t &v, jl_ return jl_cgval_t(); } } - if (jl_is_concrete_type(v.typ) && !jl_is_kind(v.typ)) { - if (jl_is_concrete_type(typ) && !jl_is_kind(typ)) { + if (jl_is_concrete_type(v.typ)) { + if (jl_is_concrete_type(typ)) { // type mismatch: changing from one leaftype to another if (skip) *skip = ConstantInt::get(getInt1Ty(ctx.builder.getContext()), 1); @@ -4587,6 +4587,8 @@ static jl_cgval_t emit_specsig_oc_call(jl_codectx_t &ctx, jl_value_t *oc_type, j typ = jl_unwrap_vararg(typ); emit_typecheck(ctx, argv[i+1], typ, "typeassert"); argv[i+1] = update_julia_type(ctx, argv[i+1], typ); + if (argv[i+1].typ == jl_bottom_type) + return jl_cgval_t(); } jl_returninfo_t::CallingConv cc = jl_returninfo_t::CallingConv::Boxed; unsigned return_roots = 0; @@ -4609,6 +4611,9 @@ static jl_cgval_t emit_call(jl_codectx_t &ctx, jl_expr_t *ex, jl_value_t *rt, bo size_t nargs = jl_array_dim0(ex->args); assert(nargs >= 1); jl_cgval_t f = emit_expr(ctx, args[0]); + if (f.typ == jl_bottom_type) { + return jl_cgval_t(); + } if (f.constant && jl_typetagis(f.constant, jl_intrinsic_type)) { JL_I::intrinsic fi = (intrinsic)*(uint32_t*)jl_data_ptr(f.constant); @@ -7159,7 +7164,14 @@ static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value if (f == NULL) { f = Function::Create(ftype, GlobalVariable::ExternalLinkage, name, M); jl_init_function(f, ctx.emission_context.TargetTriple); - f->setAttributes(AttributeList::get(f->getContext(), {attributes, f->getAttributes()})); + if (ctx.emission_context.debug_level >= 2) { + ios_t sigbuf; + ios_mem(&sigbuf, 0); + jl_static_show_func_sig((JL_STREAM*) &sigbuf, sig); + f->setAttributes(AttributeList::get(f->getContext(), {attributes.addFnAttribute(ctx.builder.getContext(),"julia.fsig", StringRef(sigbuf.buf, sigbuf.size)), f->getAttributes()})); + ios_close(&sigbuf); + } else + f->setAttributes(AttributeList::get(f->getContext(), {attributes, f->getAttributes()})); } else { assert(f->getFunctionType() == ftype); diff --git a/src/gc-pages.c b/src/gc-pages.c index 8d596f4a815ca..44434c44cfa75 100644 --- a/src/gc-pages.c +++ b/src/gc-pages.c @@ -127,7 +127,7 @@ NOINLINE jl_gc_pagemeta_t *jl_gc_alloc_page(void) JL_NOTSAFEPOINT meta = pop_lf_page_metadata_back(&global_page_pool_clean); if (meta != NULL) { uv_mutex_unlock(&gc_perm_lock); - gc_alloc_map_set(meta->data, 1); + gc_alloc_map_set(meta->data, GC_PAGE_ALLOCATED); goto exit; } // must map a new set of pages diff --git a/src/gc.c b/src/gc.c index 1603213d94efa..bbf996c98040a 100644 --- a/src/gc.c +++ b/src/gc.c @@ -2318,19 +2318,10 @@ STATIC_INLINE void gc_mark_excstack(jl_ptls_t ptls, jl_excstack_t *excstack, siz } // Mark module binding -STATIC_INLINE void gc_mark_module_binding(jl_ptls_t ptls, jl_module_t *mb_parent, jl_binding_t **mb_begin, - jl_binding_t **mb_end, uintptr_t nptr, +STATIC_INLINE void gc_mark_module_binding(jl_ptls_t ptls, jl_module_t *mb_parent, uintptr_t nptr, uint8_t bits) JL_NOTSAFEPOINT { jl_gc_markqueue_t *mq = &ptls->mark_queue; - for (; mb_begin < mb_end; mb_begin++) { - jl_binding_t *b = *mb_begin; - if (b == (jl_binding_t *)jl_nothing) - continue; - verify_parent1("module", mb_parent, mb_begin, "binding_buff"); - gc_assert_parent_validity((jl_value_t *)mb_parent, (jl_value_t *)b); - gc_try_claim_and_push(mq, b, &nptr); - } jl_value_t *bindings = (jl_value_t *)jl_atomic_load_relaxed(&mb_parent->bindings); gc_assert_parent_validity((jl_value_t *)mb_parent, bindings); gc_try_claim_and_push(mq, bindings, &nptr); @@ -2423,7 +2414,6 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ uint8_t bits = (gc_old(o->header) && !mark_reset_age) ? GC_OLD_MARKED : GC_MARKED; int update_meta = __likely(!meta_updated && !gc_verifying); int foreign_alloc = 0; - // directly point at eyt_obj_in_img to encourage inlining if (update_meta && o->bits.in_image) { foreign_alloc = 1; update_meta = 0; @@ -2462,13 +2452,8 @@ FORCE_INLINE void gc_mark_outrefs(jl_ptls_t ptls, jl_gc_markqueue_t *mq, void *_ else if (foreign_alloc) objprofile_count(jl_module_type, bits == GC_OLD_MARKED, sizeof(jl_module_t)); jl_module_t *mb_parent = (jl_module_t *)new_obj; - jl_svec_t *bindings = jl_atomic_load_relaxed(&mb_parent->bindings); - jl_binding_t **table = (jl_binding_t**)jl_svec_data(bindings); - size_t bsize = jl_svec_len(bindings); - uintptr_t nptr = ((bsize + mb_parent->usings.len + 1) << 2) | (bits & GC_OLD); - jl_binding_t **mb_begin = table + 1; - jl_binding_t **mb_end = table + bsize; - gc_mark_module_binding(ptls, mb_parent, mb_begin, mb_end, nptr, bits); + uintptr_t nptr = ((mb_parent->usings.len + 1) << 2) | (bits & GC_OLD); + gc_mark_module_binding(ptls, mb_parent, nptr, bits); } else if (vtag == jl_task_tag << 4) { if (update_meta) @@ -3729,11 +3714,12 @@ JL_DLLEXPORT void *jl_gc_counted_realloc_with_old_size(void *p, size_t old, size int64_t diff = sz - old; if (diff < 0) { + diff = -diff; uint64_t free_acc = jl_atomic_load_relaxed(&ptls->gc_num.free_acc); if (free_acc + diff < 16*1024) - jl_atomic_store_relaxed(&ptls->gc_num.free_acc, free_acc + (-diff)); + jl_atomic_store_relaxed(&ptls->gc_num.free_acc, free_acc + diff); else { - jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, -(free_acc + (-diff))); + jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, -(free_acc + diff)); jl_atomic_store_relaxed(&ptls->gc_num.free_acc, 0); } } @@ -3865,11 +3851,12 @@ static void *gc_managed_realloc_(jl_ptls_t ptls, void *d, size_t sz, size_t olds int64_t diff = allocsz - oldsz; if (diff < 0) { + diff = -diff; uint64_t free_acc = jl_atomic_load_relaxed(&ptls->gc_num.free_acc); if (free_acc + diff < 16*1024) - jl_atomic_store_relaxed(&ptls->gc_num.free_acc, free_acc + (-diff)); + jl_atomic_store_relaxed(&ptls->gc_num.free_acc, free_acc + diff); else { - jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, -(free_acc + (-diff))); + jl_atomic_fetch_add_relaxed(&gc_heap_stats.heap_size, -(free_acc + diff)); jl_atomic_store_relaxed(&ptls->gc_num.free_acc, 0); } } diff --git a/src/gc.h b/src/gc.h index 1328ef88c2d7c..90943cba84d4b 100644 --- a/src/gc.h +++ b/src/gc.h @@ -146,6 +146,8 @@ typedef struct _mallocarray_t { // pool page metadata typedef struct _jl_gc_pagemeta_t { + // next metadata structure in per-thread list + // or in one of the `jl_gc_global_page_pool_t` struct _jl_gc_pagemeta_t *next; // index of pool that owns this page uint8_t pool_n; @@ -203,6 +205,11 @@ STATIC_INLINE void gc_backoff(int *i) JL_NOTSAFEPOINT // Lock-free stack implementation taken // from Herlihy's "The Art of Multiprocessor Programming" +// XXX: this is not a general-purpose lock-free stack. We can +// get away with just using a CAS and not implementing some ABA +// prevention mechanism since once a node is popped from the +// `jl_gc_global_page_pool_t`, it may only be pushed back to them +// in the sweeping phase, which is serial STATIC_INLINE void push_lf_page_metadata_back(jl_gc_global_page_pool_t *pool, jl_gc_pagemeta_t *elt) JL_NOTSAFEPOINT { diff --git a/src/gf.c b/src/gf.c index 1e119a1054aa3..e8cdb493026b0 100644 --- a/src/gf.c +++ b/src/gf.c @@ -2130,7 +2130,10 @@ JL_DLLEXPORT void jl_method_table_insert(jl_methtable_t *mt, jl_method_t *method int replaced_edge; if (invokeTypes) { // n.b. normally we must have mi.specTypes <: invokeTypes <: m.sig (though it might not strictly hold), so we only need to check the other subtypes - replaced_edge = jl_subtype(invokeTypes, type) && is_replacing(ambig, type, m, d, n, invokeTypes, NULL, morespec); + if (jl_egal(invokeTypes, caller->def.method->sig)) + replaced_edge = 0; // if invokeTypes == m.sig, then the only way to change this invoke is to replace the method itself + else + replaced_edge = jl_subtype(invokeTypes, type) && is_replacing(ambig, type, m, d, n, invokeTypes, NULL, morespec); } else { replaced_edge = replaced_dispatch; diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index fc30072b2f89a..1d51a0983d982 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -1594,6 +1594,9 @@ struct JuliaOJIT::DLSymOptimizer { auto libname = cast(GV->getInitializer())->getAsCString(); addr = lookup(libname.data(), fname.data()); } else { + // Can happen if we fail the compile time dlfind i.e when we try a symbol that doesn't exist in libc + if (dyn_cast(libarg)) + continue; assert(cast(libarg)->getOpcode() == Instruction::IntToPtr && "libarg should be either a global variable or a integer index!"); libarg = cast(libarg)->getOperand(0); auto libidx = cast(libarg)->getZExtValue(); diff --git a/src/jlapi.c b/src/jlapi.c index 0dffaac627288..eeef60ef0c24b 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -576,16 +576,19 @@ static NOINLINE int true_main(int argc, char *argv[]) if (start_client) { jl_task_t *ct = jl_current_task; + int ret = 1; JL_TRY { size_t last_age = ct->world_age; ct->world_age = jl_get_world_counter(); - jl_apply(&start_client, 1); + jl_value_t *r = jl_apply(&start_client, 1); + jl_typeassert(r, (jl_value_t *)jl_int32_type); + ret = jl_unbox_int32(r); ct->world_age = last_age; } JL_CATCH { jl_no_exc_handler(jl_current_exception(), ct); } - return 0; + return ret; } // run program if specified, otherwise enter REPL diff --git a/src/julia.h b/src/julia.h index a4b61a4ebd913..a96b4a1f5e562 100644 --- a/src/julia.h +++ b/src/julia.h @@ -268,6 +268,7 @@ typedef union __jl_purity_overrides_t { uint8_t ipo_terminates_locally : 1; uint8_t ipo_notaskstate : 1; uint8_t ipo_inaccessiblememonly : 1; + uint8_t ipo_noub : 1; } overrides; uint8_t bits; } _jl_purity_overrides_t; diff --git a/src/julia_internal.h b/src/julia_internal.h index a61951a53ec91..1a0373f0f9131 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -209,8 +209,17 @@ JL_DLLEXPORT void jl_unlock_profile_wr(void) JL_NOTSAFEPOINT JL_NOTSAFEPOINT_LEA static inline uint64_t cycleclock(void) JL_NOTSAFEPOINT { #if defined(_CPU_X86_64_) + // This is nopl 0(%rax, %rax, 1), but assembler are incosistent about whether + // they emit that as a 4 or 5 byte sequence and we need to be guaranteed to use + // the 5 byte one. +#define NOP5_OVERRIDE_NOP ".byte 0x0f, 0x1f, 0x44, 0x00, 0x00\n\t" uint64_t low, high; - __asm__ volatile("rdtsc" : "=a"(low), "=d"(high)); + // This instruction sequence is promised by rr to be patchable. rr can usually + // also patch `rdtsc` in regular code, but without the preceeding nop, there could + // be an interfering branch into the middle of rr's patch region. Using this + // sequence prevents a massive rr-induced slowdown if the compiler happens to emit + // an unlucky pattern. See https://github.com/rr-debugger/rr/pull/3580. + __asm__ volatile(NOP5_OVERRIDE_NOP "rdtsc" : "=a"(low), "=d"(high)); return (high << 32) | low; #elif defined(_CPU_X86_) int64_t ret; diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index a35c6b9ebdb07..364888dc3ac12 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -2528,17 +2528,28 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S, bool *CFGModified) { if (CFGModified) { *CFGModified = true; } + auto DebugInfoMeta = F.getParent()->getModuleFlag("julia.debug_level"); + int debug_info = 1; + if (DebugInfoMeta != nullptr) { + debug_info = cast(cast(DebugInfoMeta)->getValue())->getZExtValue(); + } + IRBuilder<> builder(CI); builder.SetCurrentDebugLocation(CI->getDebugLoc()); - auto parBits = builder.CreateAnd(EmitLoadTag(builder, T_size, parent), 3); - auto parOldMarked = builder.CreateICmpEQ(parBits, ConstantInt::get(T_size, 3)); + auto parBits = builder.CreateAnd(EmitLoadTag(builder, T_size, parent), GC_OLD_MARKED); + setName(parBits, "parent_bits", debug_info); + auto parOldMarked = builder.CreateICmpEQ(parBits, ConstantInt::get(T_size, GC_OLD_MARKED)); + setName(parOldMarked, "parent_old_marked", debug_info); auto mayTrigTerm = SplitBlockAndInsertIfThen(parOldMarked, CI, false); builder.SetInsertPoint(mayTrigTerm); + setName(mayTrigTerm->getParent(), "may_trigger_wb", debug_info); Value *anyChldNotMarked = NULL; for (unsigned i = 1; i < CI->arg_size(); i++) { Value *child = CI->getArgOperand(i); - Value *chldBit = builder.CreateAnd(EmitLoadTag(builder, T_size, child), 1); - Value *chldNotMarked = builder.CreateICmpEQ(chldBit, ConstantInt::get(T_size, 0)); + Value *chldBit = builder.CreateAnd(EmitLoadTag(builder, T_size, child), GC_MARKED); + setName(chldBit, "child_bit", debug_info); + Value *chldNotMarked = builder.CreateICmpEQ(chldBit, ConstantInt::get(T_size, 0),"child_not_marked"); + setName(chldNotMarked, "child_not_marked", debug_info); anyChldNotMarked = anyChldNotMarked ? builder.CreateOr(anyChldNotMarked, chldNotMarked) : chldNotMarked; } assert(anyChldNotMarked); // handled by all_of test above @@ -2546,6 +2557,7 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S, bool *CFGModified) { SmallVector Weights{1, 9}; auto trigTerm = SplitBlockAndInsertIfThen(anyChldNotMarked, mayTrigTerm, false, MDB.createBranchWeights(Weights)); + setName(trigTerm->getParent(), "trigger_wb", debug_info); builder.SetInsertPoint(trigTerm); if (CI->getCalledOperand() == write_barrier_func) { builder.CreateCall(getOrDeclare(jl_intrinsics::queueGCRoot), parent); diff --git a/src/llvm-pass-helpers.cpp b/src/llvm-pass-helpers.cpp index 39d37cee3928b..543cdb137e570 100644 --- a/src/llvm-pass-helpers.cpp +++ b/src/llvm-pass-helpers.cpp @@ -311,3 +311,10 @@ namespace jl_well_known { return addGCAllocAttributes(allocTypedFunc); }); } + +void setName(llvm::Value *V, const llvm::Twine &Name, int debug_info) +{ + if (debug_info >= 2 && !llvm::isa(V)) { + V->setName(Name); + } +} diff --git a/src/llvm-pass-helpers.h b/src/llvm-pass-helpers.h index 727f463dc50ef..97cc2a03415b2 100644 --- a/src/llvm-pass-helpers.h +++ b/src/llvm-pass-helpers.h @@ -154,4 +154,6 @@ namespace jl_well_known { extern const WellKnownFunctionDescription GCAllocTyped; } +void setName(llvm::Value *V, const llvm::Twine &Name, int debug_info); + #endif diff --git a/src/method.c b/src/method.c index 06a05361a927d..00eae940f9f88 100644 --- a/src/method.c +++ b/src/method.c @@ -328,7 +328,7 @@ static void jl_code_info_set_ir(jl_code_info_t *li, jl_expr_t *ir) else if (ma == (jl_value_t*)jl_no_constprop_sym) li->constprop = 2; else if (jl_is_expr(ma) && ((jl_expr_t*)ma)->head == jl_purity_sym) { - if (jl_expr_nargs(ma) == 7) { + if (jl_expr_nargs(ma) == 8) { li->purity.overrides.ipo_consistent = jl_unbox_bool(jl_exprarg(ma, 0)); li->purity.overrides.ipo_effect_free = jl_unbox_bool(jl_exprarg(ma, 1)); li->purity.overrides.ipo_nothrow = jl_unbox_bool(jl_exprarg(ma, 2)); @@ -336,6 +336,7 @@ static void jl_code_info_set_ir(jl_code_info_t *li, jl_expr_t *ir) li->purity.overrides.ipo_terminates_locally = jl_unbox_bool(jl_exprarg(ma, 4)); li->purity.overrides.ipo_notaskstate = jl_unbox_bool(jl_exprarg(ma, 5)); li->purity.overrides.ipo_inaccessiblememonly = jl_unbox_bool(jl_exprarg(ma, 6)); + li->purity.overrides.ipo_noub = jl_unbox_bool(jl_exprarg(ma, 7)); } } else diff --git a/src/partr.c b/src/partr.c index 32291243443b1..75d6d832fe78f 100644 --- a/src/partr.c +++ b/src/partr.c @@ -70,7 +70,7 @@ JL_DLLEXPORT int jl_set_task_tid(jl_task_t *task, int16_t tid) JL_NOTSAFEPOINT JL_DLLEXPORT int jl_set_task_threadpoolid(jl_task_t *task, int8_t tpid) JL_NOTSAFEPOINT { - if (tpid < 0 || tpid >= jl_n_threadpools) + if (tpid < -1 || tpid >= jl_n_threadpools) return 0; task->threadpoolid = tpid; return 1; diff --git a/src/pipeline.cpp b/src/pipeline.cpp index 3ed40f018f23f..a68ed979492e7 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -12,6 +12,7 @@ // analysis passes #include #include +#include #include #include #include @@ -38,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -372,6 +374,10 @@ static void buildEarlyOptimizerPipeline(ModulePassManager &MPM, PassBuilder *PB, } MPM.addPass(createModuleToPostOrderCGSCCPassAdaptor(std::move(CGPM))); } + if (O.getSpeedupLevel() >= 2) { + MPM.addPass(RequireAnalysisPass()); + } + // MPM.addPass(createModuleToFunctionPassAdaptor(InvalidateAnalysisPass())); if (options.dump_native) { MPM.addPass(StripDeadPrototypesPass()); JULIA_PASS(MPM.addPass(MultiVersioningPass(options.external_use))); @@ -395,6 +401,7 @@ static void buildEarlyOptimizerPipeline(ModulePassManager &MPM, PassBuilder *PB, invokePeepholeEPCallbacks(FPM, PB, O); MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); } + MPM.addPass(GlobalDCEPass()); MPM.addPass(AfterEarlyOptimizationMarkerPass()); } diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index bf1a830b608de..ed80a1f6278f4 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -505,19 +505,17 @@ static void jl_collect_edges(jl_array_t *edges, jl_array_t *ext_targets, jl_arra size_t max_valid = ~(size_t)0; if (invokeTypes) { assert(jl_is_method_instance(callee)); - jl_methtable_t *mt = jl_method_get_table(((jl_method_instance_t*)callee)->def.method); - if ((jl_value_t*)mt == jl_nothing) { - callee_ids = NULL; // invalid - break; - } - else { + jl_method_t *m = ((jl_method_instance_t*)callee)->def.method; + matches = (jl_value_t*)m; // valid because there is no method replacement permitted +#ifndef NDEBUG + jl_methtable_t *mt = jl_method_get_table(m); + if ((jl_value_t*)mt != jl_nothing) { matches = jl_gf_invoke_lookup_worlds(invokeTypes, (jl_value_t*)mt, world, &min_valid, &max_valid); - if (matches == jl_nothing) { - callee_ids = NULL; // invalid - break; + if (matches != jl_nothing) { + assert(m == ((jl_method_match_t*)matches)->method); } - matches = (jl_value_t*)((jl_method_match_t*)matches)->method; } +#endif } else { if (jl_is_method_instance(callee)) { @@ -855,19 +853,27 @@ static jl_array_t *jl_verify_edges(jl_array_t *targets, size_t minworld) size_t max_valid = ~(size_t)0; if (invokesig) { assert(callee && "unsupported edge"); - jl_methtable_t *mt = jl_method_get_table(((jl_method_instance_t*)callee)->def.method); - if ((jl_value_t*)mt == jl_nothing) { - max_valid = 0; + jl_method_t *m = ((jl_method_instance_t*)callee)->def.method; + if (jl_egal(invokesig, m->sig)) { + // the invoke match is `m` for `m->sig`, unless `m` is invalid + if (m->deleted_world < max_valid) + max_valid = 0; } else { - matches = jl_gf_invoke_lookup_worlds(invokesig, (jl_value_t*)mt, minworld, &min_valid, &max_valid); - if (matches == jl_nothing) { - max_valid = 0; + jl_methtable_t *mt = jl_method_get_table(m); + if ((jl_value_t*)mt == jl_nothing) { + max_valid = 0; } else { - matches = (jl_value_t*)((jl_method_match_t*)matches)->method; - if (matches != expected) { - max_valid = 0; + matches = jl_gf_invoke_lookup_worlds(invokesig, (jl_value_t*)mt, minworld, &min_valid, &max_valid); + if (matches == jl_nothing) { + max_valid = 0; + } + else { + matches = (jl_value_t*)((jl_method_match_t*)matches)->method; + if (matches != expected) { + max_valid = 0; + } } } } diff --git a/src/threading.c b/src/threading.c index e2eb686e3061a..4faa8a0a2dc46 100644 --- a/src/threading.c +++ b/src/threading.c @@ -332,7 +332,7 @@ JL_DLLEXPORT int8_t jl_threadpoolid(int16_t tid) JL_NOTSAFEPOINT if (tid < n) return (int8_t)i; } - return 0; // everything else uses threadpool 0 (though does not become part of any threadpool) + return -1; // everything else uses threadpool -1 (does not belong to any threadpool) } jl_ptls_t jl_init_threadtls(int16_t tid) diff --git a/stdlib/ArgTools.version b/stdlib/ArgTools.version index 0ae273bb18db6..ad2febe81e46e 100644 --- a/stdlib/ArgTools.version +++ b/stdlib/ArgTools.version @@ -1,4 +1,4 @@ ARGTOOLS_BRANCH = master -ARGTOOLS_SHA1 = 08b11b2707593d4d7f92e5f1b9dba7668285ff82 +ARGTOOLS_SHA1 = 4eccde45ddc27e4f7fc9094b2861c684e062adb2 ARGTOOLS_GIT_URL := https://github.com/JuliaIO/ArgTools.jl.git ARGTOOLS_TAR_URL = https://api.github.com/repos/JuliaIO/ArgTools.jl/tarball/$1 diff --git a/stdlib/Dates/src/periods.jl b/stdlib/Dates/src/periods.jl index 876680dd456a5..c1d94d3d62321 100644 --- a/stdlib/Dates/src/periods.jl +++ b/stdlib/Dates/src/periods.jl @@ -443,8 +443,8 @@ Base.isless(x::CompoundPeriod, y::Period) = x < CompoundPeriod(y) Base.isless(x::CompoundPeriod, y::CompoundPeriod) = tons(x) < tons(y) # truncating conversions to milliseconds, nanoseconds and days: # overflow can happen for periods longer than ~300,000 years -toms(c::Nanosecond) = div(value(c), 1000000) -toms(c::Microsecond) = div(value(c), 1000) +toms(c::Nanosecond) = div(value(c), 1000000, RoundNearest) +toms(c::Microsecond) = div(value(c), 1000, RoundNearest) toms(c::Millisecond) = value(c) toms(c::Second) = 1000 * value(c) toms(c::Minute) = 60000 * value(c) diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index a96d183bbc590..7391c277b0718 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -142,8 +142,28 @@ abstract type AbstractDateTime <: TimeType end """ DateTime -`DateTime` wraps a `UTInstant{Millisecond}` and interprets it according to the proleptic -Gregorian calendar. +`DateTime` represents a point in time according to the proleptic Gregorian calendar. +The finest resolution of the time is millisecond (i.e., microseconds or +nanoseconds cannot be represented by this type). The type supports fixed-point +arithmetic, and thus is prone to underflowing (and overflowing). A notable +consequence is rounding when adding a `Microsecond` or a `Nanosecond`: + +```jldoctest +julia> dt = DateTime(2023, 8, 19, 17, 45, 32, 900) +2023-08-19T17:45:32.900 + +julia> dt + Millisecond(1) +2023-08-19T17:45:32.901 + +julia> dt + Microsecond(1000) # 1000us == 1ms +2023-08-19T17:45:32.901 + +julia> dt + Microsecond(999) # 999us rounded to 1000us +2023-08-19T17:45:32.901 + +julia> dt + Microsecond(1499) # 1499 rounded to 1000us +2023-08-19T17:45:32.901 +``` """ struct DateTime <: AbstractDateTime instant::UTInstant{Millisecond} diff --git a/stdlib/Dates/test/arithmetic.jl b/stdlib/Dates/test/arithmetic.jl index 2e684815a3c86..110eea6d00235 100644 --- a/stdlib/Dates/test/arithmetic.jl +++ b/stdlib/Dates/test/arithmetic.jl @@ -263,6 +263,24 @@ end @test dt - Dates.Millisecond(1) == Dates.DateTime(1972, 6, 30, 23, 59, 58, 999) @test dt + Dates.Millisecond(-1) == Dates.DateTime(1972, 6, 30, 23, 59, 58, 999) end + @testset "DateTime-Microsecond arithmetic" begin + dt = Dates.DateTime(1999, 12, 27) + @test dt + Dates.Microsecond(1) == dt + @test dt + Dates.Microsecond(501) == Dates.DateTime(1999, 12, 27, 0, 0, 0, 1) + @test dt + Dates.Microsecond(1499) == Dates.DateTime(1999, 12, 27, 0, 0, 0, 1) + @test dt - Dates.Microsecond(1) == dt + @test dt - Dates.Microsecond(501) == Dates.DateTime(1999, 12, 26, 23, 59, 59, 999) + @test dt - Dates.Microsecond(1499) == Dates.DateTime(1999, 12, 26, 23, 59, 59, 999) + end + @testset "DateTime-Nanosecond arithmetic" begin + dt = Dates.DateTime(1999, 12, 27) + @test dt + Dates.Nanosecond(1) == dt + @test dt + Dates.Nanosecond(500_001) == Dates.DateTime(1999, 12, 27, 0, 0, 0, 1) + @test dt + Dates.Nanosecond(1_499_999) == Dates.DateTime(1999, 12, 27, 0, 0, 0, 1) + @test dt - Dates.Nanosecond(1) == dt + @test dt - Dates.Nanosecond(500_001) == Dates.DateTime(1999, 12, 26, 23, 59, 59, 999) + @test dt - Dates.Nanosecond(1_499_999) == Dates.DateTime(1999, 12, 26, 23, 59, 59, 999) + end end @testset "Date arithmetic" begin @testset "Date-Year arithmetic" begin diff --git a/stdlib/Dates/test/periods.jl b/stdlib/Dates/test/periods.jl index 82e7ca27ab6f5..7cebfc55e7735 100644 --- a/stdlib/Dates/test/periods.jl +++ b/stdlib/Dates/test/periods.jl @@ -329,6 +329,14 @@ end @test Dates.default(Dates.Nanosecond) == zero(Dates.Nanosecond) end @testset "Conversions" begin + @test Dates.toms(1499 * us) == 1 + @test Dates.toms(501 * us) == 1 + @test Dates.toms(us) == 0 + + @test Dates.toms(1_499_999 * ns) == 1 + @test Dates.toms(500_001 * ns) == 1 + @test Dates.toms(ns) == 0 + @test Dates.toms(ms) == Dates.value(Dates.Millisecond(ms)) == 1 @test Dates.toms(s) == Dates.value(Dates.Millisecond(s)) == 1000 @test Dates.toms(mi) == Dates.value(Dates.Millisecond(mi)) == 60000 diff --git a/stdlib/Dates/test/types.jl b/stdlib/Dates/test/types.jl index 3bd11f80540d7..35a793867dc5a 100644 --- a/stdlib/Dates/test/types.jl +++ b/stdlib/Dates/test/types.jl @@ -74,6 +74,12 @@ ms = Dates.Millisecond(1) Dates.Hour(4), Dates.Second(10)) == Dates.DateTime(1, 2, 1, 4, 0, 10) end +@testset "DateTime construction from Date and Time" begin + @test Dates.DateTime(Dates.Date(2023, 08, 07), Dates.Time(12)) == Dates.DateTime(2023, 08, 07, 12, 0, 0, 0) + @test_throws InexactError Dates.DateTime(Dates.Date(2023, 08, 07), Dates.Time(12, 0, 0, 0, 42)) + @test_throws InexactError Dates.DateTime(Dates.Date(2023, 08, 07), Dates.Time(12, 0, 0, 0, 0, 42)) +end + @testset "Date construction by parts" begin test = Dates.Date(Dates.UTD(734869)) @test Dates.Date(2013) == test diff --git a/stdlib/Distributed/src/process_messages.jl b/stdlib/Distributed/src/process_messages.jl index 7bbf7cfde943b..e68e05b9db52b 100644 --- a/stdlib/Distributed/src/process_messages.jl +++ b/stdlib/Distributed/src/process_messages.jl @@ -210,6 +210,9 @@ function message_handler_loop(r_stream::IO, w_stream::IO, incoming::Bool) handle_msg(msg, header, r_stream, w_stream, version) end catch e + werr = worker_from_id(wpid) + oldstate = werr.state + # Check again as it may have been set in a message handler but not propagated to the calling block above if wpid < 1 wpid = worker_id_from_socket(r_stream) @@ -219,8 +222,6 @@ function message_handler_loop(r_stream::IO, w_stream::IO, incoming::Bool) println(stderr, e, CapturedException(e, catch_backtrace())) println(stderr, "Process($(myid())) - Unknown remote, closing connection.") elseif !(wpid in map_del_wrkr) - werr = worker_from_id(wpid) - oldstate = werr.state set_worker_state(werr, W_TERMINATED) # If unhandleable error occurred talking to pid 1, exit diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 37359fd1074f8..4a4907940891e 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -150,6 +150,8 @@ end r end +axes(D::Diagonal) = (ax = axes(D.diag, 1); (ax, ax)) + @inline function Base.isstored(D::Diagonal, i::Int, j::Int) @boundscheck checkbounds(D, i, j) if i == j diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index 77799ea888d91..a17fc49abf8b6 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -1284,16 +1284,17 @@ false julia> istriu(a, -1) true -julia> b = [1 im; 0 -1] -2ร—2 Matrix{Complex{Int64}}: - 1+0im 0+1im - 0+0im -1+0im - -julia> istriu(b) -true +julia> c = [1 1 1; 1 1 1; 0 1 1] +3ร—3 Matrix{Int64}: + 1 1 1 + 1 1 1 + 0 1 1 -julia> istriu(b, 1) +julia> istriu(c) false + +julia> istriu(c, -1) +true ``` """ function istriu(A::AbstractMatrix, k::Integer = 0) @@ -1328,16 +1329,17 @@ false julia> istril(a, 1) true -julia> b = [1 0; -im -1] -2ร—2 Matrix{Complex{Int64}}: - 1+0im 0+0im - 0-1im -1+0im - -julia> istril(b) -true +julia> c = [1 1 0; 1 1 1; 1 1 1] +3ร—3 Matrix{Int64}: + 1 1 0 + 1 1 1 + 1 1 1 -julia> istril(b, -1) +julia> istril(c) false + +julia> istril(c, 1) +true ``` """ function istril(A::AbstractMatrix, k::Integer = 0) diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index 61045a957cfed..f4c67e901282c 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -460,6 +460,12 @@ Random.seed!(1) end end +@testset "axes" begin + v = OffsetArray(1:3) + D = Diagonal(v) + @test axes(D) isa NTuple{2,typeof(axes(v,1))} +end + @testset "rdiv! (#40887)" begin @test rdiv!(Matrix(Diagonal([2.0, 3.0])), Diagonal(2:3)) == Diagonal([1.0, 1.0]) @test rdiv!(fill(3.0, 3, 3), 3.0I(3)) == ones(3,3) diff --git a/stdlib/Mmap/src/Mmap.jl b/stdlib/Mmap/src/Mmap.jl index 629f53e8371ed..9dd02d5aa9d04 100644 --- a/stdlib/Mmap/src/Mmap.jl +++ b/stdlib/Mmap/src/Mmap.jl @@ -208,18 +208,43 @@ function mmap(io::IO, mmaplen = (offset - offset_page) + len file_desc = gethandle(io) + szfile = convert(Csize_t, len + offset) + requestedSizeLarger = false + if !(io isa Mmap.Anonymous) + @static if !Sys.isapple() + requestedSizeLarger = szfile > filesize(io) + end + end # platform-specific mmapping @static if Sys.isunix() prot, flags, iswrite = settings(file_desc, shared) - iswrite && grow && grow!(io, offset, len) + if requestedSizeLarger + if iswrite + if grow + grow!(io, offset, len) + else + throw(ArgumentError("requested size $szfile larger than file size $(filesize(io)), but requested not to grow")) + end + else + throw(ArgumentError("unable to increase file size to $szfile due to read-only permissions")) + end + end + @static if Sys.isapple() + iswrite && grow && grow!(io, offset, len) + end # mmap the file ptr = ccall(:jl_mmap, Ptr{Cvoid}, (Ptr{Cvoid}, Csize_t, Cint, Cint, RawFD, Int64), C_NULL, mmaplen, prot, flags, file_desc, offset_page) systemerror("memory mapping failed", reinterpret(Int, ptr) == -1) else name, readonly, create = settings(io) - szfile = convert(Csize_t, len + offset) - readonly && szfile > filesize(io) && throw(ArgumentError("unable to increase file size to $szfile due to read-only permissions")) + if requestedSizeLarger + if readonly + throw(ArgumentError("unable to increase file size to $szfile due to read-only permissions")) + elseif !grow + throw(ArgumentError("requested size $szfile larger than file size $(filesize(io)), but requested not to grow")) + end + end handle = create ? ccall(:CreateFileMappingW, stdcall, Ptr{Cvoid}, (OS_HANDLE, Ptr{Cvoid}, DWORD, DWORD, DWORD, Cwstring), file_desc, C_NULL, readonly ? PAGE_READONLY : PAGE_READWRITE, szfile >> 32, szfile & typemax(UInt32), name) : ccall(:OpenFileMappingW, stdcall, Ptr{Cvoid}, (DWORD, Cint, Cwstring), diff --git a/stdlib/NetworkOptions.version b/stdlib/NetworkOptions.version index 64d3fab9d7bf4..c9165c2dfc32c 100644 --- a/stdlib/NetworkOptions.version +++ b/stdlib/NetworkOptions.version @@ -1,4 +1,4 @@ NETWORKOPTIONS_BRANCH = master -NETWORKOPTIONS_SHA1 = f7bbeb66f05fc651adb12758b650e8630a998fbd +NETWORKOPTIONS_SHA1 = 976e51a48abb4e09356f1979c3cde8fbc0852e2e NETWORKOPTIONS_GIT_URL := https://github.com/JuliaLang/NetworkOptions.jl.git NETWORKOPTIONS_TAR_URL = https://api.github.com/repos/JuliaLang/NetworkOptions.jl/tarball/$1 diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 7ded39b1dc967..851af2587e039 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = master -PKG_SHA1 = 08e1eb6ed2354102e22181bad5cfe04c701656c0 +PKG_SHA1 = 047734e4c7e2b5e99622b4803ec4ad0d49f1d1d3 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index fe543ad2d512f..5a64d372cb386 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -1506,4 +1506,7 @@ end import .Numbered.numbered_prompt! +# TODO: Move more of this implementation into REPL. +main(ARGS) = Base.exec_options(Base.JLOptions()) + end # module diff --git a/stdlib/SHA.version b/stdlib/SHA.version index f2242a336c6fe..f22bb33dc7ea2 100644 --- a/stdlib/SHA.version +++ b/stdlib/SHA.version @@ -1,4 +1,4 @@ SHA_BRANCH = master -SHA_SHA1 = 2d1f84e6f8417a1a368de48318640d948b023e7a +SHA_SHA1 = aaf2df61ff8c3898196587a375d3cf213bd40b41 SHA_GIT_URL := https://github.com/JuliaCrypto/SHA.jl.git SHA_TAR_URL = https://api.github.com/repos/JuliaCrypto/SHA.jl/tarball/$1 diff --git a/stdlib/SparseArrays.version b/stdlib/SparseArrays.version index 295400bbd3b57..77a3ec698fb1e 100644 --- a/stdlib/SparseArrays.version +++ b/stdlib/SparseArrays.version @@ -1,4 +1,4 @@ SPARSEARRAYS_BRANCH = main -SPARSEARRAYS_SHA1 = 99c99b4521eb19a7973643d1aa4d7b1e4d50a6db +SPARSEARRAYS_SHA1 = 54f4b39d1cd34a908ff4fdfcd052a38fe23f6555 SPARSEARRAYS_GIT_URL := https://github.com/JuliaSparse/SparseArrays.jl.git SPARSEARRAYS_TAR_URL = https://api.github.com/repos/JuliaSparse/SparseArrays.jl/tarball/$1 diff --git a/stdlib/Tar.version b/stdlib/Tar.version index 44e829b5fea54..f1c361eff972e 100644 --- a/stdlib/Tar.version +++ b/stdlib/Tar.version @@ -1,4 +1,4 @@ TAR_BRANCH = master -TAR_SHA1 = ff55460f4d329949661a33e6c8168ce6d890676c +TAR_SHA1 = 81888a33704b233a2ad6f82f84456a1dd82c87f0 TAR_GIT_URL := https://github.com/JuliaIO/Tar.jl.git TAR_TAR_URL = https://api.github.com/repos/JuliaIO/Tar.jl/tarball/$1 diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 622c696b383a0..25418f1f67ec4 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -155,14 +155,16 @@ struct Fail <: Result context::Union{Nothing, String} source::LineNumberNode message_only::Bool - function Fail(test_type::Symbol, orig_expr, data, value, context, source::LineNumberNode, message_only::Bool) + backtrace::Union{Nothing, String} + function Fail(test_type::Symbol, orig_expr, data, value, context, source::LineNumberNode, message_only::Bool, backtrace=nothing) return new(test_type, string(orig_expr), data === nothing ? nothing : string(data), string(isa(data, Type) ? typeof(value) : value), context, source, - message_only) + message_only, + backtrace) end end @@ -184,6 +186,11 @@ function Base.show(io::IO, t::Fail) else print(io, "\n Expected: ", data) print(io, "\n Thrown: ", value) + print(io, "\n") + if t.backtrace !== nothing + # Capture error message and indent to match + join(io, (" " * line for line in split(t.backtrace, "\n")), "\n") + end end elseif t.test_type === :test_throws_nothing # An exception was expected, but no exception was thrown @@ -768,7 +775,7 @@ macro test_throws(extype, ex) if $(esc(extype)) != InterruptException && _e isa InterruptException rethrow() end - Threw(_e, nothing, $(QuoteNode(__source__))) + Threw(_e, Base.current_exceptions(), $(QuoteNode(__source__))) end end return :(do_test_throws($result, $orig_ex, $(esc(extype)))) @@ -825,7 +832,22 @@ function do_test_throws(result::ExecutionResult, orig_expr, extype) if success testres = Pass(:test_throws, orig_expr, extype, exc, result.source, message_only) else - testres = Fail(:test_throws_wrong, orig_expr, extype, exc, nothing, result.source, message_only) + if result.backtrace !== nothing + bt = scrub_exc_stack(result.backtrace, nothing, extract_file(result.source)) + bt_str = try # try the latest world for this, since we might have eval'd new code for show + Base.invokelatest(sprint, Base.show_exception_stack, bt; context=stdout) + catch ex + "#=ERROR showing exception stack=# " * + try + sprint(Base.showerror, ex, catch_backtrace(); context=stdout) + catch + "of type " * string(typeof(ex)) + end + end + else + bt_str = nothing + end + testres = Fail(:test_throws_wrong, orig_expr, extype, exc, nothing, result.source, message_only, bt_str) end else testres = Fail(:test_throws_nothing, orig_expr, extype, nothing, nothing, result.source, false) diff --git a/test/arrayops.jl b/test/arrayops.jl index 770cec3705038..ba02847094b0e 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -604,6 +604,15 @@ end @testset "issue 43078" begin @test_throws TypeError findall([1]) end + + @testset "issue #46425" begin + counter = 0 + function pred46425(x) + counter += 1 + counter < 4 && x + end + @test findall(pred46425, [false, false, true, true]) == [3] + end end @testset "find with Matrix" begin A = [1 2 0; 3 4 0] diff --git a/test/ccall.jl b/test/ccall.jl index d55ba03799039..6e8269a36225d 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -1922,10 +1922,15 @@ function somefunction_not_found() ccall((:somefunction, libfrobozz), Cvoid, ()) end +function somefunction_not_found_libc() + ccall(:test,Int,()) +end + @testset "library not found" begin if Sys.islinux() @test_throws "could not load symbol \"somefunction\"" somefunction_not_found() else @test_throws "could not load library \"\"" somefunction_not_found() end + @test_throws "could not load symbol \"test\"" somefunction_not_found_libc() end diff --git a/test/choosetests.jl b/test/choosetests.jl index 18af88ea191e9..c38817bb4eeb9 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -19,7 +19,7 @@ const TESTNAMES = [ "mpfr", "broadcast", "complex", "floatapprox", "stdlib", "reflection", "regex", "float16", "combinatorics", "sysinfo", "env", "rounding", "ranges", "mod2pi", - "euler", "show", "client", + "euler", "show", "client", "terminfo", "errorshow", "sets", "goto", "llvmcall", "llvmcall2", "ryu", "some", "meta", "stacktraces", "docs", "gc", "misc", "threads", "stress", "binaryplatforms", "atexit", diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index b29904fb5eb6c..86c2994655295 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -977,3 +977,15 @@ end #heap-size-hint, we reserve 250 MB for non GC memory (llvm, etc.) @test readchomp(`$(Base.julia_cmd()) --startup-file=no --heap-size-hint=500M -e "println(@ccall jl_gc_get_max_memory()::UInt64)"`) == "$((500-250)*1024*1024)" end + +## `Main.main` entrypoint + +# Basic usage +@test readchomp(`$(Base.julia_cmd()) -e 'main(ARGS) = println("hello")'`) == "hello" + +# Test ARGS with -e +@test readchomp(`$(Base.julia_cmd()) -e 'main(ARGS) = println(ARGS)' a b`) == repr(["a", "b"]) + +# Test import from module +@test readchomp(`$(Base.julia_cmd()) -e 'module Hello; export main; main(ARGS) = println("hello"); end; using .Hello'`) == "hello" +@test readchomp(`$(Base.julia_cmd()) -e 'module Hello; export main; main(ARGS) = println("hello"); end; import .Hello'`) == "" diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index d79c046103710..edb6546499779 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -6,11 +6,19 @@ const CC = Core.Compiler include("irutils.jl") include("newinterp.jl") + # OverlayMethodTable # ================== using Base.Experimental: @MethodTable, @overlay +# @overlay method with return type annotation +@MethodTable RT_METHOD_DEF +@overlay RT_METHOD_DEF Base.sin(x::Float64)::Float64 = cos(x) +@overlay RT_METHOD_DEF function Base.sin(x::T)::T where T<:AbstractFloat + cos(x) +end + @newinterp MTOverlayInterp @MethodTable OverlayedMT CC.method_table(interp::MTOverlayInterp) = CC.OverlayMethodTable(CC.get_world_counter(interp), OverlayedMT) diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 5f924fa2e99ce..3e236a55af4b2 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -853,3 +853,9 @@ let res = @timed a50317[:b] @test res.bytes == 0 return res end + +# https://github.com/JuliaLang/julia/issues/50964 +@noinline bar50964(x::Core.Const) = Base.inferencebarrier(1) +@noinline bar50964(x::DataType) = Base.inferencebarrier(2) +foo50964(x) = bar50964(Base.inferencebarrier(Core.Const(x))) +foo50964(1) # Shouldn't assert! diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index 60acac844cd7c..3e017feb7cbdc 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -337,7 +337,7 @@ invoke44763(x) = @invoke increase_x44763!(x) end |> only === Int @test x44763 == 0 -# `@inbounds`/`@boundscheck` expression should taint :consistent-cy correctly +# `@inbounds`/`@boundscheck` expression should taint :consistent correctly # https://github.com/JuliaLang/julia/issues/48099 function A1_inbounds() r = 0 @@ -356,7 +356,7 @@ function f_boundscheck_elim(n) # to run the `@inbounds getfield(sin, 1)` that `ntuple` generates. ntuple(x->(@inbounds ()[x]), n) end -@test_broken !Core.Compiler.is_consistent(Base.infer_effects(f_boundscheck_elim, (Int,))) +@test !Core.Compiler.is_noub(Base.infer_effects(f_boundscheck_elim, (Int,))) @test Tuple{} <: only(Base.return_types(f_boundscheck_elim, (Int,))) # Test that purity modeling doesn't accidentally introduce new world age issues @@ -445,10 +445,8 @@ mutable struct SetfieldNothrow end f_setfield_nothrow() = SetfieldNothrow(0).x = 1 let effects = Base.infer_effects(f_setfield_nothrow, ()) - # Technically effect free even though we use the heap, since the - # object doesn't escape, but the compiler doesn't know that. - #@test Core.Compiler.is_effect_free(effects) @test Core.Compiler.is_nothrow(effects) + @test Core.Compiler.is_effect_free(effects) # see EFFECT_FREE_IF_INACCESSIBLEMEMONLY end # even if 2-arg `getfield` may throw, it should be still `:consistent` @@ -460,7 +458,7 @@ end Core.svec(nothing, 1, "foo") end |> Core.Compiler.is_consistent -# fastmath operations are inconsistent +# fastmath operations are in-`:consistent` @test !Core.Compiler.is_consistent(Base.infer_effects((a,b)->@fastmath(a+b), (Float64,Float64))) # issue 46122: @assume_effects for @ccall @@ -894,7 +892,7 @@ end |> Core.Compiler.is_foldable_nothrow @test Base.infer_effects(Tuple{Int64}) do i @inbounds (1,2,3)[i] -end |> !Core.Compiler.is_consistent +end |> !Core.Compiler.is_noub @test Base.infer_effects(Tuple{Tuple{Int64}}) do x @inbounds x[1] diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index c8cff74d3dca9..750fc0fdcea1b 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1970,7 +1970,7 @@ function foo25261() next = f25261(Core.getfield(next, 2)) end end -let opt25261 = code_typed(foo25261, Tuple{}, optimize=false)[1].first.code +let opt25261 = code_typed(foo25261, Tuple{}, optimize=true)[1].first.code i = 1 # Skip to after the branch while !isa(opt25261[i], GotoIfNot) @@ -1978,7 +1978,7 @@ let opt25261 = code_typed(foo25261, Tuple{}, optimize=false)[1].first.code end foundslot = false for expr25261 in opt25261[i:end] - if expr25261 isa Core.Compiler.TypedSlot && expr25261.typ === Tuple{Int, Int} + if expr25261 isa Core.PiNode && expr25261.typ === Tuple{Int, Int} # This should be the assignment to the SSAValue into the getfield # call - make sure it's a TypedSlot foundslot = true @@ -3465,8 +3465,12 @@ end @test Base.return_types(h33768, ()) == Any[Union{}] # constant prop of `Symbol("")` -f_getf_computed_symbol(p) = getfield(p, Symbol("first")) -@test Base.return_types(f_getf_computed_symbol, Tuple{Pair{Int8,String}}) == [Int8] +@test Base.return_types() do + Val(Symbol("julia")) +end |> only == Val{:julia} +@test Base.return_types() do p::Pair{Int8,String} + getfield(p, Symbol("first")) +end |> only == Int8 # issue #33954 struct X33954 @@ -4580,6 +4584,19 @@ end g = Base.ImmutableDict(g, 1=>2) end end |> only === Union{} + + a = Val{Union{}} + a = Core.Compiler.tmerge(Union{a, Val{a}}, a) + @test a == Union{Val{Union{}}, Val{Val{Union{}}}} + a = Core.Compiler.tmerge(Union{a, Val{a}}, a) + @test a == Union{Val{Union{}}, Val{Val{Union{}}}, Val{Union{Val{Union{}}, Val{Val{Union{}}}}}} + a = Core.Compiler.tmerge(Union{a, Val{a}}, a) + @test a == Val + + a = Val{Union{}} + a = Core.Compiler.tmerge(Core.Compiler.JLTypeLattice(), Val{<:a}, a) + @test_broken a != Val{<:Val{Union{}}} + @test_broken a == Val{<:Val} || a == Val end # Test that a function-wise `@max_methods` works as expected diff --git a/test/compiler/irpasses.jl b/test/compiler/irpasses.jl index a8d5df6a24cdf..3638218e0508a 100644 --- a/test/compiler/irpasses.jl +++ b/test/compiler/irpasses.jl @@ -6,7 +6,7 @@ import Core: CodeInfo, Argument, SSAValue, GotoNode, GotoIfNot, PiNode, PhiNode, QuoteNode, ReturnNode -include(normpath(@__DIR__, "irutils.jl")) +include("irutils.jl") # domsort # ======= @@ -1196,9 +1196,9 @@ let ci = code_typed1(optimize=false) do end end ir = Core.Compiler.inflate_ir(ci) - @test count(@nospecialize(stmt)->isa(stmt, Core.GotoIfNot), ir.stmts.stmt) == 1 + @test any(@nospecialize(stmt)->isa(stmt, Core.GotoIfNot), ir.stmts.stmt) ir = Core.Compiler.compact!(ir, true) - @test count(@nospecialize(stmt)->isa(stmt, Core.GotoIfNot), ir.stmts.stmt) == 0 + @test !any(@nospecialize(stmt)->isa(stmt, Core.GotoIfNot), ir.stmts.stmt) end # Test that adce_pass! can drop phi node uses that can be concluded unused @@ -1439,3 +1439,16 @@ function foo(b, x) getfield(f, :x) + 1 end @test foo(true, 1) == 2 + +# ifelse folding +@test Core.Compiler.is_removable_if_unused(Base.infer_effects(exp, (Float64,))) +@test !Core.Compiler.is_inlineable(code_typed1(exp, (Float64,))) +fully_eliminated(; retval=Core.Argument(2)) do x::Float64 + return Core.ifelse(true, x, exp(x)) +end +fully_eliminated(; retval=Core.Argument(2)) do x::Float64 + return ifelse(true, x, exp(x)) # the optimization should be applied to post-inlining IR too +end +fully_eliminated(; retval=Core.Argument(2)) do x::Float64 + return ifelse(isa(x, Float64), x, exp(x)) +end diff --git a/test/core.jl b/test/core.jl index f90a3d968c19b..e65cf08be9e15 100644 --- a/test/core.jl +++ b/test/core.jl @@ -374,8 +374,8 @@ let ft = Base.datatype_fieldtypes @test ft(elT2.body)[1].parameters[1] === elT2 @test Base.isconcretetype(ft(elT2.body)[1]) end -#struct S22624{A,B,C} <: Ref{S22624{Int64,A}}; end -@test_broken @isdefined S22624 +struct S22624{A,B,C} <: Ref{S22624{Int,A}}; end +@test sizeof(S22624) == sizeof(S22624{Int,Int,Int}) == 0 # issue #42297 mutable struct Node42297{T, V} @@ -414,6 +414,18 @@ mutable struct FooFoo{A,B} y::FooFoo{A} end @test FooFoo{Int} <: FooFoo{Int,AbstractString}.types[1] +# make sure this self-referential struct doesn't crash type layout +struct SelfTyA{V} + a::Base.RefValue{V} +end +struct SelfTyB{T} + a::T + b::SelfTyA{SelfTyB{T}} +end +let T = Base.RefValue{SelfTyB{Int}} + @test sizeof(T) === sizeof(Int) + @test sizeof(T.types[1]) === 2 * sizeof(Int) +end let x = (2,3) @test +(x...) == 5 @@ -4110,7 +4122,29 @@ end let z1 = Z14477() @test isa(z1, Z14477) @test isa(z1.fld, Z14477) + @test isdefined(z1, :fld) + @test !isdefined(z1.fld, :fld) +end +struct Z14477B + fld::Union{Nothing,Z14477B} + Z14477B() = new(new(nothing)) end +let z1 = Z14477B() + @test isa(z1, Z14477B) + @test isa(z1.fld, Z14477B) + @test isa(z1.fld.fld, Nothing) +end +struct Z14477C{T} + fld::Z14477C{Int8} + Z14477C() = new{Int16}(new{Int8}()) +end +let z1 = Z14477C() + @test isa(z1, Z14477C) + @test isa(z1.fld, Z14477C) + @test isdefined(z1, :fld) + @test !isdefined(z1.fld, :fld) +end + # issue #8846, generic macros macro m8846(a, b=0) diff --git a/test/llvmpasses/names.jl b/test/llvmpasses/names.jl index 24d1d8df9f963..c64577680421b 100644 --- a/test/llvmpasses/names.jl +++ b/test/llvmpasses/names.jl @@ -72,6 +72,11 @@ function f7(a) return a[2] end +# COM: check write barrier names +mutable struct Barrier + b +end + # CHECK-LABEL: define {{(swiftcc )?}}double @julia_f1 # CHECK-SAME: double %"a::Float64" # CHECK-SAME: double %"b::Float64" @@ -173,3 +178,11 @@ emit(f6, E) # CHECK-SAME: %"a::Tuple" # CHECK: %"a::Tuple[2]_ptr.unbox emit(f7,Tuple{Int,Int}) + +# CHECK: define {{(swiftcc )?}}nonnull {} addrspace(10)* @julia_Barrier +# CHECK-SAME: %"b::Int64" +# CHECK: %parent_bits +# CHECK: %parent_old_marked +# CHECK: %child_bit +# CHECK: %child_not_marked +emit(Barrier, Int64) diff --git a/test/math.jl b/test/math.jl index 8e6afba0acfce..df5c0d23d37a4 100644 --- a/test/math.jl +++ b/test/math.jl @@ -188,6 +188,7 @@ end @test exp10(x) โ‰ˆ exp10(big(x)) @test exp2(x) โ‰ˆ exp2(big(x)) @test expm1(x) โ‰ˆ expm1(big(x)) + @test expm1(T(-1.1)) โ‰ˆ expm1(big(T(-1.1))) @test hypot(x,y) โ‰ˆ hypot(big(x),big(y)) @test hypot(x,x,y) โ‰ˆ hypot(hypot(big(x),big(x)),big(y)) @test hypot(x,x,y,y) โ‰ˆ hypot(hypot(big(x),big(x)),hypot(big(y),big(y))) @@ -1540,24 +1541,31 @@ end @test (@allocated f44336()) == 0 end -# test constant-foldability -for fn in (:sin, :cos, :tan, :log, :log2, :log10, :log1p, :exponent, :sqrt, :cbrt, :fourthroot, - :asin, :atan, :acos, :sinh, :cosh, :tanh, :asinh, :acosh, :atanh, - :exp, :exp2, :exp10, :expm1 - ) - for T in (Float16, Float32, Float64) - f = getfield(@__MODULE__, fn) - eff = Base.infer_effects(f, (T,)) - @test Core.Compiler.is_foldable(eff) +@testset "constant-foldability of core math functions" begin + for fn in (:sin, :cos, :tan, :log, :log2, :log10, :log1p, :exponent, :sqrt, :cbrt, :fourthroot, + :asin, :atan, :acos, :sinh, :cosh, :tanh, :asinh, :acosh, :atanh, + :exp, :exp2, :exp10, :expm1 + ) + for T in (Float16, Float32, Float64) + @testset let f = getfield(@__MODULE__, fn), T = T + @test Core.Compiler.is_foldable(Base.infer_effects(f, (T,))) + end + end end -end -for T in (Float16, Float32, Float64) - for f in (exp, exp2, exp10) - @test Core.Compiler.is_removable_if_unused(Base.infer_effects(f, (T,))) +end; +@testset "removability of core math functions" begin + for T in (Float16, Float32, Float64) + @testset let T = T + for f in (exp, exp2, exp10) + @testset let f = f + @test Core.Compiler.is_removable_if_unused(Base.infer_effects(f, (T,))) + end + end + @test Core.Compiler.is_foldable(Base.infer_effects(^, (T,Int))) + @test Core.Compiler.is_foldable(Base.infer_effects(^, (T,T))) + end end - @test Core.Compiler.is_foldable(Base.infer_effects(^, (T,Int))) - @test Core.Compiler.is_foldable(Base.infer_effects(^, (T,T))) -end +end; @testset "BigInt Rationals with special funcs" begin @test sinpi(big(1//1)) == big(0.0) diff --git a/test/mpfr.jl b/test/mpfr.jl index 1a0a0041bf94e..a0dd15d97f70c 100644 --- a/test/mpfr.jl +++ b/test/mpfr.jl @@ -1039,3 +1039,10 @@ end end end end + +@testset "issue #50642" begin + setprecision(BigFloat, 500) do + bf = big"1.4901162082026128889687591176485489397376143775948511e-07" + @test Float16(bf) == Float16(2.0e-7) + end +end diff --git a/test/numbers.jl b/test/numbers.jl index a1b425c6d8f19..be661da6783fe 100644 --- a/test/numbers.jl +++ b/test/numbers.jl @@ -3123,3 +3123,20 @@ end end end + +@testset "FP(inf) == inf" begin + # Iterate through all pairs of FP types + fp_types = (Float16, Float32, Float64, BigFloat) + for F โˆˆ fp_types, G โˆˆ fp_types, f โˆˆ (typemin, typemax) + i = f(F) + @test i == G(i) + end +end + +@testset "small int FP conversion" begin + fp_types = (Float16, Float32, Float64, BigFloat) + m = Int(maxintfloat(Float16)) + for F โˆˆ fp_types, G โˆˆ fp_types, n โˆˆ (-m):m + @test n == G(F(n)) == F(G(n)) + end +end diff --git a/test/opaque_closure.jl b/test/opaque_closure.jl index a7cd5780b5d36..6dd80b3db674f 100644 --- a/test/opaque_closure.jl +++ b/test/opaque_closure.jl @@ -329,3 +329,9 @@ let (bt, did_gc) = make_oc_and_collect_bt() return frame.linfo.def.is_for_opaque_closure end end + +# Opaque closure with mismatch struct argtype +const op_arg_restrict2 = @opaque (x::Tuple{Int64}, y::Base.RefValue{Int64})->x+y +ccall_op_arg_restrict2_bad_args() = op_arg_restrict2((1.,), 2) + +@test_throws TypeError ccall_op_arg_restrict2_bad_args() diff --git a/test/operators.jl b/test/operators.jl index 715212a80a54f..cf72d83ab0076 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -366,3 +366,21 @@ end Base.:(<)(::B46327, ::B46327) = false @test B46327() <= B46327() end + +@testset "concrete eval `x in itr::Tuple`" begin + @test Core.Compiler.is_foldable(Base.infer_effects(in, (Int,Tuple{Int,Int,Int}))) + @test Core.Compiler.is_foldable(Base.infer_effects(in, (Char,Tuple{Char,Char,Char}))) + for i = (1,2,3) + @testset let i = i + @test @eval Base.return_types() do + Val($i in (1,2,3)) + end |> only == Val{true} + end + end + @test Base.return_types() do + Val(4 in (1,2,3)) + end |> only == Val{false} + @test Base.return_types() do + Val('1' in ('1','2','3')) + end |> only == Val{true} +end diff --git a/test/reduce.jl b/test/reduce.jl index aea1f1e60f6cd..2c852084de37e 100644 --- a/test/reduce.jl +++ b/test/reduce.jl @@ -705,3 +705,32 @@ let a = NamedTuple(Symbol(:x,i) => i for i in 1:33), b = (a...,) @test fold_alloc(a) == fold_alloc(b) == 0 end + +@testset "concrete eval `[any|all](f, itr::Tuple)`" begin + intf = in((1,2,3)); Intf = typeof(intf) + symf = in((:one,:two,:three)); Symf = typeof(symf) + @test Core.Compiler.is_foldable(Base.infer_effects(intf, (Int,))) + @test Core.Compiler.is_foldable(Base.infer_effects(symf, (Symbol,))) + @test Core.Compiler.is_foldable(Base.infer_effects(all, (Intf,Tuple{Int,Int,Int}))) + @test Core.Compiler.is_foldable(Base.infer_effects(all, (Symf,Tuple{Symbol,Symbol,Symbol}))) + @test Core.Compiler.is_foldable(Base.infer_effects(any, (Intf,Tuple{Int,Int,Int}))) + @test Core.Compiler.is_foldable(Base.infer_effects(any, (Symf,Tuple{Symbol,Symbol,Symbol}))) + @test Base.return_types() do + Val(all(in((1,2,3)), (1,2,3))) + end |> only == Val{true} + @test Base.return_types() do + Val(all(in((1,2,3)), (1,2,3,4))) + end |> only == Val{false} + @test Base.return_types() do + Val(any(in((1,2,3)), (4,5,3))) + end |> only == Val{true} + @test Base.return_types() do + Val(any(in((1,2,3)), (4,5,6))) + end |> only == Val{false} + @test Base.return_types() do + Val(all(in((:one,:two,:three)),(:three,:four))) + end |> only == Val{false} + @test Base.return_types() do + Val(any(in((:one,:two,:three)),(:four,:three))) + end |> only == Val{true} +end diff --git a/test/rounding.jl b/test/rounding.jl index 508a68032e083..6aa72936998c8 100644 --- a/test/rounding.jl +++ b/test/rounding.jl @@ -57,7 +57,7 @@ end @test pu - pd == eps(pz) end - for T in [Float32,Float64] + for T in [Float16,Float32,Float64] for v in [sqrt(big(2.0)),-big(1.0)/big(3.0),nextfloat(big(1.0)), prevfloat(big(1.0)),nextfloat(big(0.0)),prevfloat(big(0.0)), pi,โ„ฏ,eulergamma,catalan,golden, @@ -351,3 +351,68 @@ end Base.Rounding.setrounding_raw(T, Base.Rounding.to_fenv(old)) end end + +const MPFRRM = Base.MPFR.MPFRRoundingMode + +function mpfr_to_ieee(::Type{Float32}, x::BigFloat, r::MPFRRM) + ccall((:mpfr_get_flt, Base.MPFR.libmpfr), Float32, (Ref{BigFloat}, MPFRRM), x, r) +end +function mpfr_to_ieee(::Type{Float64}, x::BigFloat, r::MPFRRM) + ccall((:mpfr_get_d, Base.MPFR.libmpfr), Float64, (Ref{BigFloat}, MPFRRM), x, r) +end + +function mpfr_to_ieee(::Type{G}, x::BigFloat, r::RoundingMode) where {G} + mpfr_to_ieee(G, x, convert(MPFRRM, r)) +end + +const mpfr_rounding_modes = map( + Base.Fix1(convert, MPFRRM), + (RoundNearest, RoundToZero, RoundFromZero, RoundDown, RoundUp) +) + +sample_float(::Type{T}, e::Integer) where {T<:AbstractFloat} = ldexp(rand(T) + true, e)::T + +function float_samples(::Type{T}, exponents, n::Int) where {T<:AbstractFloat} + ret = T[] + for e โˆˆ exponents, i โˆˆ 1:n + push!(ret, sample_float(T, e), -sample_float(T, e)) + end + ret +end + +@testset "IEEEFloat(::BigFloat) against MPFR" begin + for pr โˆˆ 1:200 + setprecision(BigFloat, pr) do + exp = exponent(floatmax(Float64)) + 10 + bf_samples = float_samples(BigFloat, (-exp):exp, 20) + for mpfr_rm โˆˆ mpfr_rounding_modes, bf โˆˆ bf_samples, F โˆˆ (Float32, Float64) + @test ( + mpfr_to_ieee(F, bf, mpfr_rm) === + F(bf, mpfr_rm) === F(bf, convert(RoundingMode, mpfr_rm)) + ) + end + end + end +end + +const native_rounding_modes = ( + RoundNearest, RoundNearestTiesAway, RoundNearestTiesUp, + RoundToZero, RoundFromZero, RoundUp, RoundDown +) + +# Checks that each rounding mode is faithful. +@testset "IEEEFloat(::BigFloat) faithful rounding" begin + for pr โˆˆ 1:200 + setprecision(BigFloat, pr) do + exp = 500 + bf_samples = float_samples(BigFloat, (-exp):exp, 20) + for rm โˆˆ (mpfr_rounding_modes..., Base.MPFR.MPFRRoundFaithful, + native_rounding_modes...), + bf โˆˆ bf_samples, + F โˆˆ (Float16, Float32, Float64) + f = F(bf, rm) + @test (f === F(bf, RoundDown)) | (f === F(bf, RoundUp)) + end + end + end +end diff --git a/test/terminfo.jl b/test/terminfo.jl new file mode 100644 index 0000000000000..cbaab346a617b --- /dev/null +++ b/test/terminfo.jl @@ -0,0 +1,911 @@ +let + dumb_terminfo = UInt8[ + 0x1a, 0x01, 0x18, 0x00, 0x02, 0x00, 0x01, 0x00, 0x82, 0x00, 0x08, 0x00, + 0x64, 0x75, 0x6d, 0x62, 0x7c, 0x38, 0x30, 0x2d, 0x63, 0x6f, 0x6c, 0x75, + 0x6d, 0x6e, 0x20, 0x64, 0x75, 0x6d, 0x62, 0x20, 0x74, 0x74, 0x79, 0x00, + 0x00, 0x01, 0x50, 0x00, 0xff, 0xff, 0x00, 0x00, 0x02, 0x00, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x04, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x06, 0x00, + 0x07, 0x00, 0x0d, 0x00, 0x0a, 0x00, 0x0a, 0x00] + + dumb_capabilities = Dict{Symbol, Union{Bool, Int, String}}( + :am => true, + :auto_right_margin => true, + :bw => false, + :auto_left_margin => false, + :bel => "\a", + :bell => "\a", + :cr => "\r", + :carriage_return => "\r", + :cols => 80, + :columns => 80, + :cud1 => "\n", + :cursor_down => "\n", + :ind => "\n", + :scroll_forward => "\n") + + xterm_terminfo = UInt8[ + 0x1a, 0x01, 0x30, 0x00, 0x26, 0x00, 0x0f, 0x00, 0x9d, 0x01, 0xe6, 0x05, + 0x78, 0x74, 0x65, 0x72, 0x6d, 0x7c, 0x78, 0x74, 0x65, 0x72, 0x6d, 0x20, + 0x74, 0x65, 0x72, 0x6d, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x65, 0x6d, 0x75, + 0x6c, 0x61, 0x74, 0x6f, 0x72, 0x20, 0x28, 0x58, 0x20, 0x57, 0x69, 0x6e, + 0x64, 0x6f, 0x77, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x29, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x50, 0x00, 0x08, 0x00, 0x18, 0x00, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x08, 0x00, 0x40, 0x00, 0x00, 0x00, 0x04, 0x00, + 0x06, 0x00, 0x08, 0x00, 0x19, 0x00, 0x1e, 0x00, 0x26, 0x00, 0x2a, 0x00, + 0x2e, 0x00, 0xff, 0xff, 0x39, 0x00, 0x4a, 0x00, 0x4c, 0x00, 0x50, 0x00, + 0x57, 0x00, 0xff, 0xff, 0x59, 0x00, 0x66, 0x00, 0xff, 0xff, 0x6a, 0x00, + 0x6e, 0x00, 0x78, 0x00, 0x7c, 0x00, 0xff, 0xff, 0xff, 0xff, 0x80, 0x00, + 0x84, 0x00, 0x89, 0x00, 0x8e, 0x00, 0xff, 0xff, 0xa0, 0x00, 0xa5, 0x00, + 0xaa, 0x00, 0xff, 0xff, 0xaf, 0x00, 0xb4, 0x00, 0xb9, 0x00, 0xbe, 0x00, + 0xc7, 0x00, 0xcb, 0x00, 0xd2, 0x00, 0xff, 0xff, 0xe4, 0x00, 0xe9, 0x00, + 0xef, 0x00, 0xf5, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x01, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x19, 0x01, 0xff, 0xff, 0x1d, 0x01, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1f, 0x01, 0xff, 0xff, 0x24, 0x01, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x28, 0x01, 0x2c, 0x01, + 0x32, 0x01, 0x36, 0x01, 0x3a, 0x01, 0x3e, 0x01, 0x44, 0x01, 0x4a, 0x01, + 0x50, 0x01, 0x56, 0x01, 0x5c, 0x01, 0x60, 0x01, 0xff, 0xff, 0x65, 0x01, + 0xff, 0xff, 0x69, 0x01, 0x6e, 0x01, 0x73, 0x01, 0x77, 0x01, 0x7e, 0x01, + 0xff, 0xff, 0x85, 0x01, 0x89, 0x01, 0x91, 0x01, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x99, 0x01, 0xa2, 0x01, 0xff, 0xff, + 0xff, 0xff, 0xab, 0x01, 0xb4, 0x01, 0xbd, 0x01, 0xc6, 0x01, 0xcf, 0x01, + 0xd8, 0x01, 0xe1, 0x01, 0xea, 0x01, 0xf3, 0x01, 0xfc, 0x01, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0x05, 0x02, 0x09, 0x02, 0x0e, 0x02, 0x13, 0x02, + 0x27, 0x02, 0x2a, 0x02, 0xff, 0xff, 0xff, 0xff, 0x3c, 0x02, 0x3f, 0x02, + 0x4a, 0x02, 0x4d, 0x02, 0x4f, 0x02, 0x52, 0x02, 0xaf, 0x02, 0xff, 0xff, + 0xb2, 0x02, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb4, 0x02, + 0xb8, 0x02, 0xbc, 0x02, 0xc0, 0x02, 0xc4, 0x02, 0xff, 0xff, 0xff, 0xff, + 0xc8, 0x02, 0xff, 0xff, 0xfd, 0x02, 0xff, 0xff, 0xff, 0xff, 0x01, 0x03, + 0x07, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x0d, 0x03, 0x11, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x15, 0x03, 0xff, 0xff, 0xff, 0xff, + 0x1c, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x23, 0x03, + 0x2a, 0x03, 0x31, 0x03, 0xff, 0xff, 0xff, 0xff, 0x38, 0x03, 0xff, 0xff, + 0x3f, 0x03, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x46, 0x03, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x4d, 0x03, 0x53, 0x03, + 0x59, 0x03, 0x60, 0x03, 0x67, 0x03, 0x6e, 0x03, 0x75, 0x03, 0x7d, 0x03, + 0x85, 0x03, 0x8d, 0x03, 0x95, 0x03, 0x9d, 0x03, 0xa5, 0x03, 0xad, 0x03, + 0xb5, 0x03, 0xbc, 0x03, 0xc3, 0x03, 0xca, 0x03, 0xd1, 0x03, 0xd9, 0x03, + 0xe1, 0x03, 0xe9, 0x03, 0xf1, 0x03, 0xf9, 0x03, 0x01, 0x04, 0x09, 0x04, + 0x11, 0x04, 0x18, 0x04, 0x1f, 0x04, 0x26, 0x04, 0x2d, 0x04, 0x35, 0x04, + 0x3d, 0x04, 0x45, 0x04, 0x4d, 0x04, 0x55, 0x04, 0x5d, 0x04, 0x65, 0x04, + 0x6d, 0x04, 0x74, 0x04, 0x7b, 0x04, 0x82, 0x04, 0x89, 0x04, 0x91, 0x04, + 0x99, 0x04, 0xa1, 0x04, 0xa9, 0x04, 0xb1, 0x04, 0xb9, 0x04, 0xc1, 0x04, + 0xc9, 0x04, 0xd0, 0x04, 0xd7, 0x04, 0xde, 0x04, 0xe3, 0x04, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xea, 0x04, 0xf5, 0x04, 0xfa, 0x04, + 0x0d, 0x05, 0x11, 0x05, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x1a, 0x05, 0x60, 0x05, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xa6, 0x05, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xab, 0x05, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb1, 0x05, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb5, 0x05, 0xbf, 0x05, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xc9, 0x05, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xe0, 0x05, 0xe3, 0x05, 0x1b, 0x5b, 0x5a, 0x00, 0x07, 0x00, + 0x0d, 0x00, 0x1b, 0x5b, 0x25, 0x69, 0x25, 0x70, 0x31, 0x25, 0x64, 0x3b, + 0x25, 0x70, 0x32, 0x25, 0x64, 0x72, 0x00, 0x1b, 0x5b, 0x33, 0x67, 0x00, + 0x1b, 0x5b, 0x48, 0x1b, 0x5b, 0x32, 0x4a, 0x00, 0x1b, 0x5b, 0x4b, 0x00, + 0x1b, 0x5b, 0x4a, 0x00, 0x1b, 0x5b, 0x25, 0x69, 0x25, 0x70, 0x31, 0x25, + 0x64, 0x47, 0x00, 0x1b, 0x5b, 0x25, 0x69, 0x25, 0x70, 0x31, 0x25, 0x64, + 0x3b, 0x25, 0x70, 0x32, 0x25, 0x64, 0x48, 0x00, 0x0a, 0x00, 0x1b, 0x5b, + 0x48, 0x00, 0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x6c, 0x00, 0x08, 0x00, 0x1b, + 0x5b, 0x3f, 0x31, 0x32, 0x6c, 0x1b, 0x5b, 0x3f, 0x32, 0x35, 0x68, 0x00, + 0x1b, 0x5b, 0x43, 0x00, 0x1b, 0x5b, 0x41, 0x00, 0x1b, 0x5b, 0x3f, 0x31, + 0x32, 0x3b, 0x32, 0x35, 0x68, 0x00, 0x1b, 0x5b, 0x50, 0x00, 0x1b, 0x5b, + 0x4d, 0x00, 0x1b, 0x28, 0x30, 0x00, 0x1b, 0x5b, 0x35, 0x6d, 0x00, 0x1b, + 0x5b, 0x31, 0x6d, 0x00, 0x1b, 0x5b, 0x3f, 0x31, 0x30, 0x34, 0x39, 0x68, + 0x1b, 0x5b, 0x32, 0x32, 0x3b, 0x30, 0x3b, 0x30, 0x74, 0x00, 0x1b, 0x5b, + 0x32, 0x6d, 0x00, 0x1b, 0x5b, 0x34, 0x68, 0x00, 0x1b, 0x5b, 0x38, 0x6d, + 0x00, 0x1b, 0x5b, 0x37, 0x6d, 0x00, 0x1b, 0x5b, 0x37, 0x6d, 0x00, 0x1b, + 0x5b, 0x34, 0x6d, 0x00, 0x1b, 0x5b, 0x25, 0x70, 0x31, 0x25, 0x64, 0x58, + 0x00, 0x1b, 0x28, 0x42, 0x00, 0x1b, 0x28, 0x42, 0x1b, 0x5b, 0x6d, 0x00, + 0x1b, 0x5b, 0x3f, 0x31, 0x30, 0x34, 0x39, 0x6c, 0x1b, 0x5b, 0x32, 0x33, + 0x3b, 0x30, 0x3b, 0x30, 0x74, 0x00, 0x1b, 0x5b, 0x34, 0x6c, 0x00, 0x1b, + 0x5b, 0x32, 0x37, 0x6d, 0x00, 0x1b, 0x5b, 0x32, 0x34, 0x6d, 0x00, 0x1b, + 0x5b, 0x3f, 0x35, 0x68, 0x24, 0x3c, 0x31, 0x30, 0x30, 0x2f, 0x3e, 0x1b, + 0x5b, 0x3f, 0x35, 0x6c, 0x00, 0x1b, 0x5b, 0x21, 0x70, 0x1b, 0x5b, 0x3f, + 0x33, 0x3b, 0x34, 0x6c, 0x1b, 0x5b, 0x34, 0x6c, 0x1b, 0x3e, 0x00, 0x1b, + 0x5b, 0x4c, 0x00, 0x08, 0x00, 0x1b, 0x5b, 0x33, 0x7e, 0x00, 0x1b, 0x4f, + 0x42, 0x00, 0x1b, 0x4f, 0x50, 0x00, 0x1b, 0x5b, 0x32, 0x31, 0x7e, 0x00, + 0x1b, 0x4f, 0x51, 0x00, 0x1b, 0x4f, 0x52, 0x00, 0x1b, 0x4f, 0x53, 0x00, + 0x1b, 0x5b, 0x31, 0x35, 0x7e, 0x00, 0x1b, 0x5b, 0x31, 0x37, 0x7e, 0x00, + 0x1b, 0x5b, 0x31, 0x38, 0x7e, 0x00, 0x1b, 0x5b, 0x31, 0x39, 0x7e, 0x00, + 0x1b, 0x5b, 0x32, 0x30, 0x7e, 0x00, 0x1b, 0x4f, 0x48, 0x00, 0x1b, 0x5b, + 0x32, 0x7e, 0x00, 0x1b, 0x4f, 0x44, 0x00, 0x1b, 0x5b, 0x36, 0x7e, 0x00, + 0x1b, 0x5b, 0x35, 0x7e, 0x00, 0x1b, 0x4f, 0x43, 0x00, 0x1b, 0x5b, 0x31, + 0x3b, 0x32, 0x42, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x41, 0x00, 0x1b, + 0x4f, 0x41, 0x00, 0x1b, 0x5b, 0x3f, 0x31, 0x6c, 0x1b, 0x3e, 0x00, 0x1b, + 0x5b, 0x3f, 0x31, 0x68, 0x1b, 0x3d, 0x00, 0x1b, 0x5b, 0x3f, 0x31, 0x30, + 0x33, 0x34, 0x6c, 0x00, 0x1b, 0x5b, 0x3f, 0x31, 0x30, 0x33, 0x34, 0x68, + 0x00, 0x1b, 0x5b, 0x25, 0x70, 0x31, 0x25, 0x64, 0x50, 0x00, 0x1b, 0x5b, + 0x25, 0x70, 0x31, 0x25, 0x64, 0x4d, 0x00, 0x1b, 0x5b, 0x25, 0x70, 0x31, + 0x25, 0x64, 0x42, 0x00, 0x1b, 0x5b, 0x25, 0x70, 0x31, 0x25, 0x64, 0x40, + 0x00, 0x1b, 0x5b, 0x25, 0x70, 0x31, 0x25, 0x64, 0x53, 0x00, 0x1b, 0x5b, + 0x25, 0x70, 0x31, 0x25, 0x64, 0x4c, 0x00, 0x1b, 0x5b, 0x25, 0x70, 0x31, + 0x25, 0x64, 0x44, 0x00, 0x1b, 0x5b, 0x25, 0x70, 0x31, 0x25, 0x64, 0x43, + 0x00, 0x1b, 0x5b, 0x25, 0x70, 0x31, 0x25, 0x64, 0x54, 0x00, 0x1b, 0x5b, + 0x25, 0x70, 0x31, 0x25, 0x64, 0x41, 0x00, 0x1b, 0x5b, 0x69, 0x00, 0x1b, + 0x5b, 0x34, 0x69, 0x00, 0x1b, 0x5b, 0x35, 0x69, 0x00, 0x25, 0x70, 0x31, + 0x25, 0x63, 0x1b, 0x5b, 0x25, 0x70, 0x32, 0x25, 0x7b, 0x31, 0x7d, 0x25, + 0x2d, 0x25, 0x64, 0x62, 0x00, 0x1b, 0x63, 0x00, 0x1b, 0x5b, 0x21, 0x70, + 0x1b, 0x5b, 0x3f, 0x33, 0x3b, 0x34, 0x6c, 0x1b, 0x5b, 0x34, 0x6c, 0x1b, + 0x3e, 0x00, 0x1b, 0x38, 0x00, 0x1b, 0x5b, 0x25, 0x69, 0x25, 0x70, 0x31, + 0x25, 0x64, 0x64, 0x00, 0x1b, 0x37, 0x00, 0x0a, 0x00, 0x1b, 0x4d, 0x00, + 0x25, 0x3f, 0x25, 0x70, 0x39, 0x25, 0x74, 0x1b, 0x28, 0x30, 0x25, 0x65, + 0x1b, 0x28, 0x42, 0x25, 0x3b, 0x1b, 0x5b, 0x30, 0x25, 0x3f, 0x25, 0x70, + 0x36, 0x25, 0x74, 0x3b, 0x31, 0x25, 0x3b, 0x25, 0x3f, 0x25, 0x70, 0x35, + 0x25, 0x74, 0x3b, 0x32, 0x25, 0x3b, 0x25, 0x3f, 0x25, 0x70, 0x32, 0x25, + 0x74, 0x3b, 0x34, 0x25, 0x3b, 0x25, 0x3f, 0x25, 0x70, 0x31, 0x25, 0x70, + 0x33, 0x25, 0x7c, 0x25, 0x74, 0x3b, 0x37, 0x25, 0x3b, 0x25, 0x3f, 0x25, + 0x70, 0x34, 0x25, 0x74, 0x3b, 0x35, 0x25, 0x3b, 0x25, 0x3f, 0x25, 0x70, + 0x37, 0x25, 0x74, 0x3b, 0x38, 0x25, 0x3b, 0x6d, 0x00, 0x1b, 0x48, 0x00, + 0x09, 0x00, 0x1b, 0x4f, 0x77, 0x00, 0x1b, 0x4f, 0x79, 0x00, 0x1b, 0x4f, + 0x75, 0x00, 0x1b, 0x4f, 0x71, 0x00, 0x1b, 0x4f, 0x73, 0x00, 0x60, 0x60, + 0x61, 0x61, 0x66, 0x66, 0x67, 0x67, 0x69, 0x69, 0x6a, 0x6a, 0x6b, 0x6b, + 0x6c, 0x6c, 0x6d, 0x6d, 0x6e, 0x6e, 0x6f, 0x6f, 0x70, 0x70, 0x71, 0x71, + 0x72, 0x72, 0x73, 0x73, 0x74, 0x74, 0x75, 0x75, 0x76, 0x76, 0x77, 0x77, + 0x78, 0x78, 0x79, 0x79, 0x7a, 0x7a, 0x7b, 0x7b, 0x7c, 0x7c, 0x7d, 0x7d, + 0x7e, 0x7e, 0x00, 0x1b, 0x5b, 0x5a, 0x00, 0x1b, 0x5b, 0x3f, 0x37, 0x68, + 0x00, 0x1b, 0x5b, 0x3f, 0x37, 0x6c, 0x00, 0x1b, 0x4f, 0x46, 0x00, 0x1b, + 0x4f, 0x4d, 0x00, 0x1b, 0x5b, 0x33, 0x3b, 0x32, 0x7e, 0x00, 0x1b, 0x5b, + 0x31, 0x3b, 0x32, 0x46, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x48, 0x00, + 0x1b, 0x5b, 0x32, 0x3b, 0x32, 0x7e, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x32, + 0x44, 0x00, 0x1b, 0x5b, 0x36, 0x3b, 0x32, 0x7e, 0x00, 0x1b, 0x5b, 0x35, + 0x3b, 0x32, 0x7e, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x43, 0x00, 0x1b, + 0x5b, 0x32, 0x33, 0x7e, 0x00, 0x1b, 0x5b, 0x32, 0x34, 0x7e, 0x00, 0x1b, + 0x5b, 0x31, 0x3b, 0x32, 0x50, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x51, + 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x32, 0x52, 0x00, 0x1b, 0x5b, 0x31, 0x3b, + 0x32, 0x53, 0x00, 0x1b, 0x5b, 0x31, 0x35, 0x3b, 0x32, 0x7e, 0x00, 0x1b, + 0x5b, 0x31, 0x37, 0x3b, 0x32, 0x7e, 0x00, 0x1b, 0x5b, 0x31, 0x38, 0x3b, + 0x32, 0x7e, 0x00, 0x1b, 0x5b, 0x31, 0x39, 0x3b, 0x32, 0x7e, 0x00, 0x1b, + 0x5b, 0x32, 0x30, 0x3b, 0x32, 0x7e, 0x00, 0x1b, 0x5b, 0x32, 0x31, 0x3b, + 0x32, 0x7e, 0x00, 0x1b, 0x5b, 0x32, 0x33, 0x3b, 0x32, 0x7e, 0x00, 0x1b, + 0x5b, 0x32, 0x34, 0x3b, 0x32, 0x7e, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x35, + 0x50, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x51, 0x00, 0x1b, 0x5b, 0x31, + 0x3b, 0x35, 0x52, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x53, 0x00, 0x1b, + 0x5b, 0x31, 0x35, 0x3b, 0x35, 0x7e, 0x00, 0x1b, 0x5b, 0x31, 0x37, 0x3b, + 0x35, 0x7e, 0x00, 0x1b, 0x5b, 0x31, 0x38, 0x3b, 0x35, 0x7e, 0x00, 0x1b, + 0x5b, 0x31, 0x39, 0x3b, 0x35, 0x7e, 0x00, 0x1b, 0x5b, 0x32, 0x30, 0x3b, + 0x35, 0x7e, 0x00, 0x1b, 0x5b, 0x32, 0x31, 0x3b, 0x35, 0x7e, 0x00, 0x1b, + 0x5b, 0x32, 0x33, 0x3b, 0x35, 0x7e, 0x00, 0x1b, 0x5b, 0x32, 0x34, 0x3b, + 0x35, 0x7e, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x36, 0x50, 0x00, 0x1b, 0x5b, + 0x31, 0x3b, 0x36, 0x51, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x36, 0x52, 0x00, + 0x1b, 0x5b, 0x31, 0x3b, 0x36, 0x53, 0x00, 0x1b, 0x5b, 0x31, 0x35, 0x3b, + 0x36, 0x7e, 0x00, 0x1b, 0x5b, 0x31, 0x37, 0x3b, 0x36, 0x7e, 0x00, 0x1b, + 0x5b, 0x31, 0x38, 0x3b, 0x36, 0x7e, 0x00, 0x1b, 0x5b, 0x31, 0x39, 0x3b, + 0x36, 0x7e, 0x00, 0x1b, 0x5b, 0x32, 0x30, 0x3b, 0x36, 0x7e, 0x00, 0x1b, + 0x5b, 0x32, 0x31, 0x3b, 0x36, 0x7e, 0x00, 0x1b, 0x5b, 0x32, 0x33, 0x3b, + 0x36, 0x7e, 0x00, 0x1b, 0x5b, 0x32, 0x34, 0x3b, 0x36, 0x7e, 0x00, 0x1b, + 0x5b, 0x31, 0x3b, 0x33, 0x50, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x33, 0x51, + 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x33, 0x52, 0x00, 0x1b, 0x5b, 0x31, 0x3b, + 0x33, 0x53, 0x00, 0x1b, 0x5b, 0x31, 0x35, 0x3b, 0x33, 0x7e, 0x00, 0x1b, + 0x5b, 0x31, 0x37, 0x3b, 0x33, 0x7e, 0x00, 0x1b, 0x5b, 0x31, 0x38, 0x3b, + 0x33, 0x7e, 0x00, 0x1b, 0x5b, 0x31, 0x39, 0x3b, 0x33, 0x7e, 0x00, 0x1b, + 0x5b, 0x32, 0x30, 0x3b, 0x33, 0x7e, 0x00, 0x1b, 0x5b, 0x32, 0x31, 0x3b, + 0x33, 0x7e, 0x00, 0x1b, 0x5b, 0x32, 0x33, 0x3b, 0x33, 0x7e, 0x00, 0x1b, + 0x5b, 0x32, 0x34, 0x3b, 0x33, 0x7e, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x34, + 0x50, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x34, 0x51, 0x00, 0x1b, 0x5b, 0x31, + 0x3b, 0x34, 0x52, 0x00, 0x1b, 0x5b, 0x31, 0x4b, 0x00, 0x1b, 0x5b, 0x3f, + 0x36, 0x39, 0x6c, 0x00, 0x1b, 0x5b, 0x25, 0x69, 0x25, 0x64, 0x3b, 0x25, + 0x64, 0x52, 0x00, 0x1b, 0x5b, 0x36, 0x6e, 0x00, 0x1b, 0x5b, 0x3f, 0x25, + 0x5b, 0x3b, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x5d, 0x63, 0x00, 0x1b, 0x5b, 0x63, 0x00, 0x1b, 0x5b, 0x33, 0x39, 0x3b, + 0x34, 0x39, 0x6d, 0x00, 0x1b, 0x5b, 0x33, 0x25, 0x3f, 0x25, 0x70, 0x31, + 0x25, 0x7b, 0x31, 0x7d, 0x25, 0x3d, 0x25, 0x74, 0x34, 0x25, 0x65, 0x25, + 0x70, 0x31, 0x25, 0x7b, 0x33, 0x7d, 0x25, 0x3d, 0x25, 0x74, 0x36, 0x25, + 0x65, 0x25, 0x70, 0x31, 0x25, 0x7b, 0x34, 0x7d, 0x25, 0x3d, 0x25, 0x74, + 0x31, 0x25, 0x65, 0x25, 0x70, 0x31, 0x25, 0x7b, 0x36, 0x7d, 0x25, 0x3d, + 0x25, 0x74, 0x33, 0x25, 0x65, 0x25, 0x70, 0x31, 0x25, 0x64, 0x25, 0x3b, + 0x6d, 0x00, 0x1b, 0x5b, 0x34, 0x25, 0x3f, 0x25, 0x70, 0x31, 0x25, 0x7b, + 0x31, 0x7d, 0x25, 0x3d, 0x25, 0x74, 0x34, 0x25, 0x65, 0x25, 0x70, 0x31, + 0x25, 0x7b, 0x33, 0x7d, 0x25, 0x3d, 0x25, 0x74, 0x36, 0x25, 0x65, 0x25, + 0x70, 0x31, 0x25, 0x7b, 0x34, 0x7d, 0x25, 0x3d, 0x25, 0x74, 0x31, 0x25, + 0x65, 0x25, 0x70, 0x31, 0x25, 0x7b, 0x36, 0x7d, 0x25, 0x3d, 0x25, 0x74, + 0x33, 0x25, 0x65, 0x25, 0x70, 0x31, 0x25, 0x64, 0x25, 0x3b, 0x6d, 0x00, + 0x1b, 0x5b, 0x33, 0x6d, 0x00, 0x1b, 0x5b, 0x32, 0x33, 0x6d, 0x00, 0x1b, + 0x5b, 0x3c, 0x00, 0x1b, 0x5b, 0x33, 0x25, 0x70, 0x31, 0x25, 0x64, 0x6d, + 0x00, 0x1b, 0x5b, 0x34, 0x25, 0x70, 0x31, 0x25, 0x64, 0x6d, 0x00, 0x1b, + 0x5b, 0x3f, 0x36, 0x39, 0x68, 0x1b, 0x5b, 0x25, 0x69, 0x25, 0x70, 0x31, + 0x25, 0x64, 0x3b, 0x25, 0x70, 0x32, 0x25, 0x64, 0x73, 0x00, 0x1b, 0x6c, + 0x00, 0x1b, 0x6d, 0x00, 0x02, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x96, 0x00, + 0xac, 0x03, 0x01, 0x01, 0x00, 0x00, 0x07, 0x00, 0x13, 0x00, 0x18, 0x00, + 0x2a, 0x00, 0x30, 0x00, 0x3a, 0x00, 0x5a, 0x00, 0x61, 0x00, 0x68, 0x00, + 0x6f, 0x00, 0x76, 0x00, 0x7d, 0x00, 0x84, 0x00, 0x8b, 0x00, 0x92, 0x00, + 0x99, 0x00, 0xa0, 0x00, 0xa7, 0x00, 0xae, 0x00, 0xb5, 0x00, 0xbc, 0x00, + 0xc3, 0x00, 0xca, 0x00, 0xd1, 0x00, 0xd8, 0x00, 0xdf, 0x00, 0xe6, 0x00, + 0xed, 0x00, 0xf4, 0x00, 0xfb, 0x00, 0x02, 0x01, 0x09, 0x01, 0x10, 0x01, + 0x17, 0x01, 0x1e, 0x01, 0x25, 0x01, 0x2c, 0x01, 0x33, 0x01, 0x3a, 0x01, + 0x41, 0x01, 0x48, 0x01, 0x4f, 0x01, 0x56, 0x01, 0x5d, 0x01, 0x64, 0x01, + 0x6b, 0x01, 0x72, 0x01, 0x79, 0x01, 0x80, 0x01, 0x87, 0x01, 0x8e, 0x01, + 0x95, 0x01, 0x9c, 0x01, 0xa3, 0x01, 0xaa, 0x01, 0xb1, 0x01, 0xb8, 0x01, + 0xbf, 0x01, 0xc6, 0x01, 0xca, 0x01, 0xce, 0x01, 0xd2, 0x01, 0xd6, 0x01, + 0xda, 0x01, 0xde, 0x01, 0xe2, 0x01, 0xe6, 0x01, 0xea, 0x01, 0xee, 0x01, + 0xf2, 0x01, 0xf6, 0x01, 0xfc, 0x01, 0x01, 0x02, 0x00, 0x00, 0x03, 0x00, + 0x06, 0x00, 0x09, 0x00, 0x0c, 0x00, 0x0f, 0x00, 0x12, 0x00, 0x15, 0x00, + 0x18, 0x00, 0x1b, 0x00, 0x20, 0x00, 0x25, 0x00, 0x2a, 0x00, 0x2f, 0x00, + 0x34, 0x00, 0x38, 0x00, 0x3d, 0x00, 0x42, 0x00, 0x47, 0x00, 0x4c, 0x00, + 0x51, 0x00, 0x57, 0x00, 0x5d, 0x00, 0x63, 0x00, 0x69, 0x00, 0x6f, 0x00, + 0x75, 0x00, 0x7b, 0x00, 0x81, 0x00, 0x87, 0x00, 0x8d, 0x00, 0x92, 0x00, + 0x97, 0x00, 0x9c, 0x00, 0xa1, 0x00, 0xa6, 0x00, 0xac, 0x00, 0xb2, 0x00, + 0xb8, 0x00, 0xbe, 0x00, 0xc4, 0x00, 0xca, 0x00, 0xd0, 0x00, 0xd6, 0x00, + 0xdc, 0x00, 0xe2, 0x00, 0xe8, 0x00, 0xee, 0x00, 0xf4, 0x00, 0xfa, 0x00, + 0x00, 0x01, 0x06, 0x01, 0x0c, 0x01, 0x12, 0x01, 0x18, 0x01, 0x1e, 0x01, + 0x22, 0x01, 0x27, 0x01, 0x2c, 0x01, 0x31, 0x01, 0x36, 0x01, 0x3b, 0x01, + 0x3f, 0x01, 0x43, 0x01, 0x47, 0x01, 0x4b, 0x01, 0x4f, 0x01, 0x55, 0x01, + 0x5b, 0x01, 0x61, 0x01, 0x67, 0x01, 0x6d, 0x01, 0x73, 0x01, 0x79, 0x01, + 0x7e, 0x01, 0x83, 0x01, 0x1b, 0x5d, 0x31, 0x31, 0x32, 0x07, 0x00, 0x1b, + 0x5d, 0x31, 0x32, 0x3b, 0x25, 0x70, 0x31, 0x25, 0x73, 0x07, 0x00, 0x1b, + 0x5b, 0x33, 0x4a, 0x00, 0x1b, 0x5d, 0x35, 0x32, 0x3b, 0x25, 0x70, 0x31, + 0x25, 0x73, 0x3b, 0x25, 0x70, 0x32, 0x25, 0x73, 0x07, 0x00, 0x1b, 0x5b, + 0x32, 0x20, 0x71, 0x00, 0x1b, 0x5b, 0x25, 0x70, 0x31, 0x25, 0x64, 0x20, + 0x71, 0x00, 0x1b, 0x5b, 0x3f, 0x31, 0x30, 0x30, 0x36, 0x3b, 0x31, 0x30, + 0x30, 0x30, 0x25, 0x3f, 0x25, 0x70, 0x31, 0x25, 0x7b, 0x31, 0x7d, 0x25, + 0x3d, 0x25, 0x74, 0x68, 0x25, 0x65, 0x6c, 0x25, 0x3b, 0x00, 0x1b, 0x5b, + 0x33, 0x3b, 0x33, 0x7e, 0x00, 0x1b, 0x5b, 0x33, 0x3b, 0x34, 0x7e, 0x00, + 0x1b, 0x5b, 0x33, 0x3b, 0x35, 0x7e, 0x00, 0x1b, 0x5b, 0x33, 0x3b, 0x36, + 0x7e, 0x00, 0x1b, 0x5b, 0x33, 0x3b, 0x37, 0x7e, 0x00, 0x1b, 0x5b, 0x31, + 0x3b, 0x32, 0x42, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x33, 0x42, 0x00, 0x1b, + 0x5b, 0x31, 0x3b, 0x34, 0x42, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x42, + 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x36, 0x42, 0x00, 0x1b, 0x5b, 0x31, 0x3b, + 0x37, 0x42, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x33, 0x46, 0x00, 0x1b, 0x5b, + 0x31, 0x3b, 0x34, 0x46, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x46, 0x00, + 0x1b, 0x5b, 0x31, 0x3b, 0x36, 0x46, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x37, + 0x46, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x33, 0x48, 0x00, 0x1b, 0x5b, 0x31, + 0x3b, 0x34, 0x48, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x48, 0x00, 0x1b, + 0x5b, 0x31, 0x3b, 0x36, 0x48, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x37, 0x48, + 0x00, 0x1b, 0x5b, 0x32, 0x3b, 0x33, 0x7e, 0x00, 0x1b, 0x5b, 0x32, 0x3b, + 0x34, 0x7e, 0x00, 0x1b, 0x5b, 0x32, 0x3b, 0x35, 0x7e, 0x00, 0x1b, 0x5b, + 0x32, 0x3b, 0x36, 0x7e, 0x00, 0x1b, 0x5b, 0x32, 0x3b, 0x37, 0x7e, 0x00, + 0x1b, 0x5b, 0x31, 0x3b, 0x33, 0x44, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x34, + 0x44, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x44, 0x00, 0x1b, 0x5b, 0x31, + 0x3b, 0x36, 0x44, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x37, 0x44, 0x00, 0x1b, + 0x5b, 0x36, 0x3b, 0x33, 0x7e, 0x00, 0x1b, 0x5b, 0x36, 0x3b, 0x34, 0x7e, + 0x00, 0x1b, 0x5b, 0x36, 0x3b, 0x35, 0x7e, 0x00, 0x1b, 0x5b, 0x36, 0x3b, + 0x36, 0x7e, 0x00, 0x1b, 0x5b, 0x36, 0x3b, 0x37, 0x7e, 0x00, 0x1b, 0x5b, + 0x35, 0x3b, 0x33, 0x7e, 0x00, 0x1b, 0x5b, 0x35, 0x3b, 0x34, 0x7e, 0x00, + 0x1b, 0x5b, 0x35, 0x3b, 0x35, 0x7e, 0x00, 0x1b, 0x5b, 0x35, 0x3b, 0x36, + 0x7e, 0x00, 0x1b, 0x5b, 0x35, 0x3b, 0x37, 0x7e, 0x00, 0x1b, 0x5b, 0x31, + 0x3b, 0x33, 0x43, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x34, 0x43, 0x00, 0x1b, + 0x5b, 0x31, 0x3b, 0x35, 0x43, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x36, 0x43, + 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x37, 0x43, 0x00, 0x1b, 0x5b, 0x31, 0x3b, + 0x32, 0x41, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x33, 0x41, 0x00, 0x1b, 0x5b, + 0x31, 0x3b, 0x34, 0x41, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x35, 0x41, 0x00, + 0x1b, 0x5b, 0x31, 0x3b, 0x36, 0x41, 0x00, 0x1b, 0x5b, 0x31, 0x3b, 0x37, + 0x41, 0x00, 0x1b, 0x4f, 0x78, 0x00, 0x1b, 0x4f, 0x74, 0x00, 0x1b, 0x4f, + 0x76, 0x00, 0x1b, 0x4f, 0x72, 0x00, 0x1b, 0x4f, 0x45, 0x00, 0x1b, 0x4f, + 0x6b, 0x00, 0x1b, 0x4f, 0x6c, 0x00, 0x1b, 0x4f, 0x6f, 0x00, 0x1b, 0x4f, + 0x6e, 0x00, 0x1b, 0x4f, 0x6a, 0x00, 0x1b, 0x4f, 0x6d, 0x00, 0x1b, 0x4f, + 0x70, 0x00, 0x1b, 0x5b, 0x32, 0x39, 0x6d, 0x00, 0x1b, 0x5b, 0x39, 0x6d, + 0x00, 0x1b, 0x5b, 0x3c, 0x25, 0x69, 0x25, 0x70, 0x33, 0x25, 0x64, 0x3b, + 0x25, 0x70, 0x31, 0x25, 0x64, 0x3b, 0x25, 0x70, 0x32, 0x25, 0x64, 0x3b, + 0x25, 0x3f, 0x25, 0x70, 0x34, 0x25, 0x74, 0x4d, 0x25, 0x65, 0x6d, 0x25, + 0x3b, 0x00, 0x41, 0x58, 0x00, 0x58, 0x54, 0x00, 0x43, 0x72, 0x00, 0x43, + 0x73, 0x00, 0x45, 0x33, 0x00, 0x4d, 0x73, 0x00, 0x53, 0x65, 0x00, 0x53, + 0x73, 0x00, 0x58, 0x4d, 0x00, 0x6b, 0x44, 0x43, 0x33, 0x00, 0x6b, 0x44, + 0x43, 0x34, 0x00, 0x6b, 0x44, 0x43, 0x35, 0x00, 0x6b, 0x44, 0x43, 0x36, + 0x00, 0x6b, 0x44, 0x43, 0x37, 0x00, 0x6b, 0x44, 0x4e, 0x00, 0x6b, 0x44, + 0x4e, 0x33, 0x00, 0x6b, 0x44, 0x4e, 0x34, 0x00, 0x6b, 0x44, 0x4e, 0x35, + 0x00, 0x6b, 0x44, 0x4e, 0x36, 0x00, 0x6b, 0x44, 0x4e, 0x37, 0x00, 0x6b, + 0x45, 0x4e, 0x44, 0x33, 0x00, 0x6b, 0x45, 0x4e, 0x44, 0x34, 0x00, 0x6b, + 0x45, 0x4e, 0x44, 0x35, 0x00, 0x6b, 0x45, 0x4e, 0x44, 0x36, 0x00, 0x6b, + 0x45, 0x4e, 0x44, 0x37, 0x00, 0x6b, 0x48, 0x4f, 0x4d, 0x33, 0x00, 0x6b, + 0x48, 0x4f, 0x4d, 0x34, 0x00, 0x6b, 0x48, 0x4f, 0x4d, 0x35, 0x00, 0x6b, + 0x48, 0x4f, 0x4d, 0x36, 0x00, 0x6b, 0x48, 0x4f, 0x4d, 0x37, 0x00, 0x6b, + 0x49, 0x43, 0x33, 0x00, 0x6b, 0x49, 0x43, 0x34, 0x00, 0x6b, 0x49, 0x43, + 0x35, 0x00, 0x6b, 0x49, 0x43, 0x36, 0x00, 0x6b, 0x49, 0x43, 0x37, 0x00, + 0x6b, 0x4c, 0x46, 0x54, 0x33, 0x00, 0x6b, 0x4c, 0x46, 0x54, 0x34, 0x00, + 0x6b, 0x4c, 0x46, 0x54, 0x35, 0x00, 0x6b, 0x4c, 0x46, 0x54, 0x36, 0x00, + 0x6b, 0x4c, 0x46, 0x54, 0x37, 0x00, 0x6b, 0x4e, 0x58, 0x54, 0x33, 0x00, + 0x6b, 0x4e, 0x58, 0x54, 0x34, 0x00, 0x6b, 0x4e, 0x58, 0x54, 0x35, 0x00, + 0x6b, 0x4e, 0x58, 0x54, 0x36, 0x00, 0x6b, 0x4e, 0x58, 0x54, 0x37, 0x00, + 0x6b, 0x50, 0x52, 0x56, 0x33, 0x00, 0x6b, 0x50, 0x52, 0x56, 0x34, 0x00, + 0x6b, 0x50, 0x52, 0x56, 0x35, 0x00, 0x6b, 0x50, 0x52, 0x56, 0x36, 0x00, + 0x6b, 0x50, 0x52, 0x56, 0x37, 0x00, 0x6b, 0x52, 0x49, 0x54, 0x33, 0x00, + 0x6b, 0x52, 0x49, 0x54, 0x34, 0x00, 0x6b, 0x52, 0x49, 0x54, 0x35, 0x00, + 0x6b, 0x52, 0x49, 0x54, 0x36, 0x00, 0x6b, 0x52, 0x49, 0x54, 0x37, 0x00, + 0x6b, 0x55, 0x50, 0x00, 0x6b, 0x55, 0x50, 0x33, 0x00, 0x6b, 0x55, 0x50, + 0x34, 0x00, 0x6b, 0x55, 0x50, 0x35, 0x00, 0x6b, 0x55, 0x50, 0x36, 0x00, + 0x6b, 0x55, 0x50, 0x37, 0x00, 0x6b, 0x61, 0x32, 0x00, 0x6b, 0x62, 0x31, + 0x00, 0x6b, 0x62, 0x33, 0x00, 0x6b, 0x63, 0x32, 0x00, 0x6b, 0x70, 0x35, + 0x00, 0x6b, 0x70, 0x41, 0x44, 0x44, 0x00, 0x6b, 0x70, 0x43, 0x4d, 0x41, + 0x00, 0x6b, 0x70, 0x44, 0x49, 0x56, 0x00, 0x6b, 0x70, 0x44, 0x4f, 0x54, + 0x00, 0x6b, 0x70, 0x4d, 0x55, 0x4c, 0x00, 0x6b, 0x70, 0x53, 0x55, 0x42, + 0x00, 0x6b, 0x70, 0x5a, 0x52, 0x4f, 0x00, 0x72, 0x6d, 0x78, 0x78, 0x00, + 0x73, 0x6d, 0x78, 0x78, 0x00, 0x78, 0x6d, 0x00] + + xterm_extensions = + [:kEND5, :Cs, :kDN5, :Cr, :kDC6, :kPRV6, :kDN7, :kb1, :kpZRO, :kNXT6, + :kLFT5, :kPRV3, :kRIT4, :kDC4, :kc2, :kp5, :kLFT6, :kIC6, :kEND6, :kIC4, + :kRIT7, :rmxx, :kpADD, :xm, :kNXT3, :XT, :kIC7, :kHOM4, :kDC7, :kPRV7, + :ka2, :kUP7, :kDN6, :kIC5, :kNXT4, :kUP5, :AX, :kpSUB, :kb3, :kDN4, + :kHOM5, :kHOM6, :kDN3, :kLFT4, :kRIT5, :kIC3, :kPRV4, :kUP, :kRIT6, :E3, + :kEND3, :kHOM7, :kDC3, :kLFT7, :kNXT5, :Se, :Ss, :kHOM3, :kRIT3, :kNXT7, + :smxx, :kEND4, :kDN, :kUP6, :XM, :kPRV5, :kUP4, :kpDOT, :kpMUL, :kEND7, + :Ms, :kpCMA, :kDC5, :kLFT3, :kpDIV, :kUP3] + + xterm_capabilities = Dict{Symbol, Union{Bool, Int, String}}( + :AX => true, + :Cr => "\e]112\a", + :Cs => "\e]12;%p1%s\a", + :E3 => "\e[3J", + :Ms => "\e]52;%p1%s;%p2%s\a", + :OTbs => true, + :Se => "\e[2 q", + :Ss => "\e[%p1%d q", + :XM => "\e[?1006;1000%?%p1%{1}%=%th%el%;", + :XT => true, + :acs_chars => "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + :acsc => "``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~", + :am => true, + :auto_left_margin => false, + :auto_right_margin => true, + :back_color_erase => true, + :back_tab => "\e[Z", + :backspaces_with_bs => true, + :bce => true, + :bel => "\a", + :bell => "\a", + :blink => "\e[5m", + :bold => "\e[1m", + :bw => false, + :can_change => false, + :carriage_return => "\r", + :cbt => "\e[Z", + :ccc => false, + :ceol_standout_glitch => false, + :change_scroll_region => "\e[%i%p1%d;%p2%dr", + :chts => false, + :civis => "\e[?25l", + :clear => "\e[H\e[2J", + :clear_all_tabs => "\e[3g", + :clear_margins => "\e[?69l", + :clear_screen => "\e[H\e[2J", + :clr_bol => "\e[1K", + :clr_eol => "\e[K", + :clr_eos => "\e[J", + :cnorm => "\e[?12l\e[?25h", + :col_addr_glitch => false, + :colors => 8, + :cols => 80, + :column_address => "\e[%i%p1%dG", + :columns => 80, + :cpi_changes_res => false, + :cpix => false, + :cr => "\r", + :cr_cancels_micro_mode => false, + :crxm => false, + :csr => "\e[%i%p1%d;%p2%dr", + :cub => "\e[%p1%dD", + :cub1 => "\b", + :cud => "\e[%p1%dB", + :cud1 => "\n", + :cuf => "\e[%p1%dC", + :cuf1 => "\e[C", + :cup => "\e[%i%p1%d;%p2%dH", + :cursor_address => "\e[%i%p1%d;%p2%dH", + :cursor_down => "\n", + :cursor_home => "\e[H", + :cursor_invisible => "\e[?25l", + :cursor_left => "\b", + :cursor_normal => "\e[?12l\e[?25h", + :cursor_right => "\e[C", + :cursor_up => "\e[A", + :cursor_visible => "\e[?12;25h", + :cuu => "\e[%p1%dA", + :cuu1 => "\e[A", + :cvvis => "\e[?12;25h", + :da => false, + :daisy => false, + :db => false, + :dch => "\e[%p1%dP", + :dch1 => "\e[P", + :delete_character => "\e[P", + :delete_line => "\e[M", + :dest_tabs_magic_smso => false, + :dim => "\e[2m", + :dl => "\e[%p1%dM", + :dl1 => "\e[M", + :eat_newline_glitch => true, + :ech => "\e[%p1%dX", + :ed => "\e[J", + :el => "\e[K", + :el1 => "\e[1K", + :enter_alt_charset_mode => "\e(0", + :enter_am_mode => "\e[?7h", + :enter_blink_mode => "\e[5m", + :enter_bold_mode => "\e[1m", + :enter_ca_mode => "\e[?1049h\e[22;0;0t", + :enter_dim_mode => "\e[2m", + :enter_insert_mode => "\e[4h", + :enter_italics_mode => "\e[3m", + :enter_reverse_mode => "\e[7m", + :enter_secure_mode => "\e[8m", + :enter_standout_mode => "\e[7m", + :enter_underline_mode => "\e[4m", + :eo => false, + :erase_chars => "\e[%p1%dX", + :erase_overstrike => false, + :eslok => false, + :exit_alt_charset_mode => "\e(B", + :exit_am_mode => "\e[?7l", + :exit_attribute_mode => "\e(B\e[m", + :exit_ca_mode => "\e[?1049l\e[23;0;0t", + :exit_insert_mode => "\e[4l", + :exit_italics_mode => "\e[23m", + :exit_standout_mode => "\e[27m", + :exit_underline_mode => "\e[24m", + :flash => "\e[?5h\$<100/>\e[?5l", + :flash_screen => "\e[?5h\$<100/>\e[?5l", + :generic_type => false, + :gn => false, + :hard_copy => false, + :hard_cursor => false, + :has_meta_key => true, + :has_print_wheel => false, + :has_status_line => false, + :hc => false, + :hls => false, + :home => "\e[H", + :hpa => "\e[%i%p1%dG", + :hs => false, + :ht => "\t", + :hts => "\eH", + :hue_lightness_saturation => false, + :hz => false, + :ich => "\e[%p1%d@", + :il => "\e[%p1%dL", + :il1 => "\e[L", + :in => false, + :ind => "\n", + :indn => "\e[%p1%dS", + :init_2string => "\e[!p\e[?3;4l\e[4l\e>", + :init_tabs => 8, + :insert_line => "\e[L", + :insert_null_glitch => false, + :invis => "\e[8m", + :is2 => "\e[!p\e[?3;4l\e[4l\e>", + :it => 8, + :kDC => "\e[3;2~", + :kDC3 => "\e[3;3~", + :kDC4 => "\e[3;4~", + :kDC5 => "\e[3;5~", + :kDC6 => "\e[3;6~", + :kDC7 => "\e[3;7~", + :kDN => "\e[1;2B", + :kDN3 => "\e[1;3B", + :kDN4 => "\e[1;4B", + :kDN5 => "\e[1;5B", + :kDN6 => "\e[1;6B", + :kDN7 => "\e[1;7B", + :kEND => "\e[1;2F", + :kEND3 => "\e[1;3F", + :kEND4 => "\e[1;4F", + :kEND5 => "\e[1;5F", + :kEND6 => "\e[1;6F", + :kEND7 => "\e[1;7F", + :kHOM => "\e[1;2H", + :kHOM3 => "\e[1;3H", + :kHOM4 => "\e[1;4H", + :kHOM5 => "\e[1;5H", + :kHOM6 => "\e[1;6H", + :kHOM7 => "\e[1;7H", + :kIC => "\e[2;2~", + :kIC3 => "\e[2;3~", + :kIC4 => "\e[2;4~", + :kIC5 => "\e[2;5~", + :kIC6 => "\e[2;6~", + :kIC7 => "\e[2;7~", + :kLFT => "\e[1;2D", + :kLFT3 => "\e[1;3D", + :kLFT4 => "\e[1;4D", + :kLFT5 => "\e[1;5D", + :kLFT6 => "\e[1;6D", + :kLFT7 => "\e[1;7D", + :kNXT => "\e[6;2~", + :kNXT3 => "\e[6;3~", + :kNXT4 => "\e[6;4~", + :kNXT5 => "\e[6;5~", + :kNXT6 => "\e[6;6~", + :kNXT7 => "\e[6;7~", + :kPRV => "\e[5;2~", + :kPRV3 => "\e[5;3~", + :kPRV4 => "\e[5;4~", + :kPRV5 => "\e[5;5~", + :kPRV6 => "\e[5;6~", + :kPRV7 => "\e[5;7~", + :kRIT => "\e[1;2C", + :kRIT3 => "\e[1;3C", + :kRIT4 => "\e[1;4C", + :kRIT5 => "\e[1;5C", + :kRIT6 => "\e[1;6C", + :kRIT7 => "\e[1;7C", + :kUP => "\e[1;2A", + :kUP3 => "\e[1;3A", + :kUP4 => "\e[1;4A", + :kUP5 => "\e[1;5A", + :kUP6 => "\e[1;6A", + :kUP7 => "\e[1;7A", + :ka1 => "\eOw", + :ka2 => "\eOx", + :ka3 => "\eOy", + :kb1 => "\eOt", + :kb2 => "\eOu", + :kb3 => "\eOv", + :kbs => "\b", + :kc1 => "\eOq", + :kc2 => "\eOr", + :kc3 => "\eOs", + :kcbt => "\e[Z", + :kcub1 => "\eOD", + :kcud1 => "\eOB", + :kcuf1 => "\eOC", + :kcuu1 => "\eOA", + :kdch1 => "\e[3~", + :kend => "\eOF", + :kent => "\eOM", + :key_a1 => "\eOw", + :key_a3 => "\eOy", + :key_b2 => "\eOu", + :key_backspace => "\b", + :key_btab => "\e[Z", + :key_c1 => "\eOq", + :key_c3 => "\eOs", + :key_dc => "\e[3~", + :key_down => "\eOB", + :key_end => "\eOF", + :key_enter => "\eOM", + :key_f1 => "\eOP", + :key_f10 => "\e[21~", + :key_f11 => "\e[23~", + :key_f12 => "\e[24~", + :key_f13 => "\e[1;2P", + :key_f14 => "\e[1;2Q", + :key_f15 => "\e[1;2R", + :key_f16 => "\e[1;2S", + :key_f17 => "\e[15;2~", + :key_f18 => "\e[17;2~", + :key_f19 => "\e[18;2~", + :key_f2 => "\eOQ", + :key_f20 => "\e[19;2~", + :key_f21 => "\e[20;2~", + :key_f22 => "\e[21;2~", + :key_f23 => "\e[23;2~", + :key_f24 => "\e[24;2~", + :key_f25 => "\e[1;5P", + :key_f26 => "\e[1;5Q", + :key_f27 => "\e[1;5R", + :key_f28 => "\e[1;5S", + :key_f29 => "\e[15;5~", + :key_f3 => "\eOR", + :key_f30 => "\e[17;5~", + :key_f31 => "\e[18;5~", + :key_f32 => "\e[19;5~", + :key_f33 => "\e[20;5~", + :key_f34 => "\e[21;5~", + :key_f35 => "\e[23;5~", + :key_f36 => "\e[24;5~", + :key_f37 => "\e[1;6P", + :key_f38 => "\e[1;6Q", + :key_f39 => "\e[1;6R", + :key_f4 => "\eOS", + :key_f40 => "\e[1;6S", + :key_f41 => "\e[15;6~", + :key_f42 => "\e[17;6~", + :key_f43 => "\e[18;6~", + :key_f44 => "\e[19;6~", + :key_f45 => "\e[20;6~", + :key_f46 => "\e[21;6~", + :key_f47 => "\e[23;6~", + :key_f48 => "\e[24;6~", + :key_f49 => "\e[1;3P", + :key_f5 => "\e[15~", + :key_f50 => "\e[1;3Q", + :key_f51 => "\e[1;3R", + :key_f52 => "\e[1;3S", + :key_f53 => "\e[15;3~", + :key_f54 => "\e[17;3~", + :key_f55 => "\e[18;3~", + :key_f56 => "\e[19;3~", + :key_f57 => "\e[20;3~", + :key_f58 => "\e[21;3~", + :key_f59 => "\e[23;3~", + :key_f6 => "\e[17~", + :key_f60 => "\e[24;3~", + :key_f61 => "\e[1;4P", + :key_f62 => "\e[1;4Q", + :key_f63 => "\e[1;4R", + :key_f7 => "\e[18~", + :key_f8 => "\e[19~", + :key_f9 => "\e[20~", + :key_home => "\eOH", + :key_ic => "\e[2~", + :key_left => "\eOD", + :key_mouse => "\e[<", + :key_npage => "\e[6~", + :key_ppage => "\e[5~", + :key_right => "\eOC", + :key_sdc => "\e[3;2~", + :key_send => "\e[1;2F", + :key_sf => "\e[1;2B", + :key_shome => "\e[1;2H", + :key_sic => "\e[2;2~", + :key_sleft => "\e[1;2D", + :key_snext => "\e[6;2~", + :key_sprevious => "\e[5;2~", + :key_sr => "\e[1;2A", + :key_sright => "\e[1;2C", + :key_up => "\eOA", + :keypad_local => "\e[?1l\e>", + :keypad_xmit => "\e[?1h\e=", + :kf1 => "\eOP", + :kf10 => "\e[21~", + :kf11 => "\e[23~", + :kf12 => "\e[24~", + :kf13 => "\e[1;2P", + :kf14 => "\e[1;2Q", + :kf15 => "\e[1;2R", + :kf16 => "\e[1;2S", + :kf17 => "\e[15;2~", + :kf18 => "\e[17;2~", + :kf19 => "\e[18;2~", + :kf2 => "\eOQ", + :kf20 => "\e[19;2~", + :kf21 => "\e[20;2~", + :kf22 => "\e[21;2~", + :kf23 => "\e[23;2~", + :kf24 => "\e[24;2~", + :kf25 => "\e[1;5P", + :kf26 => "\e[1;5Q", + :kf27 => "\e[1;5R", + :kf28 => "\e[1;5S", + :kf29 => "\e[15;5~", + :kf3 => "\eOR", + :kf30 => "\e[17;5~", + :kf31 => "\e[18;5~", + :kf32 => "\e[19;5~", + :kf33 => "\e[20;5~", + :kf34 => "\e[21;5~", + :kf35 => "\e[23;5~", + :kf36 => "\e[24;5~", + :kf37 => "\e[1;6P", + :kf38 => "\e[1;6Q", + :kf39 => "\e[1;6R", + :kf4 => "\eOS", + :kf40 => "\e[1;6S", + :kf41 => "\e[15;6~", + :kf42 => "\e[17;6~", + :kf43 => "\e[18;6~", + :kf44 => "\e[19;6~", + :kf45 => "\e[20;6~", + :kf46 => "\e[21;6~", + :kf47 => "\e[23;6~", + :kf48 => "\e[24;6~", + :kf49 => "\e[1;3P", + :kf5 => "\e[15~", + :kf50 => "\e[1;3Q", + :kf51 => "\e[1;3R", + :kf52 => "\e[1;3S", + :kf53 => "\e[15;3~", + :kf54 => "\e[17;3~", + :kf55 => "\e[18;3~", + :kf56 => "\e[19;3~", + :kf57 => "\e[20;3~", + :kf58 => "\e[21;3~", + :kf59 => "\e[23;3~", + :kf6 => "\e[17~", + :kf60 => "\e[24;3~", + :kf61 => "\e[1;4P", + :kf62 => "\e[1;4Q", + :kf63 => "\e[1;4R", + :kf7 => "\e[18~", + :kf8 => "\e[19~", + :kf9 => "\e[20~", + :khome => "\eOH", + :kich1 => "\e[2~", + :kind => "\e[1;2B", + :km => true, + :kmous => "\e[<", + :knp => "\e[6~", + :kp5 => "\eOE", + :kpADD => "\eOk", + :kpCMA => "\eOl", + :kpDIV => "\eOo", + :kpDOT => "\eOn", + :kpMUL => "\eOj", + :kpSUB => "\eOm", + :kpZRO => "\eOp", + :kpp => "\e[5~", + :kri => "\e[1;2A", + :lines => 24, + :lpi_changes_res => false, + :lpix => false, + :max_colors => 8, + :max_pairs => 64, + :mc0 => "\e[i", + :mc4 => "\e[4i", + :mc5 => "\e[5i", + :mc5i => true, + :meml => "\el", + :memory_above => false, + :memory_below => false, + :memory_lock => "\el", + :memory_unlock => "\em", + :memu => "\em", + :meta_off => "\e[?1034l", + :meta_on => "\e[?1034h", + :mgc => "\e[?69l", + :mir => true, + :move_insert_mode => true, + :move_standout_mode => true, + :msgr => true, + :ndscr => false, + :needs_xon_xoff => false, + :no_esc_ctlc => false, + :no_pad_char => true, + :non_dest_scroll_region => false, + :non_rev_rmcup => false, + :npc => true, + :nrrmc => false, + :nxon => false, + :op => "\e[39;49m", + :orig_pair => "\e[39;49m", + :os => false, + :over_strike => false, + :pairs => 64, + :parm_dch => "\e[%p1%dP", + :parm_delete_line => "\e[%p1%dM", + :parm_down_cursor => "\e[%p1%dB", + :parm_ich => "\e[%p1%d@", + :parm_index => "\e[%p1%dS", + :parm_insert_line => "\e[%p1%dL", + :parm_left_cursor => "\e[%p1%dD", + :parm_right_cursor => "\e[%p1%dC", + :parm_rindex => "\e[%p1%dT", + :parm_up_cursor => "\e[%p1%dA", + :print_screen => "\e[i", + :prtr_off => "\e[4i", + :prtr_on => "\e[5i", + :prtr_silent => true, + :rc => "\e8", + :rep => "%p1%c\e[%p2%{1}%-%db", + :repeat_char => "%p1%c\e[%p2%{1}%-%db", + :reset_1string => "\ec", + :reset_2string => "\e[!p\e[?3;4l\e[4l\e>", + :restore_cursor => "\e8", + :rev => "\e[7m", + :ri => "\eM", + :rin => "\e[%p1%dT", + :ritm => "\e[23m", + :rmacs => "\e(B", + :rmam => "\e[?7l", + :rmcup => "\e[?1049l\e[23;0;0t", + :rmir => "\e[4l", + :rmkx => "\e[?1l\e>", + :rmm => "\e[?1034l", + :rmso => "\e[27m", + :rmul => "\e[24m", + :rmxx => "\e[29m", + :row_addr_glitch => false, + :row_address => "\e[%i%p1%dd", + :rs1 => "\ec", + :rs2 => "\e[!p\e[?3;4l\e[4l\e>", + :sam => false, + :save_cursor => "\e7", + :sc => "\e7", + :scroll_forward => "\n", + :scroll_reverse => "\eM", + :semi_auto_right_margin => false, + :set_a_background => "\e[4%p1%dm", + :set_a_foreground => "\e[3%p1%dm", + :set_attributes => "%?%p9%t\e(0%e\e(B%;\e[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m", + :set_background => "\e[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m", + :set_foreground => "\e[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m", + :set_lr_margin => "\e[?69h\e[%i%p1%d;%p2%ds", + :set_tab => "\eH", + :setab => "\e[4%p1%dm", + :setaf => "\e[3%p1%dm", + :setb => "\e[4%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m", + :setf => "\e[3%?%p1%{1}%=%t4%e%p1%{3}%=%t6%e%p1%{4}%=%t1%e%p1%{6}%=%t3%e%p1%d%;m", + :sgr => "%?%p9%t\e(0%e\e(B%;\e[0%?%p6%t;1%;%?%p5%t;2%;%?%p2%t;4%;%?%p1%p3%|%t;7%;%?%p4%t;5%;%?%p7%t;8%;m", + :sgr0 => "\e(B\e[m", + :sitm => "\e[3m", + :smacs => "\e(0", + :smam => "\e[?7h", + :smcup => "\e[?1049h\e[22;0;0t", + :smglr => "\e[?69h\e[%i%p1%d;%p2%ds", + :smir => "\e[4h", + :smkx => "\e[?1h\e=", + :smm => "\e[?1034h", + :smso => "\e[7m", + :smul => "\e[4m", + :smxx => "\e[9m", + :status_line_esc_ok => false, + :tab => "\t", + :tbc => "\e[3g", + :tilde_glitch => false, + :transparent_underline => false, + :u6 => "\e[%i%d;%dR", + :u7 => "\e[6n", + :u8 => "\e[?%[;0123456789]c", + :u9 => "\e[c", + :ul => false, + :user6 => "\e[%i%d;%dR", + :user7 => "\e[6n", + :user8 => "\e[?%[;0123456789]c", + :user9 => "\e[c", + :vpa => "\e[%i%p1%dd", + :xenl => true, + :xhp => false, + :xhpa => false, + :xm => "\e[<%i%p3%d;%p1%d;%p2%d;%?%p4%tM%em%;", + :xon => false, + :xon_xoff => false, + :xsb => false, + :xt => false, + :xvpa => false) + +@testset "terminfo" begin + dumb = Base.TermInfo(read(IOBuffer(dumb_terminfo), Base.TermInfoRaw)) + @test dumb.names == ["dumb", "80-column dumb tty"] + @test dumb.flags == 2 + @test dumb.numbers == [true] + @test dumb.extensions == Symbol[] + @test length(dumb.capabilities) == 14 + for (key, value) in dumb_capabilities + @test dumb[key] == value + end + + xterm = Base.TermInfo(read(IOBuffer(xterm_terminfo), Base.TermInfoRaw)) + @test xterm.names == ["xterm", "xterm terminal emulator (X Window System)"] + @test xterm.flags == 38 + @test xterm.numbers == Bool[1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1] + @test sort(xterm.extensions) == sort(xterm_extensions) + @test length(xterm.capabilities) == 519 + for (key, value) in xterm_capabilities + @test xterm[key] == value + end +end + +end diff --git a/test/testdefs.jl b/test/testdefs.jl index 4aac988cda7fb..b6f49396c1a09 100644 --- a/test/testdefs.jl +++ b/test/testdefs.jl @@ -51,15 +51,15 @@ function runtests(name, path, isolate=true; seed=nothing) error(msg) end if copy(ENV) != original_env - msg = "The `$(name)` test set mutated ENV and did not restore the original values" - @error( - msg, - testset_name = name, - testset_path = path, - ) throw_error_str = get(ENV, "JULIA_TEST_CHECK_MUTATED_ENV", "true") throw_error_b = parse(Bool, throw_error_str) if throw_error_b + msg = "The `$(name)` test set mutated ENV and did not restore the original values" + @error( + msg, + testset_name = name, + testset_path = path, + ) error(msg) end end diff --git a/test/worlds.jl b/test/worlds.jl index b5a8f1c5449ac..8e820bdab88df 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -419,3 +419,40 @@ ccall(:jl_debug_method_invalidation, Any, (Cint,), 0) which(mc48954, (AbstractFloat, Int)), "jl_method_table_insert" ] + +# issue #50091 -- missing invoke edge affecting nospecialized dispatch +module ExceptionUnwrapping +@nospecialize +unwrap_exception(@nospecialize(e)) = e +unwrap_exception(e::Base.TaskFailedException) = e.task.exception +@noinline function _summarize_task_exceptions(io::IO, exc, prefix = nothing) + _summarize_exception((;prefix,), io, exc) + nothing +end +@noinline function _summarize_exception(kws, io::IO, e::TaskFailedException) + _summarize_task_exceptions(io, e.task, kws.prefix) +end +# This is the overload that prints the actual exception that occurred. +result = Bool[] +@noinline function _summarize_exception(kws, io::IO, @nospecialize(exc)) + global result + push!(result, unwrap_exception(exc) === exc) + if unwrap_exception(exc) !== exc # something uninferrable + return _summarize_exception(kws, io, unwrap_exception(exc)) + end +end +struct X; x; end +end +let e = ExceptionUnwrapping.X(nothing) + @test ExceptionUnwrapping.unwrap_exception(e) === e + ExceptionUnwrapping._summarize_task_exceptions(devnull, e) + @test ExceptionUnwrapping.result == [true] + empty!(ExceptionUnwrapping.result) +end +ExceptionUnwrapping.unwrap_exception(e::ExceptionUnwrapping.X) = e.x +let e = ExceptionUnwrapping.X(nothing) + @test !(ExceptionUnwrapping.unwrap_exception(e) === e) + ExceptionUnwrapping._summarize_task_exceptions(devnull, e) + @test ExceptionUnwrapping.result == [false, true] + empty!(ExceptionUnwrapping.result) +end