diff --git a/README.md b/README.md index a4480ecf482cd..989d8900f86c8 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ and installing Julia, below. ## Resources - **Homepage:** -- **Binaries:** +- **Install:** - **Source code:** - **Documentation:** - **Packages:** @@ -64,17 +64,22 @@ helpful to start contributing to the Julia codebase. ## Binary Installation -If you would rather not compile the latest Julia from source, -platform-specific tarballs with pre-compiled binaries are also -[available for download](https://julialang.org/downloads/). The -downloads page also provides details on the -[different tiers of support](https://julialang.org/downloads/#supported_platforms) -for OS and platform combinations. - -If everything works correctly, you will see a Julia banner and an -interactive prompt into which you can enter expressions for -evaluation. You can read about [getting -started](https://docs.julialang.org/en/v1/manual/getting-started/) in the manual. +The recommended way of installing Julia is to use `juliaup` which will install +the latest stable `julia` for you and help keep it up to date. It can also let +you install and run different Julia versions simultaneously. Instructions for +this can be find [here](https://julialang.org/install/). If you want to manually +download specific Julia binaries, you can find those on the [downloads +page](https://julialang.org/downloads/). The downloads page also provides +details on the [different tiers of +support](https://julialang.org/downloads/#supported_platforms) for OS and +platform combinations. + +If everything works correctly, you will get a `julia` program and when you run +it in a terminal or command prompt, you will see a Julia banner and an +interactive prompt into which you can enter expressions for evaluation. You can +read about [getting +started](https://docs.julialang.org/en/v1/manual/getting-started/) in the +manual. **Note**: Although some system package managers provide Julia, such installations are neither maintained nor endorsed by the Julia diff --git a/THIRDPARTY.md b/THIRDPARTY.md index 51950d9e2c6a1..07b9f69f26e3e 100644 --- a/THIRDPARTY.md +++ b/THIRDPARTY.md @@ -6,7 +6,6 @@ for exceptions. - [crc32c.c](https://stackoverflow.com/questions/17645167/implementing-sse-4-2s-crc32c-in-software) (CRC-32c checksum code by Mark Adler) [[ZLib](https://opensource.org/licenses/Zlib)]. - [LDC](https://github.com/ldc-developers/ldc/blob/master/LICENSE) (for ccall/cfunction ABI definitions) [BSD-3]. The portion of code that Julia uses from LDC is [BSD-3] licensed. - [LLVM](https://releases.llvm.org/3.9.0/LICENSE.TXT) (for parts of src/disasm.cpp) [UIUC] -- [MINGW](https://sourceforge.net/p/mingw/mingw-org-wsl/ci/legacy/tree/mingwrt/mingwex/dirname.c) (for dirname implementation on Windows) [MIT] - [NetBSD](https://www.netbsd.org/about/redistribution.html) (for setjmp, longjmp, and strptime implementations on Windows) [BSD-3] - [Python](https://docs.python.org/3/license.html) (for strtod implementation on Windows) [PSF] - [FEMTOLISP](https://github.com/JeffBezanson/femtolisp) [BSD-3] diff --git a/base/Base.jl b/base/Base.jl index 6ed423a799e4e..4de06175a6a6f 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -330,10 +330,6 @@ using .Libc: getpid, gethostname, time, memcpy, memset, memmove, memcmp const libblas_name = "libblastrampoline" * (Sys.iswindows() ? "-5" : "") const liblapack_name = libblas_name -# Logging -include("logging.jl") -using .CoreLogging - # Concurrency include("linked_list.jl") include("condition.jl") @@ -345,6 +341,10 @@ include("task.jl") include("threads_overloads.jl") include("weakkeydict.jl") +# Logging +include("logging.jl") +using .CoreLogging + include("env.jl") # functions defined in Random diff --git a/base/abstractarray.jl b/base/abstractarray.jl index d993c7247dbc0..bc98ebdac5db9 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -3335,12 +3335,19 @@ function ith_all(i, as) end function map_n!(f::F, dest::AbstractArray, As) where F - idxs1 = LinearIndices(As[1]) - @boundscheck LinearIndices(dest) == idxs1 && all(x -> LinearIndices(x) == idxs1, As) - for i = idxs1 - @inbounds I = ith_all(i, As) - val = f(I...) - @inbounds dest[i] = val + idxs = LinearIndices(dest) + if all(x -> LinearIndices(x) == idxs, As) + for i in idxs + @inbounds as = ith_all(i, As) + val = f(as...) + @inbounds dest[i] = val + end + else + for (i, Is...) in zip(eachindex(dest), map(eachindex, As)...) + as = ntuple(j->getindex(As[j], Is[j]), length(As)) + val = f(as...) + dest[i] = val + end end return dest end @@ -3536,7 +3543,31 @@ function _keepat!(a::AbstractVector, m::AbstractVector{Bool}) deleteat!(a, j:lastindex(a)) end -## 1-d circshift ## +""" + circshift!(a::AbstractVector, shift::Integer) + +Circularly shift, or rotate, the data in vector `a` by `shift` positions. + +# Examples + +```jldoctest +julia> circshift!([1, 2, 3, 4, 5], 2) +5-element Vector{Int64}: + 4 + 5 + 1 + 2 + 3 + +julia> circshift!([1, 2, 3, 4, 5], -2) +5-element Vector{Int64}: + 3 + 4 + 5 + 1 + 2 +``` +""" function circshift!(a::AbstractVector, shift::Integer) n = length(a) n == 0 && return diff --git a/base/accumulate.jl b/base/accumulate.jl index a2d8a1d368d86..4265f6ef67cc8 100644 --- a/base/accumulate.jl +++ b/base/accumulate.jl @@ -5,12 +5,14 @@ # it does double the number of operations compared to accumulate, # though for cheap operations like + this does not have much impact (20%) function _accumulate_pairwise!(op::Op, c::AbstractVector{T}, v::AbstractVector, s, i1, n)::T where {T,Op} - @inbounds if n < 128 - s_ = v[i1] - c[i1] = op(s, s_) + if n < 128 + @inbounds s_ = v[i1] + ci1 = op(s, s_) + @inbounds c[i1] = ci1 for i = i1+1:i1+n-1 - s_ = op(s_, v[i]) - c[i] = op(s, s_) + s_ = op(s_, @inbounds(v[i])) + ci = op(s, s_) + @inbounds c[i] = ci end else n2 = n >> 1 @@ -26,7 +28,8 @@ function accumulate_pairwise!(op::Op, result::AbstractVector, v::AbstractVector) n = length(li) n == 0 && return result i1 = first(li) - @inbounds result[i1] = v1 = reduce_first(op,v[i1]) + v1 = reduce_first(op, @inbounds(v[i1])) + @inbounds result[i1] = v1 n == 1 && return result _accumulate_pairwise!(op, result, v, v1, i1+1, n-1) return result @@ -379,16 +382,16 @@ function _accumulate!(op, B, A, dims::Integer, init::Union{Nothing, Some}) # We can accumulate to a temporary variable, which allows # register usage and will be slightly faster ind1 = inds_t[1] - @inbounds for I in CartesianIndices(tail(inds_t)) + for I in CartesianIndices(tail(inds_t)) if init === nothing - tmp = reduce_first(op, A[first(ind1), I]) + tmp = reduce_first(op, @inbounds(A[first(ind1), I])) else - tmp = op(something(init), A[first(ind1), I]) + tmp = op(something(init), @inbounds(A[first(ind1), I])) end - B[first(ind1), I] = tmp + @inbounds B[first(ind1), I] = tmp for i_1 = first(ind1)+1:last(ind1) - tmp = op(tmp, A[i_1, I]) - B[i_1, I] = tmp + tmp = op(tmp, @inbounds(A[i_1, I])) + @inbounds B[i_1, I] = tmp end end else @@ -402,12 +405,15 @@ end @noinline function _accumulaten!(op, B, A, R1, ind, R2, init::Nothing) # Copy the initial element in each 1d vector along dimension `dim` ii = first(ind) - @inbounds for J in R2, I in R1 - B[I, ii, J] = reduce_first(op, A[I, ii, J]) + for J in R2, I in R1 + tmp = reduce_first(op, @inbounds(A[I, ii, J])) + @inbounds B[I, ii, J] = tmp end # Accumulate - @inbounds for J in R2, i in first(ind)+1:last(ind), I in R1 - B[I, i, J] = op(B[I, i-1, J], A[I, i, J]) + for J in R2, i in first(ind)+1:last(ind), I in R1 + @inbounds Bv, Av = B[I, i-1, J], A[I, i, J] + tmp = op(Bv, Av) + @inbounds B[I, i, J] = tmp end B end @@ -415,12 +421,15 @@ end @noinline function _accumulaten!(op, B, A, R1, ind, R2, init::Some) # Copy the initial element in each 1d vector along dimension `dim` ii = first(ind) - @inbounds for J in R2, I in R1 - B[I, ii, J] = op(something(init), A[I, ii, J]) + for J in R2, I in R1 + tmp = op(something(init), @inbounds(A[I, ii, J])) + @inbounds B[I, ii, J] = tmp end # Accumulate - @inbounds for J in R2, i in first(ind)+1:last(ind), I in R1 - B[I, i, J] = op(B[I, i-1, J], A[I, i, J]) + for J in R2, i in first(ind)+1:last(ind), I in R1 + @inbounds Bv, Av = B[I, i-1, J], A[I, i, J] + tmp = op(Bv, Av) + @inbounds B[I, i, J] = tmp end B end @@ -434,10 +443,10 @@ function _accumulate1!(op, B, v1, A::AbstractVector, dim::Integer) cur_val = v1 B[i1] = cur_val next = iterate(inds, state) - @inbounds while next !== nothing + while next !== nothing (i, state) = next - cur_val = op(cur_val, A[i]) - B[i] = cur_val + cur_val = op(cur_val, @inbounds(A[i])) + @inbounds B[i] = cur_val next = iterate(inds, state) end return B diff --git a/base/arraymath.jl b/base/arraymath.jl index 62dc3772e4938..53a7d132a2c0c 100644 --- a/base/arraymath.jl +++ b/base/arraymath.jl @@ -72,12 +72,12 @@ _reverse!(A::AbstractArray{<:Any,N}, ::Colon) where {N} = _reverse!(A, ntuple(id _reverse!(A, dim::Integer) = _reverse!(A, (Int(dim),)) _reverse!(A, dims::NTuple{M,Integer}) where {M} = _reverse!(A, Int.(dims)) function _reverse!(A::AbstractArray{<:Any,N}, dims::NTuple{M,Int}) where {N,M} + dims === () && return A # nothing to reverse dimrev = ntuple(k -> k in dims, Val{N}()) # boolean tuple indicating reversed dims if N < M || M != sum(dimrev) throw(ArgumentError("invalid dimensions $dims in reverse!")) end - M == 0 && return A # nothing to reverse # swapping loop only needs to traverse ≈half of the array halfsz = ntuple(k -> k == dims[1] ? size(A,k) ÷ 2 : size(A,k), Val{N}()) diff --git a/base/channels.jl b/base/channels.jl index 75207e9fac76b..fab01ec52758b 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -61,7 +61,7 @@ Channel(sz=0) = Channel{Any}(sz) """ Channel{T=Any}(func::Function, size=0; taskref=nothing, spawn=false, threadpool=nothing) -Create a new task from `func`, bind it to a new channel of type +Create a new task from `func`, [`bind`](@ref) it to a new channel of type `T` and size `size`, and schedule the task, all in a single call. The channel is automatically closed when the task terminates. diff --git a/base/cmd.jl b/base/cmd.jl index 475a62a82d4d7..d2aa279aaca40 100644 --- a/base/cmd.jl +++ b/base/cmd.jl @@ -497,7 +497,7 @@ julia> run(cm) Process(`echo 1`, ProcessExited(0)) ``` """ -macro cmd(str) +macro cmd(str::String) cmd_ex = shell_parse(str, special=shell_special, filename=String(__source__.file))[1] return :(cmd_gen($(esc(cmd_ex)))) end diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 49149fde59691..192d4b218cdde 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -3065,7 +3065,7 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) rt = abstract_eval_value(interp, stmt.val, currstate, frame) rt = widenreturn(rt, BestguessInfo(interp, bestguess, nargs, slottypes, currstate)) # narrow representation of bestguess slightly to prepare for tmerge with rt - if rt isa InterConditional && bestguess isa Const + if rt isa InterConditional && bestguess isa Const && bestguess.val isa Bool let slot_id = rt.slot old_id_type = slottypes[slot_id] if bestguess.val === true && rt.elsetype !== Bottom @@ -3074,6 +3074,15 @@ function typeinf_local(interp::AbstractInterpreter, frame::InferenceState) bestguess = InterConditional(slot_id, Bottom, old_id_type) end end + # or narrow representation of rt slightly to prepare for tmerge with bestguess + elseif bestguess isa InterConditional && rt isa Const && rt.val isa Bool + slot_id = bestguess.slot + old_id_type = widenconditional(slottypes[slot_id]) + if rt.val === true && bestguess.elsetype !== Bottom + rt = InterConditional(slot_id, old_id_type, Bottom) + elseif rt.val === false && bestguess.thentype !== Bottom + rt = InterConditional(slot_id, Bottom, old_id_type) + end end # copy limitations to return value if !isempty(frame.pclimitations) diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 92e6b0dd5f666..f00d8275493e9 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -406,7 +406,7 @@ function cache_result!(interp::AbstractInterpreter, result::InferenceResult) code_cache(interp)[linfo] = ci = CodeInstance(interp, result, inferred_result, valid_worlds) if track_newly_inferred[] m = linfo.def - if isa(m, Method) && m.module != Core + if isa(m, Method) ccall(:jl_push_newly_inferred, Cvoid, (Any,), ci) end end diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl index df6022609d612..12de472298326 100644 --- a/base/compiler/typelattice.jl +++ b/base/compiler/typelattice.jl @@ -427,6 +427,13 @@ end end a = Bool elseif isa(b, ConditionalT) + if isa(a, Const) && isa(a.val, Bool) + if (a.val === true && b.thentype === Any && b.elsetype === Bottom) || + (a.val === false && b.elsetype === Any && b.thentype === Bottom) + # this Conditional contains distinctly no lattice information, and is simply an alternative representation of the Const Bool used for internal tracking purposes + return true + end + end return false end return ⊑(widenlattice(lattice), a, b) diff --git a/base/generator.jl b/base/generator.jl index aa4b7f67cba95..da735bf59bccf 100644 --- a/base/generator.jl +++ b/base/generator.jl @@ -97,7 +97,7 @@ IteratorSize(::Type{Any}) = SizeUnknown() IteratorSize(::Type{<:Tuple}) = HasLength() IteratorSize(::Type{<:AbstractArray{<:Any,N}}) where {N} = HasShape{N}() -IteratorSize(::Type{Generator{I,F}}) where {I,F} = IteratorSize(I) +IteratorSize(::Type{<:Generator{I}}) where {I} = (@isdefined I) ? IteratorSize(I) : SizeUnknown() haslength(iter) = IteratorSize(iter) isa Union{HasShape, HasLength} diff --git a/base/int.jl b/base/int.jl index 4b2f542bba788..5e43761eeee13 100644 --- a/base/int.jl +++ b/base/int.jl @@ -286,8 +286,14 @@ function mod(x::T, y::T) where T<:Integer y == -1 && return T(0) # avoid potential overflow in fld return x - fld(x, y) * y end -mod(x::BitSigned, y::Unsigned) = rem(y + unsigned(rem(x, y)), y) -mod(x::Unsigned, y::Signed) = rem(y + signed(rem(x, y)), y) +function mod(x::BitSigned, y::Unsigned) + remval = rem(x, y) # correct iff remval>=0 + return unsigned(remval + (remval0 so correct iff y>0 or remval==0 + return remval + (!iszero(remval) && y typemax(p) && throw(DomainError(x,"argument is beyond the range of type of the base")) + hastypemax(typeof(p)) && x > typemax(p) && + throw(DomainError(x,"argument is beyond the range of type of the base")) p >= x && return p wp = a^n wp > p || throw(OverflowError("result is beyond the range of type of the base")) @@ -529,9 +530,10 @@ function prevpow(a::T, x::Real) where T <: Real n = floor(Integer,log(a, x)) # round-off error of log can go either direction, so need some checks p = a^n - x > typemax(p) && throw(DomainError(x,"argument is beyond the range of type of the base")) + hastypemax(typeof(p)) && x > typemax(p) && + throw(DomainError(x,"argument is beyond the range of type of the base")) if a isa Integer - wp, overflow = mul_with_overflow(a, p) + wp, overflow = mul_with_overflow(promote(a, p)...) wp <= x && !overflow && return wp else wp = a^(n+1) @@ -763,7 +765,7 @@ function append_c_digits(olength::Int, digits::Unsigned, buf, pos::Int) while i >= 2 d, c = divrem(digits, 0x64) digits = oftype(digits, d) - @inbounds d100 = _dec_d100[(c % Int) + 1] + @inbounds d100 = _dec_d100[(c % Int)::Int + 1] @inbounds buf[pos + i - 2] = d100 % UInt8 @inbounds buf[pos + i - 1] = (d100 >> 0x8) % UInt8 i -= 2 diff --git a/base/logging.jl b/base/logging.jl index 96c133a239cc0..49f0a52600102 100644 --- a/base/logging.jl +++ b/base/logging.jl @@ -131,6 +131,7 @@ isless(a::LogLevel, b::LogLevel) = isless(a.level, b.level) +(level::LogLevel, inc::Integer) = LogLevel(level.level+inc) -(level::LogLevel, inc::Integer) = LogLevel(level.level-inc) convert(::Type{LogLevel}, level::Integer) = LogLevel(level) +convert(::Type{Int32}, level::LogLevel) = level.level const BelowMinLevel = LogLevel(-1000001) """ @@ -160,7 +161,8 @@ const Error = LogLevel( 2000) const AboveMaxLevel = LogLevel( 1000001) # Global log limiting mechanism for super fast but inflexible global log limiting. -const _min_enabled_level = Ref{LogLevel}(Debug) +# Atomic ensures that the value is always consistent across threads. +const _min_enabled_level = Threads.Atomic{Int32}(Debug) function show(io::IO, level::LogLevel) if level == BelowMinLevel print(io, "BelowMinLevel") @@ -383,7 +385,7 @@ function logmsg_code(_module, file, line, level, message, exs...) level = $level # simplify std_level code emitted, if we know it is one of our global constants std_level = $(level isa Symbol ? :level : :(level isa $LogLevel ? level : convert($LogLevel, level)::$LogLevel)) - if std_level >= $(_min_enabled_level)[] + if std_level.level >= $(_min_enabled_level)[] group = $(log_data._group) _module = $(log_data._module) logger = $(current_logger_for_env)(std_level, group, _module) @@ -526,7 +528,8 @@ end Disable all log messages at log levels equal to or less than `level`. This is a *global* setting, intended to make debug logging extremely cheap when -disabled. +disabled. Note that this cannot be used to enable logging that is currently disabled +by other mechanisms. # Examples ```julia @@ -646,17 +649,21 @@ close(closed_stream) Simplistic logger for logging all messages with level greater than or equal to `min_level` to `stream`. If stream is closed then messages with log level greater or equal to `Warn` will be logged to `stderr` and below to `stdout`. + +This Logger is thread-safe, with a lock taken around orchestration of message +limits i.e. `maxlog`, and writes to the stream. """ struct SimpleLogger <: AbstractLogger stream::IO + lock::ReentrantLock min_level::LogLevel message_limits::Dict{Any,Int} end -SimpleLogger(stream::IO, level=Info) = SimpleLogger(stream, level, Dict{Any,Int}()) +SimpleLogger(stream::IO, level=Info) = SimpleLogger(stream, ReentrantLock(), level, Dict{Any,Int}()) SimpleLogger(level=Info) = SimpleLogger(closed_stream, level) shouldlog(logger::SimpleLogger, level, _module, group, id) = - get(logger.message_limits, id, 1) > 0 + @lock logger.lock get(logger.message_limits, id, 1) > 0 min_enabled_level(logger::SimpleLogger) = logger.min_level @@ -667,15 +674,14 @@ function handle_message(logger::SimpleLogger, level::LogLevel, message, _module, @nospecialize maxlog = get(kwargs, :maxlog, nothing) if maxlog isa Core.BuiltinInts - remaining = get!(logger.message_limits, id, Int(maxlog)::Int) - logger.message_limits[id] = remaining - 1 - remaining > 0 || return + @lock logger.lock begin + remaining = get!(logger.message_limits, id, Int(maxlog)::Int) + remaining == 0 && return + logger.message_limits[id] = remaining - 1 + end end buf = IOBuffer() stream::IO = logger.stream - if !(isopen(stream)::Bool) - stream = stderr - end iob = IOContext(buf, stream) levelstr = level == Warn ? "Warning" : string(level) msglines = eachsplit(chomp(convert(String, string(message))::String), '\n') @@ -689,7 +695,13 @@ function handle_message(logger::SimpleLogger, level::LogLevel, message, _module, println(iob, "│ ", key, " = ", val) end println(iob, "└ @ ", _module, " ", filepath, ":", line) - write(stream, take!(buf)) + b = take!(buf) + @lock logger.lock begin + if !(isopen(stream)::Bool) + stream = stderr + end + write(stream, b) + end nothing end diff --git a/base/math.jl b/base/math.jl index 0f917dce7c99c..0e2b7fb7c6397 100644 --- a/base/math.jl +++ b/base/math.jl @@ -1574,7 +1574,7 @@ for f in (:sin, :cos, :tan, :asin, :atan, :acos, :exponent, :sqrt, :cbrt, :sinpi, :cospi, :sincospi, :tanpi) @eval function ($f)(x::Real) xf = float(x) - x === xf && throw(MethodError($f, (x,))) + xf isa typeof(x) && throw(MethodError($f, (x,))) return ($f)(xf) end @eval $(f)(::Missing) = missing diff --git a/base/namedtuple.jl b/base/namedtuple.jl index 687ada6417e11..463051d0af056 100644 --- a/base/namedtuple.jl +++ b/base/namedtuple.jl @@ -193,9 +193,8 @@ function convert(::Type{NT}, nt::NamedTuple{names}) where {names, NT<:NamedTuple end if nameof(@__MODULE__) === :Base - Tuple(nt::NamedTuple) = (nt...,) - (::Type{T})(nt::NamedTuple) where {T <: Tuple} = (t = Tuple(nt); t isa T ? t : convert(T, t)::T) -end +Tuple(nt::NamedTuple) = (nt...,) +(::Type{T})(nt::NamedTuple) where {T <: Tuple} = (t = Tuple(nt); t isa T ? t : convert(T, t)::T) function show(io::IO, t::NamedTuple) n = nfields(t) @@ -229,6 +228,7 @@ function show(io::IO, t::NamedTuple) print(io, ")") end end +end eltype(::Type{T}) where T<:NamedTuple = nteltype(T) nteltype(::Type) = Any diff --git a/base/reflection.jl b/base/reflection.jl index 7a1a9836b3681..6f32d8bad5aaa 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -1851,11 +1851,11 @@ function hasmethod(f, t, kwnames::Tuple{Vararg{Symbol}}; world::UInt=get_world_c match = ccall(:jl_gf_invoke_lookup, Any, (Any, Any, UInt), tt, nothing, world) match === nothing && return false kws = ccall(:jl_uncompress_argnames, Array{Symbol,1}, (Any,), (match::Method).slot_syms) + kws = kws[((match::Method).nargs + 1):end] # remove positional arguments isempty(kws) && return true # some kwfuncs simply forward everything directly for kw in kws endswith(String(kw), "...") && return true end - kwnames = Symbol[kwnames[i] for i in 1:length(kwnames)] return issubset(kwnames, kws) end diff --git a/base/regex.jl b/base/regex.jl index c8d66265e0784..e8af2f41804d4 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -39,7 +39,13 @@ mutable struct Regex <: AbstractPattern end re = compile(new(pattern, compile_options, match_options, C_NULL)) finalizer(re) do re - re.regex == C_NULL || PCRE.free_re(re.regex) + # don't free during exit because tasks may still be running and + # using it. Issue #57817. During sysimage creation _atexit_hooks_finished + # is not defined but threads aren't running so just always run + during_exit = @isdefined(_atexit_hooks_finished) && _atexit_hooks_finished + if re.regex != C_NULL && !during_exit + PCRE.free_re(re.regex) + end end re end diff --git a/base/shell.jl b/base/shell.jl index 48214418bdee5..3d72ffc253011 100644 --- a/base/shell.jl +++ b/base/shell.jl @@ -315,7 +315,7 @@ function shell_escape_csh(io::IO, args::AbstractString...) end shell_escape_csh(args::AbstractString...) = sprint(shell_escape_csh, args...; - sizehint = sum(sizeof.(args)) + length(args) * 3) + sizehint = sum(sizeof, args) + length(args) * 3) """ shell_escape_wincmd(s::AbstractString) @@ -465,4 +465,4 @@ function escape_microsoft_c_args(io::IO, args::AbstractString...) end escape_microsoft_c_args(args::AbstractString...) = sprint(escape_microsoft_c_args, args...; - sizehint = (sum(sizeof.(args)) + 3*length(args))) + sizehint = (sum(sizeof, args) + 3*length(args))) diff --git a/base/show.jl b/base/show.jl index 6e9089f6a3b99..dd728a1eb31dd 100644 --- a/base/show.jl +++ b/base/show.jl @@ -1744,7 +1744,7 @@ function show_sym(io::IO, sym::Symbol; allow_macroname=false) print(io, '@') show_sym(io, Symbol(sym_str[2:end])) else - print(io, "var", repr(string(sym))) + print(io, "var", repr(string(sym))) # TODO: this is not quite right, since repr uses String escaping rules, and Symbol uses raw string rules end end diff --git a/base/special/exp.jl b/base/special/exp.jl index 32de6b9be296d..44a022b9400ee 100644 --- a/base/special/exp.jl +++ b/base/special/exp.jl @@ -224,7 +224,7 @@ end twopk = (k + UInt64(53)) << 52 return reinterpret(T, twopk + reinterpret(UInt64, small_part))*0x1p-53 end - #k == 1024 && return (small_part * 2.0) * 2.0^1023 + #k == 1024 && return (small_part * 2.0) * 0x1p1023 end twopk = Int64(k) << 52 return reinterpret(T, twopk + reinterpret(Int64, small_part)) @@ -252,7 +252,7 @@ end twopk = (k + UInt64(53)) << 52 return reinterpret(T, twopk + reinterpret(UInt64, small_part))*0x1p-53 end - #k == 1024 && return (small_part * 2.0) * 2.0^1023 + # k == 1024 && return (small_part * 2.0) * 0x1p1023 end twopk = Int64(k) << 52 return reinterpret(T, twopk + reinterpret(Int64, small_part)) diff --git a/base/strings/basic.jl b/base/strings/basic.jl index 2609edeaaaa18..72088e0e06153 100644 --- a/base/strings/basic.jl +++ b/base/strings/basic.jl @@ -498,11 +498,11 @@ prevind(s::AbstractString, i::Int) = prevind(s, i, 1) function prevind(s::AbstractString, i::Int, n::Int) n < 0 && throw(ArgumentError("n cannot be negative: $n")) - z = ncodeunits(s) + 1 + z = ncodeunits(s)::Int + 1 @boundscheck 0 < i ≤ z || throw(BoundsError(s, i)) - n == 0 && return thisind(s, i) == i ? i : string_index_err(s, i) + n == 0 && return thisind(s, i)::Int == i ? i : string_index_err(s, i) while n > 0 && 1 < i - @inbounds n -= isvalid(s, i -= 1) + @inbounds n -= isvalid(s, i -= 1)::Bool end return i - n end @@ -557,11 +557,11 @@ nextind(s::AbstractString, i::Int) = nextind(s, i, 1) function nextind(s::AbstractString, i::Int, n::Int) n < 0 && throw(ArgumentError("n cannot be negative: $n")) - z = ncodeunits(s) + z = ncodeunits(s)::Int @boundscheck 0 ≤ i ≤ z || throw(BoundsError(s, i)) - n == 0 && return thisind(s, i) == i ? i : string_index_err(s, i) + n == 0 && return thisind(s, i)::Int == i ? i : string_index_err(s, i) while n > 0 && i < z - @inbounds n -= isvalid(s, i += 1) + @inbounds n -= isvalid(s, i += 1)::Bool end return i + n end diff --git a/base/strings/io.jl b/base/strings/io.jl index 987a64798d3da..2bb19c7e249c7 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -557,7 +557,7 @@ julia> v[2] 0x32 ``` """ -macro b_str(s) +macro b_str(s::String) v = codeunits(unescape_string(s)) QuoteNode(v) end diff --git a/base/timing.jl b/base/timing.jl index eb56ba2a9521f..7dfd73a5b2640 100644 --- a/base/timing.jl +++ b/base/timing.jl @@ -511,11 +511,12 @@ end # here so it's possible to time/trace all imports, including InteractiveUtils and its deps macro time_imports(ex) quote - try - Base.Threads.atomic_add!(Base.TIMING_IMPORTS, 1) - $(esc(ex)) - finally + Base.Threads.atomic_add!(Base.TIMING_IMPORTS, 1) + @__tryfinally( + # try + $(esc(ex)), + # finally Base.Threads.atomic_sub!(Base.TIMING_IMPORTS, 1) - end + ) end end diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 7ad976e1e0106..8785696138a7d 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -74,6 +74,7 @@ print("") printstyled("a", "b") display([1]) display([1 2; 3 4]) +display("a string") foo(x) = 1 @time @eval foo(1) ; pwd diff --git a/deps/checksums/Pkg-0b3af4592d7158d4cba1132a9cc9df15e8e51706.tar.gz/md5 b/deps/checksums/Pkg-0b3af4592d7158d4cba1132a9cc9df15e8e51706.tar.gz/md5 deleted file mode 100644 index 4a0ebe571c2f4..0000000000000 --- a/deps/checksums/Pkg-0b3af4592d7158d4cba1132a9cc9df15e8e51706.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -7269046040e860f243cf379ad1b4291d diff --git a/deps/checksums/Pkg-0b3af4592d7158d4cba1132a9cc9df15e8e51706.tar.gz/sha512 b/deps/checksums/Pkg-0b3af4592d7158d4cba1132a9cc9df15e8e51706.tar.gz/sha512 deleted file mode 100644 index 46348fa21d5a3..0000000000000 --- a/deps/checksums/Pkg-0b3af4592d7158d4cba1132a9cc9df15e8e51706.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -31853a2d083a576ddacf2e0318fc5f3ca982b8b7ce2da88ce0fb20617ff2e4adcb3973bcf0d793d131f61104418da0471dd23f7912c65e406881d76f2e0b6914 diff --git a/deps/checksums/Pkg-2525c69000ea321c80eca26a3c4f2f0b1ba11e0e.tar.gz/md5 b/deps/checksums/Pkg-2525c69000ea321c80eca26a3c4f2f0b1ba11e0e.tar.gz/md5 new file mode 100644 index 0000000000000..f1a40d23aa48f --- /dev/null +++ b/deps/checksums/Pkg-2525c69000ea321c80eca26a3c4f2f0b1ba11e0e.tar.gz/md5 @@ -0,0 +1 @@ +3c84ff9961b0b0bba68446a6c1b9101b diff --git a/deps/checksums/Pkg-2525c69000ea321c80eca26a3c4f2f0b1ba11e0e.tar.gz/sha512 b/deps/checksums/Pkg-2525c69000ea321c80eca26a3c4f2f0b1ba11e0e.tar.gz/sha512 new file mode 100644 index 0000000000000..ec48ec9407c61 --- /dev/null +++ b/deps/checksums/Pkg-2525c69000ea321c80eca26a3c4f2f0b1ba11e0e.tar.gz/sha512 @@ -0,0 +1 @@ +5e82f3ae975f149e012eb0ba1ccf1032b51b1c939b2a71912914cbd65e9c2744a19443db04eaac6e7bfe39a24ee1b9865dbee05b62c7dbabcca5108b0ce192c6 diff --git a/deps/checksums/openlibm b/deps/checksums/openlibm index 22d35d23ea7aa..cad61fd42cf94 100644 --- a/deps/checksums/openlibm +++ b/deps/checksums/openlibm @@ -1,34 +1,38 @@ -OpenLibm.v0.8.1+4.aarch64-apple-darwin.tar.gz/md5/f7f8947d276e50b58db7530e050062fd -OpenLibm.v0.8.1+4.aarch64-apple-darwin.tar.gz/sha512/9e28c02d1aa98fdff8e637c9d1a577325d76c098bc89837b5bd3a50ec05647ab8379893a7945d570907b82ef9446804a0065403b6ddec8ede4cb9b0acf5b27ea -OpenLibm.v0.8.1+4.aarch64-linux-gnu.tar.gz/md5/bf9e4bc08aa8488319724b9a3011be8b -OpenLibm.v0.8.1+4.aarch64-linux-gnu.tar.gz/sha512/773263ff709fbdbd07cde5049372175f7cf8fefa26a5fae0fdc48990886911e8c9bcfdb8b05158f38621c79a17e445ad73d41cc4186f6d9df84a3e8232a8a17d -OpenLibm.v0.8.1+4.aarch64-linux-musl.tar.gz/md5/eab078c285039562f65f8fc1d8d62fd9 -OpenLibm.v0.8.1+4.aarch64-linux-musl.tar.gz/sha512/c0b818d728f85ef1ed4231dc2a3bc78e95de9186829d12012b168505b6d6505e4732a379c67a5a3c37e40e048bbaadabc00d153383347e9f606fc9731ebb26d5 -OpenLibm.v0.8.1+4.armv6l-linux-gnueabihf.tar.gz/md5/c76f4ca7bbcd276e5769bd73bee00966 -OpenLibm.v0.8.1+4.armv6l-linux-gnueabihf.tar.gz/sha512/0a711684d1289c747ecadeee0fa1c8ca6067727a5ab5d531f7c30f998c8ee04803c6712067426c939f50698c15104724214eaa3953e522a6dc210c39954f2727 -OpenLibm.v0.8.1+4.armv6l-linux-musleabihf.tar.gz/md5/e072d09a671f98bede25cb0bd00a2fd4 -OpenLibm.v0.8.1+4.armv6l-linux-musleabihf.tar.gz/sha512/37a741e5f66ca4dc8c9358376afb562c7d2a1bb1fcbde2c9b555001a4e6faffc0446b5faff33aead192d4b2fc8cd45c8261b06cc40abec73e39dc63da932b8ab -OpenLibm.v0.8.1+4.armv7l-linux-gnueabihf.tar.gz/md5/c76f4ca7bbcd276e5769bd73bee00966 -OpenLibm.v0.8.1+4.armv7l-linux-gnueabihf.tar.gz/sha512/0a711684d1289c747ecadeee0fa1c8ca6067727a5ab5d531f7c30f998c8ee04803c6712067426c939f50698c15104724214eaa3953e522a6dc210c39954f2727 -OpenLibm.v0.8.1+4.armv7l-linux-musleabihf.tar.gz/md5/e072d09a671f98bede25cb0bd00a2fd4 -OpenLibm.v0.8.1+4.armv7l-linux-musleabihf.tar.gz/sha512/37a741e5f66ca4dc8c9358376afb562c7d2a1bb1fcbde2c9b555001a4e6faffc0446b5faff33aead192d4b2fc8cd45c8261b06cc40abec73e39dc63da932b8ab -OpenLibm.v0.8.1+4.i686-linux-gnu.tar.gz/md5/d8602652f0f347a16e39a869eb2ccb83 -OpenLibm.v0.8.1+4.i686-linux-gnu.tar.gz/sha512/b140032c96497c7e6626751b799598bd54f687af3976bc43ac7ddb6f490527dca64fd44a125da2f54d8ca3679ad53d2700d412ec4c2ddcfe7bfbfe719c0cfc05 -OpenLibm.v0.8.1+4.i686-linux-musl.tar.gz/md5/5d20637487e85b529e3901d129b586a7 -OpenLibm.v0.8.1+4.i686-linux-musl.tar.gz/sha512/b83742801cdfee62bf14bf66f4d20e42219899d431ab335c23f2943c00363c82ee2ab1b853ec421eb825dbe65899fc8f3a3c91c3d86c5e4891c60ddc981d0831 -OpenLibm.v0.8.1+4.i686-w64-mingw32.tar.gz/md5/6ba08a1d5b3aa21fa58618f9ea2a9b8d -OpenLibm.v0.8.1+4.i686-w64-mingw32.tar.gz/sha512/63bed0bc519fa87abae157b53f439f8b452720090780f306b62ff802b8f8ddc4b546899aad5b3e91dacdc3439b7a10a36080d34f09f072719f766c3cdb0baa82 -OpenLibm.v0.8.1+4.powerpc64le-linux-gnu.tar.gz/md5/c9c690ff59b202763ee901f22a798d69 -OpenLibm.v0.8.1+4.powerpc64le-linux-gnu.tar.gz/sha512/83a1e37a62dcff357836385f55f9410cf49b10445c8320b62381359557306c97f11773c508e816fb0bbd4de372b6666833e960921959b6dbb4c7c954e230004a -OpenLibm.v0.8.1+4.x86_64-apple-darwin.tar.gz/md5/bbe8f7a039ad3f07baca340112ea2e65 -OpenLibm.v0.8.1+4.x86_64-apple-darwin.tar.gz/sha512/89e578b7e8e56e7152c9f5881fbf51b33833f8e2143126212e60db262de5641573f33dea5b810575a39e21ec4fbaaa307a049d42e06970c7d4e052b97622938a -OpenLibm.v0.8.1+4.x86_64-linux-gnu.tar.gz/md5/94f42c769349c92987ed3bd9f35d96e0 -OpenLibm.v0.8.1+4.x86_64-linux-gnu.tar.gz/sha512/5e901618da26aa6f0f7d05ed1e5c538aea88cadb59b0efc7213203f1714e85aa0b6f0d463f2a4d367d2a8af36e37e7c353fdefbb98599fdc5bc182a6b93e4348 -OpenLibm.v0.8.1+4.x86_64-linux-musl.tar.gz/md5/462b9dfebd341f0c8bb33ba72f99f769 -OpenLibm.v0.8.1+4.x86_64-linux-musl.tar.gz/sha512/df84815a282a065e8fa33aa35ee29166a1119cd776f8812073e55521ca8c94f4bca679206b8a67ef69edbbb6ba6fca520d81626f687ca353a63146c4858ced80 -OpenLibm.v0.8.1+4.x86_64-unknown-freebsd.tar.gz/md5/f3642b29670e6216056a13f5da9e87b8 -OpenLibm.v0.8.1+4.x86_64-unknown-freebsd.tar.gz/sha512/613242d2f24b1dcc4b2bfd6f22ad66e9889f618ee685b50d9fdf33b9f3798fe503fc9e278f291118db70bacd54bd475f12554a4a56c60c369222fd23b3c5ba22 -OpenLibm.v0.8.1+4.x86_64-w64-mingw32.tar.gz/md5/59381a874c86a5ad5f3fb3b206587f64 -OpenLibm.v0.8.1+4.x86_64-w64-mingw32.tar.gz/sha512/3a8dd3ef46f15f78a34bdc37c5e26dcbf8522625457aa7c52dca21cbb967c5e39257216e5fc203ff57c9171b6c0f2e2ee9d6216c43b0254008f0e91318bb5f0e -openlibm-ae2d91698508701c83cab83714d42a1146dccf85.tar.gz/md5/19408d70bf042a109e1c267a53740089 -openlibm-ae2d91698508701c83cab83714d42a1146dccf85.tar.gz/sha512/9597fdcbc4af8369e6eecc3f8e86f251661cc64d236578f3ee8a6b39e77a47951446e1a0fe1151513da153e7ed17bf39aa5a36c32153d0d0400232bed2839e22 +OpenLibm.v0.8.5+0.aarch64-apple-darwin.tar.gz/md5/5fcbd746e90712e396e76dc4e76724d0 +OpenLibm.v0.8.5+0.aarch64-apple-darwin.tar.gz/sha512/f4ac2bc38bdc723384b67119daa2974fb43da34b2e45cea2029ea48f92c84c4cad6dfb43521b09a1e89ddf8c5b8cc22a38fa4b78ba39ac7524fd6bd1ba897aa9 +OpenLibm.v0.8.5+0.aarch64-linux-gnu.tar.gz/md5/4d1b4cd566805b5179c5ecdd060da473 +OpenLibm.v0.8.5+0.aarch64-linux-gnu.tar.gz/sha512/a9fe1a3d2e3898c017eb8615b2f3dbb514995ff041ac964c931c99c60d8cfe4eab7563a9cd65058f42f83c812f33d998573a7c5cc56a2e3960a4657e459ed321 +OpenLibm.v0.8.5+0.aarch64-linux-musl.tar.gz/md5/413be59af62b3ce0ebafeca093e3179e +OpenLibm.v0.8.5+0.aarch64-linux-musl.tar.gz/sha512/7bd76373e047ba854066af61f1c56b2e3a4d28c266228d7b30f596eadbaec52b070548ae60d41840c425ad5d0829c6c0cdaf326f2f160ed7508877ab5ec1a4b1 +OpenLibm.v0.8.5+0.aarch64-unknown-freebsd.tar.gz/md5/80736f9022c695eb1198e0b591a8fa63 +OpenLibm.v0.8.5+0.aarch64-unknown-freebsd.tar.gz/sha512/c633644578265e7ccc259ceb0442457b8c09290b4861b66c86dd6be7b30c4e394e70728142798097d6fe3afcfb4d9d1bd7ef58513fe8eed5684a4fba51bf185a +OpenLibm.v0.8.5+0.armv6l-linux-gnueabihf.tar.gz/md5/8fe0900a318393a290907f016bc654c3 +OpenLibm.v0.8.5+0.armv6l-linux-gnueabihf.tar.gz/sha512/167100a2d46e68462ef9a66915ced881d6358f05337bd38f2f77176f41cfd5be37e3c5226dd5d7d59147bd3e1aa7fb0893c1c81e9516134d3ab663b5752c4969 +OpenLibm.v0.8.5+0.armv6l-linux-musleabihf.tar.gz/md5/e8566719387984604f19dc5f9354a783 +OpenLibm.v0.8.5+0.armv6l-linux-musleabihf.tar.gz/sha512/532dd2b764fa15f7a838fb14cccafd2d4fe8fa4a132ea8394479a719c7aee11442f1b8a18e5d4a26ca820fa696d9d2afc7f5ec63dd96fa3b6763cea72b7026c3 +OpenLibm.v0.8.5+0.armv7l-linux-gnueabihf.tar.gz/md5/8fe0900a318393a290907f016bc654c3 +OpenLibm.v0.8.5+0.armv7l-linux-gnueabihf.tar.gz/sha512/167100a2d46e68462ef9a66915ced881d6358f05337bd38f2f77176f41cfd5be37e3c5226dd5d7d59147bd3e1aa7fb0893c1c81e9516134d3ab663b5752c4969 +OpenLibm.v0.8.5+0.armv7l-linux-musleabihf.tar.gz/md5/e8566719387984604f19dc5f9354a783 +OpenLibm.v0.8.5+0.armv7l-linux-musleabihf.tar.gz/sha512/532dd2b764fa15f7a838fb14cccafd2d4fe8fa4a132ea8394479a719c7aee11442f1b8a18e5d4a26ca820fa696d9d2afc7f5ec63dd96fa3b6763cea72b7026c3 +OpenLibm.v0.8.5+0.i686-linux-gnu.tar.gz/md5/9580d34e69d6067427b9c33db631cfd3 +OpenLibm.v0.8.5+0.i686-linux-gnu.tar.gz/sha512/46934f82791f69ac5f5da0dab7dcc6e3e9a4577c3bb529e9c0519c38f140c7b54517c55ff3579cd4ed4df68f0863e006aa98e51873f1dab452ce9f853996429a +OpenLibm.v0.8.5+0.i686-linux-musl.tar.gz/md5/66bfc9611d04c5d609e7824cb076d24b +OpenLibm.v0.8.5+0.i686-linux-musl.tar.gz/sha512/1bda2395d44c22aba3d1aab2b08ae06f763d3755037d454aa73f8e8134289a1ab5d65862bbc5a17a7a6b9f2918eb87e926b21527ddc4471e2ea20d605ba14e2d +OpenLibm.v0.8.5+0.i686-w64-mingw32.tar.gz/md5/0e97311b2f08b57d79085635f01ccced +OpenLibm.v0.8.5+0.i686-w64-mingw32.tar.gz/sha512/ae061ea406c06969332af58ed6fdfce2825326d771d30274d90775a1709b0361b7ca1dc7e6b0b76b93e4dd7a81d1842510a2c835251ee0a0978d6c839d96070e +OpenLibm.v0.8.5+0.powerpc64le-linux-gnu.tar.gz/md5/8ecfff7db76eee29591a654871e88855 +OpenLibm.v0.8.5+0.powerpc64le-linux-gnu.tar.gz/sha512/af03993b162316dd581f6ba5d1c23bca4c26cb22356ab229f326c42e111acbdf7ef45c9ad05894fe2d68794a63670cf89888653f788192a38b9255ce4bc72e28 +OpenLibm.v0.8.5+0.riscv64-linux-gnu.tar.gz/md5/69e06de135940666791c984941e9c4ad +OpenLibm.v0.8.5+0.riscv64-linux-gnu.tar.gz/sha512/2ac84deb7eb80a6a6237eff6fe861fd2907b3c95d1a76366dea062f3f35228dbc67aa40bd982e646508b4ff7cb6ef029111e2c0325039e60679800d6c6886be5 +OpenLibm.v0.8.5+0.x86_64-apple-darwin.tar.gz/md5/bd671ab9fe01835cab3e42e7cfa790fb +OpenLibm.v0.8.5+0.x86_64-apple-darwin.tar.gz/sha512/8bf2e66df17effc1e8778453904ffc20127f785bf096873289e8fdd8b17069ca844faffbd9f7621b87a7cb0a0051037eb9402360f2a03cf8794fbac8f7719777 +OpenLibm.v0.8.5+0.x86_64-linux-gnu.tar.gz/md5/df7fab134fbce3b625e9a82376f23e79 +OpenLibm.v0.8.5+0.x86_64-linux-gnu.tar.gz/sha512/64d07434e0db79833f84a2225838456eb9532617d377a776b3a534a908b1673bc4f890903f95350e4045e05c29539d993a18ecadeb879761e279ec3947f74390 +OpenLibm.v0.8.5+0.x86_64-linux-musl.tar.gz/md5/ebef6bb7651d116b397e035f39adfb1b +OpenLibm.v0.8.5+0.x86_64-linux-musl.tar.gz/sha512/de9036073e5dba2721b4119ecbbd21a0c9f75b65aff9392b7e88e464da35b97135d62404477441d0dadd3a2f8d49f1082291b35bf4b626fb1096d36d401980bf +OpenLibm.v0.8.5+0.x86_64-unknown-freebsd.tar.gz/md5/1115497539f00a37af18aa6516d52268 +OpenLibm.v0.8.5+0.x86_64-unknown-freebsd.tar.gz/sha512/71a2c06d141b3671fd220f2d88d72e845848b6d2b08a7b3a6c4bb1d5cc27cc450e1e681647bb583e7ed6375d5a70748401e95e61dc95d7808f33a9aa06755337 +OpenLibm.v0.8.5+0.x86_64-w64-mingw32.tar.gz/md5/b6b5335f4c83f7ebf0f74cf753358f00 +OpenLibm.v0.8.5+0.x86_64-w64-mingw32.tar.gz/sha512/e8351ddda305b757f337bb7ea26c441968843b23861676f0bdd7bcf83bb3969af790d4112307d3204eb87fac044dda9be305f349700ebe9ba2bfe3d6df24fde8 +openlibm-db24332879c320606c37f77fea165e6ecb49153c.tar.gz/md5/2375dd448e77e59152442a4b33abda01 +openlibm-db24332879c320606c37f77fea165e6ecb49153c.tar.gz/sha512/36054e7051990d04913f054a0542e2e104273f61308e9a442c2dab3dd392d40c03f264fbeca93c4296218eed85dad71028989a225088254013d752f4407d57ef diff --git a/deps/openlibm.version b/deps/openlibm.version index f35b291260380..16b17504430bc 100644 --- a/deps/openlibm.version +++ b/deps/openlibm.version @@ -2,6 +2,6 @@ OPENLIBM_JLL_NAME := OpenLibm ## source build -OPENLIBM_VER := 0.8.1 -OPENLIBM_BRANCH=v0.8.1 -OPENLIBM_SHA1=ae2d91698508701c83cab83714d42a1146dccf85 +OPENLIBM_VER := 0.8.5 +OPENLIBM_BRANCH=v0.8.5 +OPENLIBM_SHA1=db24332879c320606c37f77fea165e6ecb49153c diff --git a/doc/src/devdocs/build/windows.md b/doc/src/devdocs/build/windows.md index 7192bb8a7a544..1191169b565f5 100644 --- a/doc/src/devdocs/build/windows.md +++ b/doc/src/devdocs/build/windows.md @@ -32,7 +32,7 @@ or edit `%USERPROFILE%\.gitconfig` and add/edit the lines: ## Binary distribution For the binary distribution installation notes on Windows please see the instructions at -[https://julialang.org/downloads/platform/#windows](https://julialang.org/downloads/platform/#windows). +[https://julialang.org/downloads/platform/#windows](https://julialang.org/downloads/platform/#windows). Note, however, that on all platforms [using `juliaup`](https://julialang.org/install/) is recommended over manually installing binaries. ## Source distribution diff --git a/doc/src/index.md b/doc/src/index.md index bb758d14b4cf2..a8ebc7fa9beb3 100644 --- a/doc/src/index.md +++ b/doc/src/index.md @@ -37,7 +37,7 @@ Markdown.parse(""" Below is a non-exhasutive list of links that will be useful as you learn and use the Julia programming language. - [Julia Homepage](https://julialang.org) -- [Download Julia](https://julialang.org/downloads/) +- [Install Julia](https://julialang.org/install/) - [Discussion forum](https://discourse.julialang.org) - [Julia YouTube](https://www.youtube.com/user/JuliaLanguage) - [Find Julia Packages](https://julialang.org/packages/) diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index 7b889589c592d..63e0178e2bb84 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -543,15 +543,14 @@ is not valid, since the type layout of `T` is not known statically. ### SIMD Values -Note: This feature is currently implemented on 64-bit x86 and AArch64 platforms only. - If a C/C++ routine has an argument or return value that is a native SIMD type, the corresponding Julia type is a homogeneous tuple of `VecElement` that naturally maps to the SIMD type. Specifically: -> * The tuple must be the same size as the SIMD type. For example, a tuple representing an `__m128` -> on x86 must have a size of 16 bytes. -> * The element type of the tuple must be an instance of `VecElement{T}` where `T` is a primitive type that -> is 1, 2, 4 or 8 bytes. +> * The tuple must be the same size and elements as the SIMD type. For example, a tuple +> representing an `__m128` on x86 must have a size of 16 bytes and Float32 elements. +> * The element type of the tuple must be an instance of `VecElement{T}` where `T` is a +> primitive type with a power-of-two number of bytes (e.g. 1, 2, 4, 8, 16, etc) such as +> Int8 or Float64. For instance, consider this C routine that uses AVX intrinsics: @@ -624,6 +623,10 @@ For translating a C argument list to Julia: * `T`, where `T` is a Julia leaf type * argument value will be copied (passed by value) + * `vector T` (or `__attribute__ vector_size`, or a typedef such as `__m128`) + + * `NTuple{N, VecElement{T}}`, where `T` is a primitive Julia type of the correct size + and N is the number of elements in the vector (equal to `vector_size / sizeof T`). * `void*` * depends on how this parameter is used, first translate this to the intended pointer type, then @@ -670,13 +673,16 @@ For translating a C return type to Julia: * `T`, where `T` is one of the primitive types: `char`, `int`, `long`, `short`, `float`, `double`, `complex`, `enum` or any of their `typedef` equivalents - * `T`, where `T` is an equivalent Julia Bits Type (per the table above) - * if `T` is an `enum`, the argument type should be equivalent to `Cint` or `Cuint` + * same as C argument list * argument value will be copied (returned by-value) * `struct T` (including typedef to a struct) - * `T`, where `T` is a Julia Leaf Type + * same as C argument list * argument value will be copied (returned by-value) + + * `vector T` + + * same as C argument list * `void*` * depends on how this parameter is used, first translate this to the intended pointer type, then diff --git a/doc/src/manual/faq.md b/doc/src/manual/faq.md index b04c9585e30f3..57742e2fdefe6 100644 --- a/doc/src/manual/faq.md +++ b/doc/src/manual/faq.md @@ -1072,8 +1072,7 @@ You may wish to test against the nightly version to ensure that such regressions Finally, you may also consider building Julia from source for yourself. This option is mainly for those individuals who are comfortable at the command line, or interested in learning. If this describes you, you may also be interested in reading our [guidelines for contributing](https://github.com/JuliaLang/julia/blob/master/CONTRIBUTING.md). -Links to each of these download types can be found on the download page at [https://julialang.org/downloads/](https://julialang.org/downloads/). -Note that not all versions of Julia are available for all platforms. +The [`juliaup` install manager](https://julialang.org/install/) has pre-defined channels named `release` and `lts` for the latest stable release and the current LTS release, as well as version-specific channels. ### How can I transfer the list of installed packages after updating my version of Julia? diff --git a/doc/src/manual/getting-started.md b/doc/src/manual/getting-started.md index e972788022de6..9e0011ee7b714 100644 --- a/doc/src/manual/getting-started.md +++ b/doc/src/manual/getting-started.md @@ -1,7 +1,7 @@ # [Getting Started](@id man-getting-started) Julia installation is straightforward, whether using precompiled binaries or compiling from source. -Download and install Julia by following the instructions at [https://julialang.org/downloads/](https://julialang.org/downloads/). +Download and install Julia by following the instructions at [https://julialang.org/install/](https://julialang.org/install/). If you are coming to Julia from one of the following languages, then you should start by reading the section on noteworthy differences from [MATLAB](@ref Noteworthy-differences-from-MATLAB), [R](@ref Noteworthy-differences-from-R), [Python](@ref Noteworthy-differences-from-Python), [C/C++](@ref Noteworthy-differences-from-C/C) or [Common Lisp](@ref Noteworthy-differences-from-Common-Lisp). This will help you avoid some common pitfalls since Julia differs from those languages in many subtle ways. diff --git a/doc/src/manual/integers-and-floating-point-numbers.md b/doc/src/manual/integers-and-floating-point-numbers.md index 173ca7847616e..2175f5f21a36c 100644 --- a/doc/src/manual/integers-and-floating-point-numbers.md +++ b/doc/src/manual/integers-and-floating-point-numbers.md @@ -335,8 +335,8 @@ julia> typeof(x) Float64 ``` -Half-precision floating-point numbers are also supported ([`Float16`](@ref)), but they are -implemented in software and use [`Float32`](@ref) for calculations. +Half-precision floating-point numbers are also supported ([`Float16`](@ref)) on all platforms, with native instructions used on hardware which supports this number format. Otherwise, operations are implemented in software, and use [`Float32`](@ref) for intermediate calculations. +As an internal implementation detail, this is achieved under the hood by using LLVM's [`half`](https://llvm.org/docs/LangRef.html#half-precision-floating-point-intrinsics) type, which behaves similarly to what the GCC [`-fexcess-precision=16`](https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fexcess-precision) flag does for C/C++ code. ```jldoctest julia> sizeof(Float16(4.)) diff --git a/src/ast.c b/src/ast.c index 06727b453d6a3..c1e1c71733d69 100644 --- a/src/ast.c +++ b/src/ast.c @@ -942,7 +942,7 @@ JL_DLLEXPORT jl_value_t *jl_copy_ast(jl_value_t *expr) return expr; } -JL_DLLEXPORT int jl_is_operator(char *sym) +JL_DLLEXPORT int jl_is_operator(const char *sym) { jl_ast_context_t *ctx = jl_ast_ctx_enter(NULL); fl_context_t *fl_ctx = &ctx->fl; @@ -951,7 +951,7 @@ JL_DLLEXPORT int jl_is_operator(char *sym) return res; } -JL_DLLEXPORT int jl_is_unary_operator(char *sym) +JL_DLLEXPORT int jl_is_unary_operator(const char *sym) { jl_ast_context_t *ctx = jl_ast_ctx_enter(NULL); fl_context_t *fl_ctx = &ctx->fl; @@ -960,7 +960,7 @@ JL_DLLEXPORT int jl_is_unary_operator(char *sym) return res; } -JL_DLLEXPORT int jl_is_unary_and_binary_operator(char *sym) +JL_DLLEXPORT int jl_is_unary_and_binary_operator(const char *sym) { jl_ast_context_t *ctx = jl_ast_ctx_enter(NULL); fl_context_t *fl_ctx = &ctx->fl; @@ -969,7 +969,7 @@ JL_DLLEXPORT int jl_is_unary_and_binary_operator(char *sym) return res; } -JL_DLLEXPORT int jl_is_syntactic_operator(char *sym) +JL_DLLEXPORT int jl_is_syntactic_operator(const char *sym) { jl_ast_context_t *ctx = jl_ast_ctx_enter(NULL); fl_context_t *fl_ctx = &ctx->fl; @@ -978,7 +978,7 @@ JL_DLLEXPORT int jl_is_syntactic_operator(char *sym) return res; } -JL_DLLEXPORT int jl_operator_precedence(char *sym) +JL_DLLEXPORT int jl_operator_precedence(const char *sym) { jl_ast_context_t *ctx = jl_ast_ctx_enter(NULL); fl_context_t *fl_ctx = &ctx->fl; diff --git a/src/builtins.c b/src/builtins.c index a8285aab99953..6b5c22f0301a0 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1696,42 +1696,54 @@ 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); +// 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_vararg(p)) { jl_value_t *T = ((jl_vararg_t*)p)->T; jl_value_t *N = ((jl_vararg_t*)p)->N; - return (T && references_name(T, name, affects_layout)) || - (N && references_name(N, name, affects_layout)); + return (T && references_name(T, name, affects_layout, freevars)) || + (N && references_name(N, name, affects_layout, freevars)); } - 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); 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; } } @@ -1767,12 +1779,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/cgutils.cpp b/src/cgutils.cpp index bb96805bbb72b..d560ad47ff7aa 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -3301,7 +3301,7 @@ static void union_alloca_type(jl_uniontype_t *ut, [&](unsigned idx, jl_datatype_t *jt) { if (!jl_is_datatype_singleton(jt)) { size_t nb1 = jl_datatype_size(jt); - size_t align1 = jl_datatype_align(jt); + size_t align1 = julia_alignment((jl_value_t*)jt); if (nb1 > nbytes) nbytes = nb1; if (align1 > align) @@ -3850,9 +3850,10 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg // whether we should perform the initialization with the struct as a IR value // or instead initialize the stack buffer with stores + // although we do the former if it is a vector or could be a vector element auto tracked = CountTrackedPointers(lt); bool init_as_value = false; - if (lt->isVectorTy() || jl_is_vecelement_type(ty)) { // maybe also check the size ? + if (lt->isVectorTy() || jl_special_vector_alignment(1, ty) != 0) { init_as_value = true; } else if (tracked.count) { diff --git a/src/codegen.cpp b/src/codegen.cpp index 1968050c3bd34..20a6240a926d0 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -7715,6 +7715,8 @@ static jl_llvm_functions_t Type *RT = Arg->getParamStructRetType(); TypeSize sz = DL.getTypeAllocSize(RT); Align al = DL.getPrefTypeAlign(RT); + if (al > MAX_ALIGN) + al = Align(MAX_ALIGN); param.addAttribute(Attribute::NonNull); // The `dereferenceable` below does not imply `nonnull` for non addrspace(0) pointers. param.addDereferenceableAttr(sz); diff --git a/src/datatype.c b/src/datatype.c index 905959fb80e0a..b823f933b1376 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -291,9 +291,10 @@ static jl_datatype_layout_t *jl_get_layout(uint32_t sz, } // Determine if homogeneous tuple with fields of type t will have -// a special alignment beyond normal Julia rules. +// a special alignment and vector-ABI beyond normal rules for aggregates. // Return special alignment if one exists, 0 if normal alignment rules hold. // A non-zero result *must* match the LLVM rules for a vector type . +// Matching the compiler's `__attribute__ vector_size` behavior. // For sake of Ahead-Of-Time (AOT) compilation, this routine has to work // without LLVM being available. unsigned jl_special_vector_alignment(size_t nfields, jl_value_t *t) @@ -308,8 +309,12 @@ unsigned jl_special_vector_alignment(size_t nfields, jl_value_t *t) // motivating use case comes up for Julia, we reject pointers. return 0; size_t elsz = jl_datatype_size(ty); - if (elsz != 1 && elsz != 2 && elsz != 4 && elsz != 8) - // Only handle power-of-two-sized elements (for now) + if (next_power_of_two(elsz) != elsz) + // Only handle power-of-two-sized elements (for now), since other + // lengths may be packed into very complicated arrangements (llvm pads + // extra bits on most platforms when computing alignment but not when + // computing type size, but adds no extra bytes for each element, so + // their effect on offsets are never what you may naturally expect). return 0; size_t size = nfields * elsz; // Use natural alignment for this vector: this matches LLVM and clang. @@ -601,9 +606,9 @@ void jl_compute_field_offsets(jl_datatype_t *st) } else { fsz = sizeof(void*); - if (fsz > MAX_ALIGN) - fsz = MAX_ALIGN; al = fsz; + if (al > MAX_ALIGN) + al = MAX_ALIGN; desc[i].isptr = 1; zeroinit = 1; npointers++; @@ -823,6 +828,18 @@ JL_DLLEXPORT jl_datatype_t *jl_new_primitivetype(jl_value_t *name, jl_module_t * jl_emptysvec, jl_emptysvec, jl_emptysvec, 0, 0, 0); uint32_t nbytes = (nbits + 7) / 8; uint32_t alignm = next_power_of_two(nbytes); +# if defined(_CPU_X86_) && !defined(_OS_WINDOWS_) + // datalayout strings are often weird: on 64-bit they usually follow fairly simple rules, + // but on x86 32 bit platforms, sometimes 5 to 8 byte types are + // 32-bit aligned even though the MAX_ALIGN (for types 9+ bytes) is 16 + // (except for f80 which is align 4 on Mingw, Linux, and BSDs--but align 16 on MSVC and Darwin) + // https://llvm.org/doxygen/ARMTargetMachine_8cpp.html#adb29b487708f0dc2a940345b68649270 + // https://llvm.org/doxygen/AArch64TargetMachine_8cpp.html#a003a58caf135efbf7273c5ed84e700d7 + // https://llvm.org/doxygen/X86TargetMachine_8cpp.html#aefdbcd6131ef195da070cef7fdaf0532 + // 32-bit alignment is weird + if (alignm == 8) + alignm = 4; +# endif if (alignm > MAX_ALIGN) alignm = MAX_ALIGN; // memoize isprimitivetype, since it is much easier than checking diff --git a/src/flisp/print.c b/src/flisp/print.c index 2b20d0d98b225..a6f633c2e6701 100644 --- a/src/flisp/print.c +++ b/src/flisp/print.c @@ -518,7 +518,7 @@ static void print_string(fl_context_t *fl_ctx, ios_t *f, char *str, size_t sz) } else { while (i < sz) { - size_t n = u8_escape(buf, sizeof(buf), str, &i, sz, 1, 0); + size_t n = u8_escape(buf, sizeof(buf), str, &i, sz, "\"", 0); outsn(fl_ctx, buf, f, n-1); } } diff --git a/src/gf.c b/src/gf.c index ac661782e43be..1f87a3035076e 100644 --- a/src/gf.c +++ b/src/gf.c @@ -3376,7 +3376,7 @@ static int sort_mlmatches(jl_array_t *t, size_t idx, arraylist_t *visited, array continue; // already part of this cycle jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(t, childidx); jl_method_t *m2 = matc2->method; - int subt2 = matc2->fully_covers != NOT_FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) + int subt2 = matc2->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) // TODO: we could change this to jl_has_empty_intersection(ti, (jl_value_t*)matc2->spec_types); // since we only care about sorting of the intersections the user asked us about if (!subt2 && jl_has_empty_intersection(m2->sig, m->sig)) @@ -3521,7 +3521,7 @@ static int sort_mlmatches(jl_array_t *t, size_t idx, arraylist_t *visited, array size_t idx2 = (size_t)stack->items[j]; jl_method_match_t *matc2 = (jl_method_match_t*)jl_array_ptr_ref(t, idx2); jl_method_t *m2 = matc2->method; - int subt2 = matc2->fully_covers != NOT_FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) + int subt2 = matc2->fully_covers == FULLY_COVERS; // jl_subtype((jl_value_t*)type, (jl_value_t*)m2->sig) // if their intersection contributes to the ambiguity cycle // and the contribution of m is fully ambiguous with the portion of the cycle from m2 if (subt2 || jl_subtype((jl_value_t*)ti, m2->sig)) { diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 9c163458f22d2..fad6934706795 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -629,10 +629,24 @@ static jl_cgval_t generic_bitcast(jl_codectx_t &ctx, const jl_cgval_t *argv) setName(ctx.emission_context, vx, "bitcast_coercion"); } else if (!vxt->isPointerTy() && llvmt->isPointerTy()) { vx = emit_inttoptr(ctx, vx, llvmt); - setName(ctx.emission_context, vx, "bitcast_coercion"); + if (isa(vx) && !vx->hasName()) + // emit_inttoptr may undo an PtrToInt + setName(ctx.emission_context, vx, "bitcast_coercion"); + } else if (vxt->isPointerTy() && llvmt->isPointerTy()) { + // emit_bitcast preserves the origin address space, which we can't have here + #if JL_LLVM_VERSION >= 170000 + vx = ctx.builder.CreateAddrSpaceCast(vx, llvmt); + #else + vx = ctx.builder.CreatePointerBitCastOrAddrSpaceCast(vx, llvmt); + #endif + if (isa(vx) && !vx->hasName()) + // cast may have been folded + setName(ctx.emission_context, vx, "bitcast_coercion"); } else { vx = emit_bitcast(ctx, vx, llvmt); - setName(ctx.emission_context, vx, "bitcast_coercion"); + if (isa(vx) && !vx->hasName()) + // emit_bitcast may undo another bitcast + setName(ctx.emission_context, vx, "bitcast_coercion"); } } diff --git a/src/julia.h b/src/julia.h index 8166eb0286830..26783ceb1a45e 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1955,11 +1955,11 @@ JL_DLLEXPORT jl_array_t *jl_uncompress_argnames(jl_value_t *syms); JL_DLLEXPORT jl_value_t *jl_uncompress_argname_n(jl_value_t *syms, size_t i); -JL_DLLEXPORT int jl_is_operator(char *sym); -JL_DLLEXPORT int jl_is_unary_operator(char *sym); -JL_DLLEXPORT int jl_is_unary_and_binary_operator(char *sym); -JL_DLLEXPORT int jl_is_syntactic_operator(char *sym); -JL_DLLEXPORT int jl_operator_precedence(char *sym); +JL_DLLEXPORT int jl_is_operator(const char *sym); +JL_DLLEXPORT int jl_is_unary_operator(const char *sym); +JL_DLLEXPORT int jl_is_unary_and_binary_operator(const char *sym); +JL_DLLEXPORT int jl_is_syntactic_operator(const char *sym); +JL_DLLEXPORT int jl_operator_precedence(const char *sym); STATIC_INLINE int jl_vinfo_sa(uint8_t vi) { diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index 63a71a9e180ac..77a55915bf363 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -2305,8 +2305,10 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S, bool *CFGModified) { // strip all constant alias information, as it might depend on the gc having // preserved a gc root, which stops being true after this pass (#32215) // similar to RewriteStatepointsForGC::stripNonValidData, but less aggressive - if (I->getMetadata(LLVMContext::MD_invariant_load)) - I->setMetadata(LLVMContext::MD_invariant_load, NULL); + if (auto *LI = dyn_cast(I)){ + if (isSpecialPtr(LI->getPointerOperand()->getType()) && LI->getMetadata(LLVMContext::MD_invariant_load)) + LI->setMetadata(LLVMContext::MD_invariant_load, NULL); + } if (MDNode *TBAA = I->getMetadata(LLVMContext::MD_tbaa)) { if (TBAA->getNumOperands() == 4 && isTBAA(TBAA, {"jtbaa_const"})) { MDNode *MutableTBAA = createMutableTBAAAccessTag(TBAA); diff --git a/src/llvm-remove-addrspaces.cpp b/src/llvm-remove-addrspaces.cpp index a72013eaeb65f..97df6957b2708 100644 --- a/src/llvm-remove-addrspaces.cpp +++ b/src/llvm-remove-addrspaces.cpp @@ -271,7 +271,7 @@ bool removeAddrspaces(Module &M, AddrspaceRemapFunction ASRemapper) Name, (GlobalVariable *)nullptr, GV->getThreadLocalMode(), - GV->getType()->getAddressSpace()); + cast(TypeRemapper.remapType(GV->getType()))->getAddressSpace()); NGV->copyAttributesFrom(GV); VMap[GV] = NGV; } @@ -291,7 +291,7 @@ bool removeAddrspaces(Module &M, AddrspaceRemapFunction ASRemapper) auto *NGA = GlobalAlias::create( TypeRemapper.remapType(GA->getValueType()), - GA->getType()->getPointerAddressSpace(), + cast(TypeRemapper.remapType(GA->getType()))->getAddressSpace(), GA->getLinkage(), Name, &M); diff --git a/src/method.c b/src/method.c index e96d1008e3056..d99493208cc4d 100644 --- a/src/method.c +++ b/src/method.c @@ -380,11 +380,13 @@ static void jl_code_info_set_ir(jl_code_info_t *li, jl_expr_t *ir) } bd[j] = jl_nothing; } - else if (jl_is_expr(st) && ((jl_expr_t*)st)->head == jl_return_sym) { + else if (jl_is_expr(st) && ((jl_expr_t*)st)->head == jl_return_sym) jl_array_ptr_set(body, j, jl_new_struct(jl_returnnode_type, jl_exprarg(st, 0))); - } - else if (jl_is_expr(st) && (((jl_expr_t*)st)->head == jl_foreigncall_sym || ((jl_expr_t*)st)->head == jl_cfunction_sym)) { - li->has_fcall = 1; + else { + if (jl_is_expr(st) && ((jl_expr_t*)st)->head == jl_assign_sym) + st = jl_exprarg(st, 1); + if (jl_is_expr(st) && (((jl_expr_t*)st)->head == jl_foreigncall_sym || ((jl_expr_t*)st)->head == jl_cfunction_sym)) + li->has_fcall = 1; } if (is_flag_stmt) jl_array_uint8_set(li->ssaflags, j, 0); diff --git a/src/module.c b/src/module.c index d39b7e377b2df..173f4dacf2769 100644 --- a/src/module.c +++ b/src/module.c @@ -24,7 +24,8 @@ JL_DLLEXPORT jl_module_t *jl_new_module_(jl_sym_t *name, jl_module_t *parent, ui m->istopmod = 0; m->uuid = uuid_zero; static unsigned int mcounter; // simple counter backup, in case hrtime is not incrementing - m->build_id.lo = jl_hrtime() + (++mcounter); + // TODO: this is used for ir decompression and is liable to hash collisions so use more of the bits + m->build_id.lo = bitmix(jl_hrtime() + (++mcounter), jl_rand()); if (!m->build_id.lo) m->build_id.lo++; // build id 0 is invalid m->build_id.hi = ~(uint64_t)0; diff --git a/src/pipeline.cpp b/src/pipeline.cpp index 8a9410aa98247..dd81c1d9989c3 100644 --- a/src/pipeline.cpp +++ b/src/pipeline.cpp @@ -458,6 +458,13 @@ static void buildScalarOptimizerPipeline(FunctionPassManager &FPM, PassBuilder * FPM.addPass(IRCEPass()); FPM.addPass(InstCombinePass()); FPM.addPass(JumpThreadingPass()); + } else if (O.getSpeedupLevel() >= 1) { + JULIA_PASS(FPM.addPass(AllocOptPass())); + FPM.addPass(SROAPass()); + FPM.addPass(MemCpyOptPass()); + FPM.addPass(SCCPPass()); + FPM.addPass(InstCombinePass()); + FPM.addPass(ADCEPass()); } if (O.getSpeedupLevel() >= 3) { FPM.addPass(GVNPass()); diff --git a/src/rtutils.c b/src/rtutils.c index eefd1b25f9bc4..3710aba3429c2 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -5,6 +5,8 @@ */ #include "platform.h" +#include +#include #include #include #include @@ -572,7 +574,7 @@ static size_t jl_show_svec(JL_STREAM *out, jl_svec_t *t, const char *head, const JL_DLLEXPORT int jl_id_start_char(uint32_t wc) JL_NOTSAFEPOINT; JL_DLLEXPORT int jl_id_char(uint32_t wc) JL_NOTSAFEPOINT; -JL_DLLEXPORT int jl_is_identifier(char *str) JL_NOTSAFEPOINT +JL_DLLEXPORT int jl_is_identifier(const char *str) JL_NOTSAFEPOINT { size_t i = 0; uint32_t wc = u8_nextchar(str, &i); @@ -655,22 +657,97 @@ static int is_globfunction(jl_value_t *v, jl_datatype_t *dv, jl_sym_t **globname return 0; } -static size_t jl_static_show_x_sym_escaped(JL_STREAM *out, jl_sym_t *name) JL_NOTSAFEPOINT +static size_t jl_static_show_string(JL_STREAM *out, const char *str, size_t len, int wrap, int raw) JL_NOTSAFEPOINT { size_t n = 0; - - char *sn = jl_symbol_name(name); - int hidden = 0; - if (!(jl_is_identifier(sn) || jl_is_operator(sn))) { - hidden = 1; + if (wrap) + n += jl_printf(out, "\""); + if (!raw && !u8_isvalid(str, len)) { + // alternate print algorithm that preserves data if it's not UTF-8 + static const char hexdig[] = "0123456789abcdef"; + for (size_t i = 0; i < len; i++) { + uint8_t c = str[i]; + if (c == '\\' || c == '"' || c == '$') + n += jl_printf(out, "\\%c", c); + else if (c >= 32 && c < 0x7f) + n += jl_printf(out, "%c", c); + else + n += jl_printf(out, "\\x%c%c", hexdig[c>>4], hexdig[c&0xf]); + } } - - if (hidden) { - n += jl_printf(out, "var\""); + else { + int special = 0; + for (size_t i = 0; i < len; i++) { + uint8_t c = str[i]; + if (raw && ((c == '\\' && i == len-1) || c == '"')) { + special = 1; + break; + } + else if (!raw && (c < 32 || c == 0x7f || c == '\\' || c == '"' || c == '$')) { + special = 1; + break; + } + } + if (!special) { + jl_uv_puts(out, str, len); + n += len; + } + else if (raw) { + // REF: Base.escape_raw_string + int escapes = 0; + for (size_t i = 0; i < len; i++) { + uint8_t c = str[i]; + if (c == '\\') { + escapes++; + } + else { + if (c == '"') + for (escapes++; escapes > 0; escapes--) + n += jl_printf(out, "\\"); + escapes = 0; + } + n += jl_printf(out, "%c", str[i]); + } + for (; escapes > 0; escapes--) + n += jl_printf(out, "\\"); + } + else { + char buf[512]; + size_t i = 0; + while (i < len) { + size_t r = u8_escape(buf, sizeof(buf), str, &i, len, "\"$", 0); + jl_uv_puts(out, buf, r - 1); + n += r - 1; + } + } } - n += jl_printf(out, "%s", sn); - if (hidden) { + if (wrap) n += jl_printf(out, "\""); + return n; +} + +static int jl_is_quoted_sym(const char *sn) +{ + static const char *const quoted_syms[] = {":", "::", ":=", "=", "==", "===", "=>", "`"}; + for (int i = 0; i < sizeof quoted_syms / sizeof *quoted_syms; i++) + if (!strcmp(sn, quoted_syms[i])) + return 1; + return 0; +} + +// TODO: in theory, we need a separate function for showing symbols in an +// expression context (where `Symbol("foo\x01bar")` is ok) and a syntactic +// context (where var"" must be used). +static size_t jl_static_show_symbol(JL_STREAM *out, jl_sym_t *name) JL_NOTSAFEPOINT +{ + size_t n = 0; + const char *sn = jl_symbol_name(name); + if (jl_is_identifier(sn) || (jl_is_operator(sn) && !jl_is_quoted_sym(sn))) { + n += jl_printf(out, "%s", sn); + } + else { + n += jl_printf(out, "var"); + n += jl_static_show_string(out, sn, strlen(sn), 1, 1); } return n; } @@ -699,6 +776,51 @@ static int jl_static_is_function_(jl_datatype_t *vt) JL_NOTSAFEPOINT { return 0; } +static size_t jl_static_show_float(JL_STREAM *out, double v, + jl_datatype_t *vt) JL_NOTSAFEPOINT +{ + size_t n = 0; + // TODO: non-canonical NaNs do not round-trip + // TOOD: BFloat16 + const char *size_suffix = vt == jl_float16_type ? "16" : + vt == jl_float32_type ? "32" : + ""; + // Requires minimum 1 (sign) + 17 (sig) + 1 (dot) + 5 ("e-123") + 1 (null) + char buf[32]; + // Base B significand digits required to print n base-b significand bits + // (including leading 1): N = 2 + floor(n/log(b, B)) + // Float16 5 + // Float32 9 + // Float64 17 + // REF: https://dl.acm.org/doi/pdf/10.1145/93542.93559 + if (isnan(v)) { + n += jl_printf(out, "NaN%s", size_suffix); + } + else if (isinf(v)) { + n += jl_printf(out, "%sInf%s", v < 0 ? "-" : "", size_suffix); + } + else if (vt == jl_float64_type) { + n += jl_printf(out, "%#.17g", v); + } + else if (vt == jl_float32_type) { + size_t m = snprintf(buf, sizeof buf, "%.9g", v); + // If the exponent was printed, replace it with 'f' + char *p = (char *)memchr(buf, 'e', m); + if (p) + *p = 'f'; + jl_uv_puts(out, buf, m); + n += m; + // If no exponent was printed, we must add one + if (!p) + n += jl_printf(out, "f0"); + } + else { + assert(vt == jl_float16_type); + n += jl_printf(out, "Float16(%#.5g)", v); + } + return n; +} + // `v` might be pointing to a field inlined in a structure therefore // `jl_typeof(v)` may not be the same with `vt` and only `vt` should be // used to determine the type of the value. @@ -788,11 +910,6 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt // Types are printed as a fully qualified name, with parameters, e.g. // `Base.Set{Int}`, and function types are printed as e.g. `typeof(Main.f)` jl_datatype_t *dv = (jl_datatype_t*)v; - jl_sym_t *globname; - int globfunc = is_globname_binding(v, dv) && is_globfunction(v, dv, &globname); - jl_sym_t *sym = globfunc ? globname : dv->name->name; - char *sn = jl_symbol_name(sym); - size_t quote = 0; if (dv->name == jl_tuple_typename) { if (dv == jl_tuple_type) return jl_printf(out, "Tuple"); @@ -825,8 +942,13 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt return n; } if (ctx.quiet) { - return jl_printf(out, "%s", jl_symbol_name(dv->name->name)); + return jl_static_show_symbol(out, dv->name->name); } + jl_sym_t *globname; + int globfunc = is_globname_binding(v, dv) && is_globfunction(v, dv, &globname); + jl_sym_t *sym = globfunc ? globname : dv->name->name; + char *sn = jl_symbol_name(sym); + size_t quote = 0; if (globfunc) { n += jl_printf(out, "typeof("); } @@ -839,7 +961,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt quote = 1; } } - n += jl_static_show_x_sym_escaped(out, sym); + n += jl_static_show_symbol(out, sym); if (globfunc) { n += jl_printf(out, ")"); if (quote) { @@ -864,17 +986,21 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt int f = *(uint32_t*)jl_data_ptr(v); n += jl_printf(out, "#", f, jl_intrinsic_name(f)); } + else if (vt == jl_long_type) { + // Avoid unnecessary Int64(x)/Int32(x) + n += jl_printf(out, "%" PRIdPTR, *(intptr_t*)v); + } else if (vt == jl_int64_type) { - n += jl_printf(out, "%" PRId64, *(int64_t*)v); + n += jl_printf(out, "Int64(%" PRId64 ")", *(int64_t*)v); } else if (vt == jl_int32_type) { - n += jl_printf(out, "%" PRId32, *(int32_t*)v); + n += jl_printf(out, "Int32(%" PRId32 ")", *(int32_t*)v); } else if (vt == jl_int16_type) { - n += jl_printf(out, "%" PRId16, *(int16_t*)v); + n += jl_printf(out, "Int16(%" PRId16 ")", *(int16_t*)v); } else if (vt == jl_int8_type) { - n += jl_printf(out, "%" PRId8, *(int8_t*)v); + n += jl_printf(out, "Int8(%" PRId8 ")", *(int8_t*)v); } else if (vt == jl_uint64_type) { n += jl_printf(out, "0x%016" PRIx64, *(uint64_t*)v); @@ -888,18 +1014,14 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt else if (vt == jl_uint8_type) { n += jl_printf(out, "0x%02" PRIx8, *(uint8_t*)v); } - else if (jl_pointer_type && jl_is_cpointer_type((jl_value_t*)vt)) { -#ifdef _P64 - n += jl_printf(out, "0x%016" PRIx64, *(uint64_t*)v); -#else - n += jl_printf(out, "0x%08" PRIx32, *(uint32_t*)v); -#endif + else if (vt == jl_float16_type) { + n += jl_static_show_float(out, julia__gnu_h2f_ieee(*(uint16_t *)v), vt); } else if (vt == jl_float32_type) { - n += jl_printf(out, "%gf", *(float*)v); + n += jl_static_show_float(out, *(float *)v, vt); } else if (vt == jl_float64_type) { - n += jl_printf(out, "%g", *(double*)v); + n += jl_static_show_float(out, *(double *)v, vt); } else if (vt == jl_bool_type) { n += jl_printf(out, "%s", *(uint8_t*)v ? "true" : "false"); @@ -908,9 +1030,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, "nothing"); } else if (vt == jl_string_type) { - n += jl_printf(out, "\""); - jl_uv_puts(out, jl_string_data(v), jl_string_len(v)); n += jl_string_len(v); - n += jl_printf(out, "\""); + n += jl_static_show_string(out, jl_string_data(v), jl_string_len(v), 1, 0); } else if (v == jl_bottom_type) { n += jl_printf(out, "Union{}"); @@ -959,7 +1079,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_printf(out, ")"); n += jl_printf(out, "<:"); } - n += jl_static_show_x_sym_escaped(out, var->name); + n += jl_static_show_symbol(out, var->name); if (showbounds && (ub != (jl_value_t*)jl_any_type || lb != jl_bottom_type)) { // show type-var upper bound if it is defined, or if we showed the lower bound int ua = jl_is_unionall(ub); @@ -977,18 +1097,11 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt n += jl_static_show_x(out, (jl_value_t*)m->parent, depth, ctx); n += jl_printf(out, "."); } - n += jl_printf(out, "%s", jl_symbol_name(m->name)); + n += jl_static_show_symbol(out, m->name); } else if (vt == jl_symbol_type) { - char *sn = jl_symbol_name((jl_sym_t*)v); - int quoted = !jl_is_identifier(sn) && jl_operator_precedence(sn) == 0; - if (quoted) - n += jl_printf(out, "Symbol(\""); - else - n += jl_printf(out, ":"); - n += jl_printf(out, "%s", sn); - if (quoted) - n += jl_printf(out, "\")"); + n += jl_printf(out, ":"); + n += jl_static_show_symbol(out, (jl_sym_t*)v); } else if (vt == jl_ssavalue_type) { n += jl_printf(out, "SSAValue(%" PRIuPTR ")", @@ -996,8 +1109,12 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt } else if (vt == jl_globalref_type) { n += jl_static_show_x(out, (jl_value_t*)jl_globalref_mod(v), depth, ctx); - char *name = jl_symbol_name(jl_globalref_name(v)); - n += jl_printf(out, jl_is_identifier(name) ? ".%s" : ".:(%s)", name); + jl_sym_t *name = jl_globalref_name(v); + n += jl_printf(out, "."); + if (jl_is_operator(jl_symbol_name(name))) + n += jl_printf(out, ":(%s)", jl_symbol_name(name)); + else + n += jl_static_show_symbol(out, name); } else if (vt == jl_gotonode_type) { n += jl_printf(out, "goto %" PRIuPTR, jl_gotonode_label(v)); @@ -1031,17 +1148,17 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt else if (vt == jl_expr_type) { jl_expr_t *e = (jl_expr_t*)v; if (e->head == jl_assign_sym && jl_array_len(e->args) == 2) { - n += jl_static_show_x(out, jl_exprarg(e,0), depth, ctx); + n += jl_static_show_x(out, jl_exprarg(e, 0), depth, ctx); n += jl_printf(out, " = "); - n += jl_static_show_x(out, jl_exprarg(e,1), depth, ctx); + n += jl_static_show_x(out, jl_exprarg(e, 1), depth, ctx); } else { - char sep = ' '; - n += jl_printf(out, "Expr(:%s", jl_symbol_name(e->head)); + n += jl_printf(out, "Expr("); + n += jl_static_show_x(out, (jl_value_t*)e->head, depth, ctx); size_t i, len = jl_array_len(e->args); for (i = 0; i < len; i++) { - n += jl_printf(out, ",%c", sep); - n += jl_static_show_x(out, jl_exprarg(e,i), depth, ctx); + n += jl_printf(out, ", "); + n += jl_static_show_x(out, jl_exprarg(e, i), depth, ctx); } n += jl_printf(out, ")"); } @@ -1128,7 +1245,7 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt } } - n += jl_static_show_x_sym_escaped(out, sym); + n += jl_static_show_symbol(out, sym); if (globfunc) { if (quote) { @@ -1164,8 +1281,14 @@ static size_t jl_static_show_x_(JL_STREAM *out, jl_value_t *v, jl_datatype_t *vt jl_value_t *names = isnamedtuple ? jl_tparam0(vt) : (jl_value_t*)jl_field_names(vt); for (; i < tlen; i++) { if (!istuple) { - jl_value_t *fname = isnamedtuple ? jl_fieldref_noalloc(names, i) : jl_svecref(names, i); - n += jl_printf(out, "%s=", jl_symbol_name((jl_sym_t*)fname)); + jl_sym_t *fname = (jl_sym_t*)(isnamedtuple ? jl_fieldref_noalloc(names, i) : jl_svecref(names, i)); + if (fname == NULL || !jl_is_symbol(fname)) + n += jl_static_show_x(out, (jl_value_t*)fname, depth, ctx); + else if (jl_is_operator(jl_symbol_name(fname))) + n += jl_printf(out, "(%s)", jl_symbol_name(fname)); + else + n += jl_static_show_symbol(out, fname); + n += jl_printf(out, "="); } size_t offs = jl_field_offset(vt, i); char *fld_ptr = (char*)v + offs; @@ -1300,7 +1423,7 @@ size_t jl_static_show_func_sig_(JL_STREAM *s, jl_value_t *type, jl_static_show_c if ((jl_nparams(ftype) == 0 || ftype == ((jl_datatype_t*)ftype)->name->wrapper) && ((jl_datatype_t*)ftype)->name->mt != jl_type_type_mt && ((jl_datatype_t*)ftype)->name->mt != jl_nonfunction_mt) { - n += jl_printf(s, "%s", jl_symbol_name(((jl_datatype_t*)ftype)->name->mt->name)); + n += jl_static_show_symbol(s, ((jl_datatype_t*)ftype)->name->mt->name); } else { n += jl_printf(s, "(::"); @@ -1399,10 +1522,10 @@ void jl_log(int level, jl_value_t *module, jl_value_t *group, jl_value_t *id, } jl_printf(str, "\n@ "); if (jl_is_string(file)) { - jl_uv_puts(str, jl_string_data(file), jl_string_len(file)); + jl_static_show_string(str, jl_string_data(file), jl_string_len(file), 0, 0); } else if (jl_is_symbol(file)) { - jl_printf(str, "%s", jl_symbol_name((jl_sym_t*)file)); + jl_static_show_string(str, jl_symbol_name((jl_sym_t*)file), strlen(jl_symbol_name((jl_sym_t*)file)), 0, 0); } jl_printf(str, ":"); jl_static_show(str, line); diff --git a/src/stackwalk.c b/src/stackwalk.c index 913e233418796..d552008fe9ab0 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -97,9 +97,13 @@ static int jl_unw_stepn(bt_cursor_t *cursor, jl_bt_element_t *bt_data, size_t *b } uintptr_t oldsp = thesp; have_more_frames = jl_unw_step(cursor, from_signal_handler, &return_ip, &thesp); - if (oldsp >= thesp && !jl_running_under_rr(0)) { - // The stack pointer is clearly bad, as it must grow downwards. + if ((n < 2 ? oldsp > thesp : oldsp >= thesp) && !jl_running_under_rr(0)) { + // The stack pointer is clearly bad, as it must grow downwards, // But sometimes the external unwinder doesn't check that. + // Except for n==0 when there is no oldsp and n==1 on all platforms but i686/x86_64. + // (on x86, the platform first pushes the new stack frame, then does the + // call, on almost all other platforms, the platform first does the call, + // then the user pushes the link register to the frame). have_more_frames = 0; } if (return_ip == 0) { @@ -131,11 +135,11 @@ static int jl_unw_stepn(bt_cursor_t *cursor, jl_bt_element_t *bt_data, size_t *b // * The way that libunwind handles it in `unw_get_proc_name`: // https://lists.nongnu.org/archive/html/libunwind-devel/2014-06/msg00025.html uintptr_t call_ip = return_ip; + #if defined(_CPU_ARM_) // ARM instruction pointer encoding uses the low bit as a flag for // thumb mode, which must be cleared before further use. (Note not // needed for ARM AArch64.) See // https://github.com/libunwind/libunwind/pull/131 - #ifdef _CPU_ARM_ call_ip &= ~(uintptr_t)0x1; #endif // Now there's two main cases to adjust for: diff --git a/src/staticdata.c b/src/staticdata.c index a780add783d46..e87323e3383b1 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -89,6 +89,22 @@ External links: #include "valgrind.h" #include "julia_assert.h" +// This structure is used to store hash tables for the memoization +// of queries in staticdata.c (currently only `type_in_worklist`). +typedef struct { + htable_t type_in_worklist; +} jl_query_cache; + +static void init_query_cache(jl_query_cache *cache) +{ + htable_new(&cache->type_in_worklist, 0); +} + +static void destroy_query_cache(jl_query_cache *cache) +{ + htable_free(&cache->type_in_worklist); +} + #include "staticdata_utils.c" #include "precompile_utils.c" @@ -479,6 +495,7 @@ typedef struct { jl_array_t *link_ids_gctags; jl_array_t *link_ids_gvars; jl_array_t *link_ids_external_fnvars; + jl_query_cache *query_cache; jl_ptls_t ptls; htable_t callers_with_edges; jl_image_t *image; @@ -621,38 +638,37 @@ static int jl_needs_serialization(jl_serializer_state *s, jl_value_t *v) JL_NOTS return 1; } - -static int caching_tag(jl_value_t *v) JL_NOTSAFEPOINT +static int caching_tag(jl_value_t *v, jl_query_cache *query_cache) JL_NOTSAFEPOINT { if (jl_is_method_instance(v)) { jl_method_instance_t *mi = (jl_method_instance_t*)v; jl_value_t *m = mi->def.value; if (jl_is_method(m) && jl_object_in_image(m)) - return 1 + type_in_worklist(mi->specTypes); + return 1 + type_in_worklist(mi->specTypes, query_cache); } if (jl_is_datatype(v)) { jl_datatype_t *dt = (jl_datatype_t*)v; if (jl_is_tuple_type(dt) ? !dt->isconcretetype : dt->hasfreetypevars) return 0; // aka !is_cacheable from jltypes.c if (jl_object_in_image((jl_value_t*)dt->name)) - return 1 + type_in_worklist(v); + return 1 + type_in_worklist(v, query_cache); } jl_value_t *dtv = jl_typeof(v); if (jl_is_datatype_singleton((jl_datatype_t*)dtv)) { - return 1 - type_in_worklist(dtv); // these are already recached in the datatype in the image + return 1 - type_in_worklist(dtv, query_cache); // these are already recached in the datatype in the image } return 0; } -static int needs_recaching(jl_value_t *v) JL_NOTSAFEPOINT +static int needs_recaching(jl_value_t *v, jl_query_cache *query_cache) JL_NOTSAFEPOINT { - return caching_tag(v) == 2; + return caching_tag(v, query_cache) == 2; } -static int needs_uniquing(jl_value_t *v) JL_NOTSAFEPOINT +static int needs_uniquing(jl_value_t *v, jl_query_cache *query_cache) JL_NOTSAFEPOINT { assert(!jl_object_in_image(v)); - return caching_tag(v) == 1; + return caching_tag(v, query_cache) == 1; } static void record_field_change(jl_value_t **addr, jl_value_t *newval) JL_NOTSAFEPOINT @@ -738,7 +754,7 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ // ensure all type parameters are recached jl_queue_for_serialization_(s, (jl_value_t*)dt->parameters, 1, 1); jl_value_t *singleton = dt->instance; - if (singleton && needs_uniquing(singleton)) { + if (singleton && needs_uniquing(singleton, s->query_cache)) { assert(jl_needs_serialization(s, singleton)); // should be true, since we visited dt // do not visit dt->instance for our template object as it leads to unwanted cycles here // (it may get serialized from elsewhere though) @@ -749,7 +765,7 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ if (s->incremental && jl_is_method_instance(v)) { jl_method_instance_t *mi = (jl_method_instance_t*)v; jl_value_t *def = mi->def.value; - if (needs_uniquing(v)) { + if (needs_uniquing(v, s->query_cache)) { // we only need 3 specific fields of this (the rest are not used) jl_queue_for_serialization(s, mi->def.value); jl_queue_for_serialization(s, mi->specTypes); @@ -767,7 +783,7 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ record_field_change((jl_value_t**)&mi->cache, NULL); } else { - assert(!needs_recaching(v)); + assert(!needs_recaching(v, s->query_cache)); } // n.b. opaque closures cannot be inspected and relied upon like a // normal method since they can get improperly introduced by generated @@ -901,9 +917,9 @@ static void jl_queue_for_serialization_(jl_serializer_state *s, jl_value_t *v, i // Items that require postorder traversal must visit their children prior to insertion into // the worklist/serialization_order (and also before their first use) if (s->incremental && !immediate) { - if (jl_is_datatype(t) && needs_uniquing(v)) + if (jl_is_datatype(t) && needs_uniquing(v, s->query_cache)) immediate = 1; - if (jl_is_datatype_singleton((jl_datatype_t*)t) && needs_uniquing(v)) + if (jl_is_datatype_singleton((jl_datatype_t*)t) && needs_uniquing(v, s->query_cache)) immediate = 1; } @@ -1067,7 +1083,7 @@ static uintptr_t _backref_id(jl_serializer_state *s, jl_value_t *v, jl_array_t * static void record_uniquing(jl_serializer_state *s, jl_value_t *fld, uintptr_t offset) JL_NOTSAFEPOINT { - if (s->incremental && jl_needs_serialization(s, fld) && needs_uniquing(fld)) { + if (s->incremental && jl_needs_serialization(s, fld) && needs_uniquing(fld, s->query_cache)) { if (jl_is_datatype(fld) || jl_is_datatype_singleton((jl_datatype_t*)jl_typeof(fld))) arraylist_push(&s->uniquing_types, (void*)(uintptr_t)offset); else if (jl_is_method_instance(fld)) @@ -1211,7 +1227,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED write_padding(f, LLT_ALIGN(skip_header_pos, 16) - skip_header_pos); // write header - if (s->incremental && jl_needs_serialization(s, (jl_value_t*)t) && needs_uniquing((jl_value_t*)t)) + if (s->incremental && jl_needs_serialization(s, (jl_value_t*)t) && needs_uniquing((jl_value_t*)t, s->query_cache)) arraylist_push(&s->uniquing_types, (void*)(uintptr_t)(ios_pos(f)|1)); if (f == s->const_data) write_uint(s->const_data, ((uintptr_t)t->smalltag << 4) | GC_OLD_MARKED); @@ -1222,7 +1238,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED layout_table.items[item] = (void*)(reloc_offset | (f == s->const_data)); // store the inverse mapping of `serialization_order` (`id` => object-as-streampos) if (s->incremental) { - if (needs_uniquing(v)) { + if (needs_uniquing(v, s->query_cache)) { if (jl_is_method_instance(v)) { assert(f == s->s); jl_method_instance_t *mi = (jl_method_instance_t*)v; @@ -1243,7 +1259,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED assert(jl_is_datatype_singleton(t) && "unreachable"); } } - else if (needs_recaching(v)) { + else if (needs_recaching(v, s->query_cache)) { arraylist_push(jl_is_datatype(v) ? &s->fixup_types : &s->fixup_objs, (void*)reloc_offset); } else if (jl_typetagis(v, jl_binding_type)) { @@ -1606,7 +1622,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED } } void *superidx = ptrhash_get(&serialization_order, dt->super); - if (s->incremental && superidx != HT_NOTFOUND && (char*)superidx - 1 - (char*)HT_NOTFOUND > item && needs_uniquing((jl_value_t*)dt->super)) + if (s->incremental && superidx != HT_NOTFOUND && (char*)superidx - 1 - (char*)HT_NOTFOUND > item && needs_uniquing((jl_value_t*)dt->super, s->query_cache)) arraylist_push(&s->uniquing_super, dt->super); } else if (jl_is_typename(v)) { @@ -2351,7 +2367,8 @@ JL_DLLEXPORT jl_value_t *jl_as_global_root(jl_value_t *val JL_MAYBE_UNROOTED) static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *newly_inferred, uint64_t worklist_key, /* outputs */ jl_array_t **extext_methods, jl_array_t **new_specializations, - jl_array_t **method_roots_list, jl_array_t **ext_targets, jl_array_t **edges) + jl_array_t **method_roots_list, jl_array_t **ext_targets, jl_array_t **edges, + jl_query_cache *query_cache) { // extext_methods: [method1, ...], worklist-owned "extending external" methods added to functions owned by modules outside the worklist // ext_targets: [invokesig1, callee1, matches1, ...] non-worklist callees of worklist-owned methods @@ -2362,7 +2379,7 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new assert(edges_map == NULL); // Save the inferred code from newly inferred, external methods - *new_specializations = queue_external_cis(newly_inferred); + *new_specializations = queue_external_cis(newly_inferred, query_cache); // Collect method extensions and edges data JL_GC_PUSH1(&edges_map); @@ -2401,7 +2418,8 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, jl_array_t *worklist, jl_array_t *extext_methods, jl_array_t *new_specializations, jl_array_t *method_roots_list, - jl_array_t *ext_targets, jl_array_t *edges) JL_GC_DISABLED + jl_array_t *ext_targets, jl_array_t *edges, + jl_query_cache *query_cache) JL_GC_DISABLED { htable_new(&field_replace, 0); // strip metadata and IR when requested @@ -2428,6 +2446,7 @@ static void jl_save_system_image_to_stream(ios_t *f, jl_array_t *mod_array, ios_mem(&gvar_record, 0); ios_mem(&fptr_record, 0); jl_serializer_state s = {0}; + s.query_cache = query_cache; s.incremental = !(worklist == NULL); s.s = &sysimg; s.const_data = &const_data; @@ -2743,12 +2762,15 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli int64_t datastartpos = 0; JL_GC_PUSH6(&mod_array, &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges); + jl_query_cache query_cache; + init_query_cache(&query_cache); + if (worklist) { mod_array = jl_get_loaded_modules(); // __toplevel__ modules loaded in this session (from Base.loaded_modules_array) // Generate _native_data` if (_native_data != NULL) { jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist), - &extext_methods, &new_specializations, NULL, NULL, NULL); + &extext_methods, &new_specializations, NULL, NULL, NULL, &query_cache); jl_precompile_toplevel_module = (jl_module_t*)jl_array_ptr_ref(worklist, jl_array_len(worklist)-1); *_native_data = jl_precompile_worklist(worklist, extext_methods, new_specializations); jl_precompile_toplevel_module = NULL; @@ -2777,7 +2799,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli if (worklist) { htable_new(&relocatable_ext_cis, 0); jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist), - &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges); + &extext_methods, &new_specializations, &method_roots_list, &ext_targets, &edges, &query_cache); if (!emit_split) { write_int32(f, 0); // No clone_targets write_padding(f, LLT_ALIGN(ios_pos(f), JL_CACHE_BYTE_ALIGNMENT) - ios_pos(f)); @@ -2789,7 +2811,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli } if (_native_data != NULL) native_functions = *_native_data; - jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_specializations, method_roots_list, ext_targets, edges); + jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_specializations, method_roots_list, ext_targets, edges, &query_cache); if (_native_data != NULL) native_functions = NULL; if (worklist) @@ -2820,6 +2842,8 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli } } + destroy_query_cache(&query_cache); + JL_GC_POP(); *s = f; if (emit_split) @@ -3451,7 +3475,11 @@ static jl_value_t *jl_restore_package_image_from_stream(void* pkgimage_handle, i jl_insert_methods(extext_methods); // No special processing of `new_specializations` is required because recaching handled it // Add roots to methods - jl_copy_roots(method_roots_list, jl_worklist_key((jl_array_t*)restored)); + int failed = jl_copy_roots(method_roots_list, jl_worklist_key((jl_array_t*)restored)); + if (failed != 0) { + jl_printf(JL_STDERR, "Error copying roots to methods from Module: %s\n", pkgname); + abort(); + } // Handle edges size_t world = jl_atomic_load_acquire(&jl_world_counter); jl_insert_backedges((jl_array_t*)edges, (jl_array_t*)ext_targets, (jl_array_t*)new_specializations, world); // restore external backedges (needs to be last) diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index ce2c9fa70d67c..2972f33756c95 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -103,62 +103,80 @@ JL_DLLEXPORT void jl_push_newly_inferred(jl_value_t* ci) JL_UNLOCK(&newly_inferred_mutex); } - // compute whether a type references something internal to worklist // and thus could not have existed before deserialize // and thus does not need delayed unique-ing -static int type_in_worklist(jl_value_t *v) JL_NOTSAFEPOINT +static int type_in_worklist(jl_value_t *v, jl_query_cache *cache) JL_NOTSAFEPOINT { if (jl_object_in_image(v)) return 0; // fast-path for rejection + + void *cached = HT_NOTFOUND; + if (cache != NULL) + cached = ptrhash_get(&cache->type_in_worklist, v); + + // fast-path for memoized results + if (cached != HT_NOTFOUND) + return cached == v; + + int result = 0; if (jl_is_uniontype(v)) { jl_uniontype_t *u = (jl_uniontype_t*)v; - return type_in_worklist(u->a) || - type_in_worklist(u->b); + result = type_in_worklist(u->a, cache) || + type_in_worklist(u->b, cache); } else if (jl_is_unionall(v)) { jl_unionall_t *ua = (jl_unionall_t*)v; - return type_in_worklist((jl_value_t*)ua->var) || - type_in_worklist(ua->body); + result = type_in_worklist((jl_value_t*)ua->var, cache) || + type_in_worklist(ua->body, cache); } else if (jl_is_typevar(v)) { jl_tvar_t *tv = (jl_tvar_t*)v; - return type_in_worklist(tv->lb) || - type_in_worklist(tv->ub); + result = type_in_worklist(tv->lb, cache) || + type_in_worklist(tv->ub, cache); } else if (jl_is_vararg(v)) { jl_vararg_t *tv = (jl_vararg_t*)v; - if (tv->T && type_in_worklist(tv->T)) - return 1; - if (tv->N && type_in_worklist(tv->N)) - return 1; + result = ((tv->T && type_in_worklist(tv->T, cache)) || + (tv->N && type_in_worklist(tv->N, cache))); } else if (jl_is_datatype(v)) { jl_datatype_t *dt = (jl_datatype_t*)v; - if (!jl_object_in_image((jl_value_t*)dt->name)) - return 1; - jl_svec_t *tt = dt->parameters; - size_t i, l = jl_svec_len(tt); - for (i = 0; i < l; i++) - if (type_in_worklist(jl_tparam(dt, i))) - return 1; + if (!jl_object_in_image((jl_value_t*)dt->name)) { + result = 1; + } + else { + jl_svec_t *tt = dt->parameters; + size_t i, l = jl_svec_len(tt); + for (i = 0; i < l; i++) { + if (type_in_worklist(jl_tparam(dt, i), cache)) { + result = 1; + break; + } + } + } } else { - return type_in_worklist(jl_typeof(v)); + return type_in_worklist(jl_typeof(v), cache); } - return 0; + + // Memoize result + if (cache != NULL) + ptrhash_put(&cache->type_in_worklist, (void*)v, result ? (void*)v : NULL); + + return result; } // When we infer external method instances, ensure they link back to the // package. Otherwise they might be, e.g., for external macros. // Implements Tarjan's SCC (strongly connected components) algorithm, simplified to remove the count variable -static int has_backedge_to_worklist(jl_method_instance_t *mi, htable_t *visited, arraylist_t *stack) +static int has_backedge_to_worklist(jl_method_instance_t *mi, htable_t *visited, arraylist_t *stack, jl_query_cache *query_cache) { jl_module_t *mod = mi->def.module; if (jl_is_method(mod)) mod = ((jl_method_t*)mod)->module; assert(jl_is_module(mod)); - if (mi->precompiled || !jl_object_in_image((jl_value_t*)mod) || type_in_worklist(mi->specTypes)) { + if (mi->precompiled || !jl_object_in_image((jl_value_t*)mod) || type_in_worklist(mi->specTypes, query_cache)) { return 1; } if (!mi->backedges) { @@ -181,7 +199,7 @@ static int has_backedge_to_worklist(jl_method_instance_t *mi, htable_t *visited, while (i < n) { jl_method_instance_t *be; i = get_next_edge(mi->backedges, i, NULL, &be); - int child_found = has_backedge_to_worklist(be, visited, stack); + int child_found = has_backedge_to_worklist(be, visited, stack, query_cache); if (child_found == 1 || child_found == 2) { // found what we were looking for, so terminate early found = 1; @@ -224,7 +242,7 @@ static int is_relocatable_ci(htable_t *relocatable_ext_cis, jl_code_instance_t * // from the worklist or explicitly added by a `precompile` statement, and // (4) are the most recently computed result for that method. // These will be preserved in the image. -static jl_array_t *queue_external_cis(jl_array_t *list) +static jl_array_t *queue_external_cis(jl_array_t *list, jl_query_cache *query_cache) { if (list == NULL) return NULL; @@ -245,7 +263,7 @@ static jl_array_t *queue_external_cis(jl_array_t *list) jl_method_instance_t *mi = ci->def; jl_method_t *m = mi->def.method; if (ci->inferred && jl_is_method(m) && jl_object_in_image((jl_value_t*)m->module)) { - int found = has_backedge_to_worklist(mi, &visited, &stack); + int found = has_backedge_to_worklist(mi, &visited, &stack, query_cache); assert(found == 0 || found == 1 || found == 2); assert(stack.len == 0); if (found == 1 && ci->max_world == ~(size_t)0) { @@ -829,17 +847,31 @@ static void jl_insert_methods(jl_array_t *list) } } -static void jl_copy_roots(jl_array_t *method_roots_list, uint64_t key) +static int jl_copy_roots(jl_array_t *method_roots_list, uint64_t key) { size_t i, l = jl_array_len(method_roots_list); + int failed = 0; for (i = 0; i < l; i+=2) { jl_method_t *m = (jl_method_t*)jl_array_ptr_ref(method_roots_list, i); jl_array_t *roots = (jl_array_t*)jl_array_ptr_ref(method_roots_list, i+1); if (roots) { assert(jl_is_array(roots)); + if (m->root_blocks) { + // check for key collision + uint64_t *blocks = (uint64_t*)jl_array_data(m->root_blocks); + size_t nx2 = jl_array_nrows(m->root_blocks); + for (size_t i = 0; i < nx2; i+=2) { + if (blocks[i] == key) { + // found duplicate block + failed = -1; + } + } + } + jl_append_method_roots(m, key, roots); } } + return failed; } diff --git a/src/subtype.c b/src/subtype.c index 5b326237e0dd2..bc66a7a9da14b 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -128,7 +128,7 @@ static jl_varbinding_t *lookup(jl_stenv_t *e, jl_tvar_t *v) JL_GLOBALLY_ROOTED J static int statestack_get(jl_unionstate_t *st, int i) JL_NOTSAFEPOINT { - assert(i >= 0 && i <= 32767); // limited by the depth bit. + assert(i >= 0 && i < 32767); // limited by the depth bit. // get the `i`th bit in an array of 32-bit words jl_bits_stack_t *stack = &st->stack; while (i >= sizeof(stack->data) * 8) { @@ -142,7 +142,7 @@ static int statestack_get(jl_unionstate_t *st, int i) JL_NOTSAFEPOINT static void statestack_set(jl_unionstate_t *st, int i, int val) JL_NOTSAFEPOINT { - assert(i >= 0 && i <= 32767); // limited by the depth bit. + assert(i >= 0 && i < 32767); // limited by the depth bit. jl_bits_stack_t *stack = &st->stack; while (i >= sizeof(stack->data) * 8) { if (__unlikely(stack->next == NULL)) { @@ -1052,7 +1052,8 @@ static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e); static int subtype_tuple_varargs( jl_vararg_t *vtx, jl_vararg_t *vty, - size_t vx, size_t vy, + jl_value_t *lastx, jl_value_t *lasty, + size_t vx, size_t vy, size_t x_reps, jl_stenv_t *e, int param) { jl_value_t *xp0 = jl_unwrap_vararg(vtx); jl_value_t *xp1 = jl_unwrap_vararg_num(vtx); @@ -1103,12 +1104,30 @@ static int subtype_tuple_varargs( } } } - - // in Vararg{T1} <: Vararg{T2}, need to check subtype twice to - // simulate the possibility of multiple arguments, which is needed - // to implement the diagonal rule correctly. - if (!subtype(xp0, yp0, e, param)) return 0; - if (!subtype(xp0, yp0, e, 1)) return 0; + int x_same = vx > 1 || (lastx && obviously_egal(xp0, lastx)); + int y_same = vy > 1 || (lasty && obviously_egal(yp0, lasty)); + // keep track of number of consecutive identical subtyping + x_reps = y_same && x_same ? x_reps + 1 : 1; + if (x_reps > 2) { + // an identical type on the left doesn't need to be compared to the same + // element type on the right more than twice. + } + else if (x_same && e->Runions.depth == 0 && y_same && + !jl_has_free_typevars(xp0) && !jl_has_free_typevars(yp0)) { + // fast path for repeated elements + } + else if ((e->Runions.depth == 0 ? !jl_has_free_typevars(xp0) : jl_is_concrete_type(xp0)) && !jl_has_free_typevars(yp0)) { + // fast path for separable sub-formulas + if (!jl_subtype(xp0, yp0)) + return 0; + } + else { + // in Vararg{T1} <: Vararg{T2}, need to check subtype twice to + // simulate the possibility of multiple arguments, which is needed + // to implement the diagonal rule correctly. + if (!subtype(xp0, yp0, e, param)) return 0; + if (x_reps < 2 && !subtype(xp0, yp0, e, 1)) return 0; + } constrain_length: if (!yp1) { @@ -1232,7 +1251,8 @@ static int subtype_tuple_tail(jl_datatype_t *xd, jl_datatype_t *yd, int8_t R, jl return subtype_tuple_varargs( (jl_vararg_t*)xi, (jl_vararg_t*)yi, - vx, vy, e, param); + lastx, lasty, + vx, vy, x_reps, e, param); } if (j >= ly) @@ -1253,7 +1273,7 @@ static int subtype_tuple_tail(jl_datatype_t *xd, jl_datatype_t *yd, int8_t R, jl (yi == lastx && !vx && vy && jl_is_concrete_type(xi)))) { // fast path for repeated elements } - else if (e->Runions.depth == 0 && !jl_has_free_typevars(xi) && !jl_has_free_typevars(yi)) { + else if ((e->Runions.depth == 0 ? !jl_has_free_typevars(xi) : jl_is_concrete_type(xi)) && !jl_has_free_typevars(yi)) { // fast path for separable sub-formulas if (!jl_subtype(xi, yi)) return 0; @@ -1354,7 +1374,7 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) x = pick_union_element(x, e, 0); } if (jl_is_uniontype(y)) { - if (x == ((jl_uniontype_t*)y)->a || x == ((jl_uniontype_t*)y)->b) + if (obviously_in_union(y, x)) return 1; if (jl_is_unionall(x)) return subtype_unionall(y, (jl_unionall_t*)x, e, 0, param); @@ -1409,11 +1429,14 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) } if (jl_is_unionall(y)) { jl_varbinding_t *xb = lookup(e, (jl_tvar_t*)x); - if (xb == NULL ? !e->ignore_free : !xb->right) { + jl_value_t *xub = xb == NULL ? ((jl_tvar_t *)x)->ub : xb->ub; + if ((xb == NULL ? !e->ignore_free : !xb->right) && xub != y) { // We'd better unwrap `y::UnionAll` eagerly if `x` isa ∀-var. // This makes sure the following cases work correct: // 1) `∀T <: Union{∃S, SomeType{P}} where {P}`: `S == Any` ==> `S >: T` // 2) `∀T <: Union{∀T, SomeType{P}} where {P}`: + // note: if xub == y we'd better try `subtype_var` as `subtype_left_var` + // hit `==` based fast path. return subtype_unionall(x, (jl_unionall_t*)y, e, 1, param); } } @@ -1551,6 +1574,8 @@ static int has_exists_typevar(jl_value_t *x, jl_stenv_t *e) JL_NOTSAFEPOINT return env != NULL && jl_has_bound_typevars(x, env); } +static int forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param); + static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param, int limit_slow) { int16_t oldRmore = e->Runions.more; @@ -1564,7 +1589,18 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t return jl_subtype(x, y); int has_exists = (!kindx && has_exists_typevar(x, e)) || (!kindy && has_exists_typevar(y, e)); - if (has_exists && (is_exists_typevar(x, e) != is_exists_typevar(y, e))) { + if (!has_exists) { + // We can use ∀_∃_subtype safely for ∃ free inputs. + // This helps to save some bits in union stack. + jl_saved_unionstate_t oldRunions; push_unionstate(&oldRunions, &e->Runions); + e->Lunions.used = e->Runions.used = 0; + e->Lunions.depth = e->Runions.depth = 0; + e->Lunions.more = e->Runions.more = 0; + sub = forall_exists_subtype(x, y, e, param); + pop_unionstate(&e->Runions, &oldRunions); + return sub; + } + if (is_exists_typevar(x, e) != is_exists_typevar(y, e)) { e->Lunions.used = 0; while (1) { e->Lunions.more = 0; @@ -1578,7 +1614,7 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t if (limit_slow == -1) limit_slow = kindx || kindy; jl_savedenv_t se; - save_env(e, &se, has_exists); + save_env(e, &se, 1); int count, limited = 0, ini_count = 0; jl_saved_unionstate_t latestLunions = {0, 0, 0, NULL}; while (1) { @@ -1596,13 +1632,13 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t limited = 1; if (!sub || !next_union_state(e, 0)) break; - if (limited || !has_exists || e->Runions.more == oldRmore) { + if (limited || e->Runions.more == oldRmore) { // re-save env and freeze the ∃decision for previous ∀Union // Note: We could ignore the rest `∃Union` decisions if `x` and `y` // contain no ∃ typevar, as they have no effect on env. ini_count = count; push_unionstate(&latestLunions, &e->Lunions); - re_save_env(e, &se, has_exists); + re_save_env(e, &se, 1); e->Runions.more = oldRmore; } } @@ -1610,12 +1646,12 @@ static int local_forall_exists_subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t break; assert(e->Runions.more > oldRmore); next_union_state(e, 1); - restore_env(e, &se, has_exists); // also restore Rdepth here + restore_env(e, &se, 1); // also restore Rdepth here e->Runions.more = oldRmore; } if (!sub) assert(e->Runions.more == oldRmore); - else if (limited || !has_exists) + else if (limited) e->Runions.more = oldRmore; free_env(&se); return sub; @@ -2484,9 +2520,6 @@ static jl_value_t *intersect_aside(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, static jl_value_t *intersect_union(jl_value_t *x, jl_uniontype_t *u, jl_stenv_t *e, int8_t R, int param) { - // band-aid for #56040 - if (!jl_is_uniontype(x) && obviously_in_union((jl_value_t *)u, x)) - return x; if (param == 2 || (!jl_has_free_typevars(x) && !jl_has_free_typevars((jl_value_t*)u))) { jl_value_t *a=NULL, *b=NULL; JL_GC_PUSH2(&a, &b); @@ -2616,31 +2649,22 @@ static void set_bound(jl_value_t **bound, jl_value_t *val, jl_tvar_t *v, jl_sten // subtype, treating all vars as existential static int subtype_in_env_existential(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) { - jl_varbinding_t *v = e->vars; - int len = 0; - if (x == jl_bottom_type || y == (jl_value_t*)jl_any_type) + if (x == jl_bottom_type || y == (jl_value_t*)jl_any_type || obviously_in_union(y, x)) return 1; - while (v != NULL) { - len++; - v = v->prev; - } - int8_t *rs = (int8_t*)malloc_s(len); + int8_t *rs = (int8_t*)alloca(current_env_length(e)); + jl_varbinding_t *v = e->vars; int n = 0; - v = e->vars; - while (n < len) { - assert(v != NULL); + while (v != NULL) { rs[n++] = v->right; v->right = 1; v = v->prev; } int issub = subtype_in_env(x, y, e); n = 0; v = e->vars; - while (n < len) { - assert(v != NULL); + while (v != NULL) { v->right = rs[n++]; v = v->prev; } - free(rs); return issub; } @@ -2688,6 +2712,8 @@ static int check_unsat_bound(jl_value_t *t, jl_tvar_t *v, jl_stenv_t *e) JL_NOTS } +static int intersect_var_ccheck_in_env(jl_value_t *xlb, jl_value_t *xub, jl_value_t *ylb, jl_value_t *yub, jl_stenv_t *e, int flip); + static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int8_t R, int param) { jl_varbinding_t *bb = lookup(e, b); @@ -2699,20 +2725,14 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int return R ? intersect(a, bb->lb, e, param) : intersect(bb->lb, a, e, param); if (!jl_is_type(a) && !jl_is_typevar(a)) return set_var_to_const(bb, a, e, R); - jl_savedenv_t se; if (param == 2) { jl_value_t *ub = NULL; JL_GC_PUSH1(&ub); if (!jl_has_free_typevars(a)) { - save_env(e, &se, 1); - int issub = subtype_in_env_existential(bb->lb, a, e); - restore_env(e, &se, 1); - if (issub) { - issub = subtype_in_env_existential(a, bb->ub, e); - restore_env(e, &se, 1); - } - free_env(&se); - if (!issub) { + if (R) flip_offset(e); + int ccheck = intersect_var_ccheck_in_env(bb->lb, bb->ub, a, a, e, !R); + if (R) flip_offset(e); + if (!ccheck) { JL_GC_POP(); return jl_bottom_type; } @@ -2722,6 +2742,7 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int e->triangular++; ub = R ? intersect_aside(a, bb->ub, e, bb->depth0) : intersect_aside(bb->ub, a, e, bb->depth0); e->triangular--; + jl_savedenv_t se; save_env(e, &se, 1); int issub = subtype_in_env_existential(bb->lb, ub, e); restore_env(e, &se, 1); @@ -2751,7 +2772,7 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int jl_value_t *ub = R ? intersect_aside(a, bb->ub, e, bb->depth0) : intersect_aside(bb->ub, a, e, bb->depth0); if (ub == jl_bottom_type) return jl_bottom_type; - if (bb->constraintkind == 1 || e->triangular) { + if (bb->constraintkind == 1 || (e->triangular && param == 1)) { if (e->triangular && check_unsat_bound(ub, b, e)) return jl_bottom_type; set_bound(&bb->ub, ub, b, e); @@ -3654,6 +3675,89 @@ static int subtype_by_bounds(jl_value_t *x, jl_value_t *y, jl_stenv_t *e) JL_NOT return compareto_var(x, (jl_tvar_t*)y, e, -1) || compareto_var(y, (jl_tvar_t*)x, e, 1); } +static int intersect_var_ccheck_in_env(jl_value_t *xlb, jl_value_t *xub, jl_value_t *ylb, jl_value_t *yub, jl_stenv_t *e, int flip) +{ + int easy_check1 = xlb == jl_bottom_type || + yub == (jl_value_t *)jl_any_type || + (e->Loffset == 0 && obviously_in_union(yub, xlb)); + int easy_check2 = ylb == jl_bottom_type || + xub == (jl_value_t *)jl_any_type || + (e->Loffset == 0 && obviously_in_union(xub, ylb)); + int nofree1 = 0, nofree2 = 0; + if (!easy_check1) { + nofree1 = !jl_has_free_typevars(xlb) && !jl_has_free_typevars(yub); + if (nofree1 && e->Loffset == 0) { + easy_check1 = jl_subtype(xlb, yub); + if (!easy_check1) + return 0; + } + } + if (!easy_check2) { + nofree2 = !jl_has_free_typevars(ylb) && !jl_has_free_typevars(xub); + if (nofree2 && e->Loffset == 0) { + easy_check2 = jl_subtype(ylb, xub); + if (!easy_check2) + return 0; + } + } + if (easy_check1 && easy_check2) + return 1; + int ccheck = 0; + if ((easy_check1 || nofree1) && (easy_check2 || nofree2)) { + jl_varbinding_t *vars = e->vars; + e->vars = NULL; + ccheck = easy_check1 || subtype_in_env(xlb, yub, e); + if (ccheck && !easy_check2) { + flip_offset(e); + ccheck = subtype_in_env(ylb, xub, e); + flip_offset(e); + } + e->vars = vars; + return ccheck; + } + jl_savedenv_t se; + save_env(e, &se, 1); + // first try normal flip. + if (flip) flip_vars(e); + ccheck = easy_check1 || subtype_in_env(xlb, yub, e); + if (ccheck && !easy_check2) { + flip_offset(e); + ccheck = subtype_in_env(ylb, xub, e); + flip_offset(e); + } + if (flip) flip_vars(e); + if (!ccheck) { + // then try reverse flip. + restore_env(e, &se, 1); + if (!flip) flip_vars(e); + ccheck = easy_check1 || subtype_in_env(xlb, yub, e); + if (ccheck && !easy_check2) { + flip_offset(e); + ccheck = subtype_in_env(ylb, xub, e); + flip_offset(e); + } + if (!flip) flip_vars(e); + } + if (!ccheck) { + // then try existential. + restore_env(e, &se, 1); + if (easy_check1) + ccheck = 1; + else { + ccheck = subtype_in_env_existential(xlb, yub, e); + restore_env(e, &se, 1); + } + if (ccheck && !easy_check2) { + flip_offset(e); + ccheck = subtype_in_env_existential(ylb, xub, e); + flip_offset(e); + restore_env(e, &se, 1); + } + } + free_env(&se); + return ccheck; +} + static int has_typevar_via_env(jl_value_t *x, jl_tvar_t *t, jl_stenv_t *e) { if (e->Loffset == 0) { @@ -3786,14 +3890,8 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa ccheck = 1; } else { - if (R) flip_vars(e); - ccheck = subtype_in_env(xlb, yub, e); - if (ccheck) { - flip_offset(e); - ccheck = subtype_in_env(ylb, xub, e); - flip_offset(e); - } - if (R) flip_vars(e); + // try many subtype check to avoid false `Union{}` + ccheck = intersect_var_ccheck_in_env(xlb, xub, ylb, yub, e, R); } if (R) flip_offset(e); if (!ccheck) @@ -3849,12 +3947,14 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa if (jl_subtype(y, x)) return y; } if (jl_is_uniontype(x)) { - if (y == ((jl_uniontype_t*)x)->a || y == ((jl_uniontype_t*)x)->b) + if (obviously_in_union(x, y)) return y; + if (jl_is_uniontype(y) && obviously_in_union(y, x)) + return x; return intersect_union(y, (jl_uniontype_t*)x, e, 0, param); } if (jl_is_uniontype(y)) { - if (x == ((jl_uniontype_t*)y)->a || x == ((jl_uniontype_t*)y)->b) + if (obviously_in_union(y, x)) return x; if (jl_is_unionall(x) && (jl_has_free_typevars(x) || jl_has_free_typevars(y))) return intersect_unionall(y, (jl_unionall_t*)x, e, 0, param); diff --git a/src/support/utf8.c b/src/support/utf8.c index 42a420fb0c499..17dcf5f1efd51 100644 --- a/src/support/utf8.c +++ b/src/support/utf8.c @@ -410,7 +410,7 @@ int u8_escape_wchar(char *buf, size_t sz, uint32_t ch) } size_t u8_escape(char *buf, size_t sz, const char *src, size_t *pi, size_t end, - int escape_quotes, int ascii) + const char *escapes, int ascii) { size_t i = *pi, i0; uint32_t ch; @@ -420,12 +420,9 @@ size_t u8_escape(char *buf, size_t sz, const char *src, size_t *pi, size_t end, while (i 0x9f) return 0; - // Check for overlong encoding - if (byt == 0xe0 && *pnt < 0xa0) return 0; + // Check for overlong encoding + if (byt == 0xe0 && *pnt < 0xa0) return 0; pnt += 2; } else { // 4-byte sequence // Must have 3 valid continuation characters diff --git a/src/support/utf8.h b/src/support/utf8.h index a3d89f2268b41..eab86f602ee61 100644 --- a/src/support/utf8.h +++ b/src/support/utf8.h @@ -63,7 +63,7 @@ int u8_escape_wchar(char *buf, size_t sz, uint32_t ch); sz is buf size in bytes. must be at least 12. - if escape_quotes is nonzero, quote characters will be escaped. + if escapes is given, given characters will also be escaped (in addition to \\). if ascii is nonzero, the output is 7-bit ASCII, no UTF-8 survives. @@ -75,7 +75,7 @@ int u8_escape_wchar(char *buf, size_t sz, uint32_t ch); returns number of bytes placed in buf, including a NUL terminator. */ size_t u8_escape(char *buf, size_t sz, const char *src, size_t *pi, size_t end, - int escape_quotes, int ascii); + const char *escapes, int ascii); /* utility predicates used by the above */ int octal_digit(char c); diff --git a/stdlib/Dates/src/io.jl b/stdlib/Dates/src/io.jl index 257e86064c2fb..0771d0f46c69f 100644 --- a/stdlib/Dates/src/io.jl +++ b/stdlib/Dates/src/io.jl @@ -460,7 +460,7 @@ but creates the DateFormat object once during macro expansion. See [`DateFormat`](@ref) for details about format specifiers. """ -macro dateformat_str(str) +macro dateformat_str(str::String) DateFormat(str) end diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index d93ee299ba834..73af49f8ef0c3 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -364,7 +364,7 @@ let err = tempname(), @test startswith(errstr, """start end Internal error: encountered unexpected error during compilation of f_broken_code: - ErrorException(\"unsupported or misplaced expression \"invalid\" in function f_broken_code\") + ErrorException(\"unsupported or misplaced expression \\\"invalid\\\" in function f_broken_code\") """) || errstr @test !endswith(errstr, "\nend\n") || errstr end diff --git a/stdlib/LibGit2/src/LibGit2.jl b/stdlib/LibGit2/src/LibGit2.jl index 13b78c1046490..1d1874d55e992 100644 --- a/stdlib/LibGit2/src/LibGit2.jl +++ b/stdlib/LibGit2/src/LibGit2.jl @@ -1005,24 +1005,20 @@ function set_ssl_cert_locations(cert_loc) else # files, /dev/null, non-existent paths, etc. cert_file = cert_loc end - ret = @ccall libgit2.git_libgit2_opts( + ret = @ccall libgit2.git_libgit2_opts( Consts.SET_SSL_CERT_LOCATIONS::Cint; cert_file::Cstring, cert_dir::Cstring)::Cint ret >= 0 && return ret + # On macOS and Windows LibGit2_jll is built without a TLS backend that supports + # certificate locations; don't throw on this expected error so we allow certificate + # location environment variables to be set for other purposes. + # We still try doing so to support other LibGit2 builds. err = Error.GitError(ret) err.class == Error.SSL && err.msg == "TLS backend doesn't support certificate locations" || throw(err) - var = nothing - for v in NetworkOptions.CA_ROOTS_VARS - haskey(ENV, v) && (var = v) - end - @assert var !== nothing # otherwise we shouldn't be here - msg = """ - Your Julia is built with a SSL/TLS engine that libgit2 doesn't know how to configure to use a file or directory of certificate authority roots, but your environment specifies one via the $var variable. If you believe your system's root certificates are safe to use, you can `export JULIA_SSL_CA_ROOTS_PATH=""` in your environment to use those instead. - """ - throw(Error.GitError(err.class, err.code, chomp(msg))) + return ret end """ diff --git a/stdlib/LibGit2/test/bad_ca_roots.jl b/stdlib/LibGit2/test/bad_ca_roots.jl index 4882065167bdb..4caed4ed90beb 100644 --- a/stdlib/LibGit2/test/bad_ca_roots.jl +++ b/stdlib/LibGit2/test/bad_ca_roots.jl @@ -12,20 +12,24 @@ const CAN_SET_CA_ROOTS_PATH = !Sys.isapple() && !Sys.iswindows() # Given this is a sub-processed test file, not using @testsets avoids # leaking the report print into the Base test runner report begin # empty CA roots file - # these fail for different reasons on different platforms: - # - on Apple & Windows you cannot set the CA roots path location - # - on Linux & FreeBSD you you can but these are invalid files + # different behavior on different platforms: + # - on Apple & Windows you cannot set the CA roots path location; don't error + # - on Linux & FreeBSD you can but these are invalid files + ENV["JULIA_SSL_CA_ROOTS_PATH"] = "/dev/null" - @test_throws LibGit2.GitError LibGit2.ensure_initialized() + if CAN_SET_CA_ROOTS_PATH + @test_throws LibGit2.GitError LibGit2.ensure_initialized() + else + @test LibGit2.ensure_initialized() === nothing + end + ENV["JULIA_SSL_CA_ROOTS_PATH"] = tempname() - @test_throws LibGit2.GitError LibGit2.ensure_initialized() - # test that it still fails if called a second time - @test_throws LibGit2.GitError LibGit2.ensure_initialized() - if !CAN_SET_CA_ROOTS_PATH - # test that this doesn't work on macOS & Windows - ENV["JULIA_SSL_CA_ROOTS_PATH"] = NetworkOptions.bundled_ca_roots() + if CAN_SET_CA_ROOTS_PATH + @test_throws LibGit2.GitError LibGit2.ensure_initialized() + # test that it still fails if called a second time @test_throws LibGit2.GitError LibGit2.ensure_initialized() - delete!(ENV, "JULIA_SSL_CA_ROOTS_PATH") + else + @test LibGit2.ensure_initialized() === nothing @test LibGit2.ensure_initialized() === nothing end end diff --git a/stdlib/Logging/src/ConsoleLogger.jl b/stdlib/Logging/src/ConsoleLogger.jl index 747f8a2b22966..6cb0851675390 100644 --- a/stdlib/Logging/src/ConsoleLogger.jl +++ b/stdlib/Logging/src/ConsoleLogger.jl @@ -9,6 +9,9 @@ interactive work with the Julia REPL. Log levels less than `min_level` are filtered out. +This Logger is thread-safe, with locks for both orchestration of message +limits i.e. `maxlog`, and writes to the stream. + Message formatting can be controlled by setting keyword arguments: * `meta_formatter` is a function which takes the log event metadata @@ -24,6 +27,7 @@ Message formatting can be controlled by setting keyword arguments: """ struct ConsoleLogger <: AbstractLogger stream::IO + lock::ReentrantLock # do not log within this lock min_level::LogLevel meta_formatter show_limited::Bool @@ -33,19 +37,19 @@ end function ConsoleLogger(stream::IO, min_level=Info; meta_formatter=default_metafmt, show_limited=true, right_justify=0) - ConsoleLogger(stream, min_level, meta_formatter, + ConsoleLogger(stream, ReentrantLock(), min_level, meta_formatter, show_limited, right_justify, Dict{Any,Int}()) end function ConsoleLogger(min_level=Info; meta_formatter=default_metafmt, show_limited=true, right_justify=0) - ConsoleLogger(closed_stream, min_level, meta_formatter, + ConsoleLogger(closed_stream, ReentrantLock(), min_level, meta_formatter, show_limited, right_justify, Dict{Any,Int}()) end shouldlog(logger::ConsoleLogger, level, _module, group, id) = - get(logger.message_limits, id, 1) > 0 + @lock logger.lock get(logger.message_limits, id, 1) > 0 min_enabled_level(logger::ConsoleLogger) = logger.min_level @@ -109,9 +113,11 @@ function handle_message(logger::ConsoleLogger, level::LogLevel, message, _module hasmaxlog = haskey(kwargs, :maxlog) ? 1 : 0 maxlog = get(kwargs, :maxlog, nothing) if maxlog isa Core.BuiltinInts - remaining = get!(logger.message_limits, id, Int(maxlog)::Int) - logger.message_limits[id] = remaining - 1 - remaining > 0 || return + @lock logger.lock begin + remaining = get!(logger.message_limits, id, Int(maxlog)::Int) + remaining == 0 && return + logger.message_limits[id] = remaining - 1 + end end # Generate a text representation of the message and all key value pairs, @@ -175,6 +181,7 @@ function handle_message(logger::ConsoleLogger, level::LogLevel, message, _module println(iob) end - write(stream, take!(buf)) + b = take!(buf) + @lock logger.lock write(stream, b) nothing end diff --git a/stdlib/Logging/test/runtests.jl b/stdlib/Logging/test/runtests.jl index 3a793c4e0bc33..65a8c4051f4e7 100644 --- a/stdlib/Logging/test/runtests.jl +++ b/stdlib/Logging/test/runtests.jl @@ -292,4 +292,47 @@ end @test occursin("LogLevel(-500): a", String(take!(buf))) end +@testset "Logging when multithreaded" begin + n = 10000 + cmd = `$(Base.julia_cmd()) -t4 --color=no $(joinpath(@__DIR__, "threads_exec.jl")) $n` + fname = tempname() + @testset "Thread safety" begin + f = open(fname, "w") + @test success(run(pipeline(cmd, stderr=f))) + close(f) + end + + @testset "No tearing in log printing" begin + # Check for print tearing by verifying that each log entry starts and ends correctly + f = open(fname, "r") + entry_start = r"┌ (Info|Warning|Error): iteration" + entry_end = r"└ " + + open_entries = 0 + total_entries = 0 + for line in eachline(fname) + starts = count(entry_start, line) + starts > 1 && error("Interleaved logs: Multiple log entries started on one line") + if starts == 1 + startswith(line, entry_start) || error("Interleaved logs: Log entry started in the middle of a line") + open_entries += 1 + total_entries += 1 + end + + ends = count(entry_end, line) + starts == 1 && ends == 1 && error("Interleaved logs: Log entry started and and another ended on one line") + ends > 1 && error("Interleaved logs: Multiple log entries ended on one line") + if ends == 1 + startswith(line, entry_end) || error("Interleaved logs: Log entry ended in the middle of a line") + open_entries -= 1 + end + # Ensure no mismatched log entries + open_entries >= 0 || error("Interleaved logs") + end + + @test open_entries == 0 # Ensure all entries closed properly + @test total_entries == n * 3 # Ensure all logs were printed (3 because @debug is hidden) + end +end + end diff --git a/stdlib/Logging/test/threads_exec.jl b/stdlib/Logging/test/threads_exec.jl new file mode 100644 index 0000000000000..497a22b1c7b22 --- /dev/null +++ b/stdlib/Logging/test/threads_exec.jl @@ -0,0 +1,13 @@ +using Logging + +function test_threads_exec(n) + Threads.@threads for i in 1:n + @debug "iteration" maxlog=1 _id=Symbol("$(i)_debug") i Threads.threadid() + @info "iteration" maxlog=1 _id=Symbol("$(i)_info") i Threads.threadid() + @warn "iteration" maxlog=1 _id=Symbol("$(i)_warn") i Threads.threadid() + @error "iteration" maxlog=1 _id=Symbol("$(i)_error") i Threads.threadid() + end +end + +n = parse(Int, ARGS[1]) +test_threads_exec(n) diff --git a/stdlib/OpenLibm_jll/Project.toml b/stdlib/OpenLibm_jll/Project.toml index c5ba7fe77b651..431528ee3f400 100644 --- a/stdlib/OpenLibm_jll/Project.toml +++ b/stdlib/OpenLibm_jll/Project.toml @@ -1,6 +1,6 @@ name = "OpenLibm_jll" uuid = "05823500-19ac-5b8b-9628-191a04bc5112" -version = "0.8.1+4" +version = "0.8.5+0" [deps] Libdl = "8f399da3-3557-5675-b5ff-fb832c97cbdb" diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 1fa2db36f0cb9..9e1380e653cd2 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.10 -PKG_SHA1 = 0b3af4592d7158d4cba1132a9cc9df15e8e51706 +PKG_SHA1 = 2525c69000ea321c80eca26a3c4f2f0b1ba11e0e 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/Profile/src/Allocs.jl b/stdlib/Profile/src/Allocs.jl index e45f4dca9607f..0b10781d39ba4 100644 --- a/stdlib/Profile/src/Allocs.jl +++ b/stdlib/Profile/src/Allocs.jl @@ -73,11 +73,11 @@ end function _prof_expr(expr, opts) quote $start(; $(esc(opts))) - try + Base.@__tryfinally( $(esc(expr)) - finally + , $stop() - end + ) end end diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index f88bc45e2dee4..2501d3b200dea 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -39,12 +39,12 @@ appended to an internal buffer of backtraces. """ macro profile(ex) return quote - try - start_timer() + start_timer() + Base.@__tryfinally( $(esc(ex)) - finally + , stop_timer() - end + ) end end diff --git a/stdlib/Profile/test/runtests.jl b/stdlib/Profile/test/runtests.jl index 2d6df81b1015d..183128205adb2 100644 --- a/stdlib/Profile/test/runtests.jl +++ b/stdlib/Profile/test/runtests.jl @@ -117,6 +117,16 @@ end @test z == 10 end +@testset "@profile no scope" begin + @profile no_scope_57858_1 = 1 + @test @isdefined no_scope_57858_1 + Profile.clear() + + Profile.Allocs.@profile no_scope_57858_2 = 1 + @test @isdefined no_scope_57858_2 + Profile.Allocs.clear() +end + @testset "setting sample count and delay in init" begin n_, delay_ = Profile.init() n_original = n_ diff --git a/stdlib/Random/src/RNGs.jl b/stdlib/Random/src/RNGs.jl index 292ae00d33628..e944aaae5b205 100644 --- a/stdlib/Random/src/RNGs.jl +++ b/stdlib/Random/src/RNGs.jl @@ -151,21 +151,26 @@ function show(io::IO, rng::MersenneTwister) end print(io, MersenneTwister, "(", seed_str, ", (") # state - adv = Integer[rng.adv_jump, rng.adv] + sep = ", " + show(io, rng.adv_jump) + print(io, sep) + show(io, rng.adv) if rng.adv_vals != -1 || rng.adv_ints != -1 - if rng.adv_vals == -1 - @assert rng.idxF == MT_CACHE_F - push!(adv, 0, 0) # "(0, 0)" is nicer on the eyes than (-1, 1002) - else - push!(adv, rng.adv_vals, rng.idxF) - end + # "(0, 0)" is nicer on the eyes than (-1, 1002) + s = rng.adv_vals != -1 + print(io, sep) + show(io, s ? rng.adv_vals : zero(rng.adv_vals)) + print(io, sep) + show(io, s ? rng.idxF : zero(rng.idxF)) end if rng.adv_ints != -1 idxI = (length(rng.ints)*16 - rng.idxI) / 8 # 8 represents one Int64 idxI = Int(idxI) # idxI should always be an integer when using public APIs - push!(adv, rng.adv_ints, idxI) + print(io, sep) + show(io, rng.adv_ints) + print(io, sep) + show(io, idxI) end - join(io, adv, ", ") print(io, "))") end diff --git a/stdlib/Sockets/src/IPAddr.jl b/stdlib/Sockets/src/IPAddr.jl index 04710e400fe87..153720aca2f1c 100644 --- a/stdlib/Sockets/src/IPAddr.jl +++ b/stdlib/Sockets/src/IPAddr.jl @@ -265,7 +265,7 @@ julia> @ip_str "2001:db8:0:0:0:0:2:1" ip"2001:db8::2:1" ``` """ -macro ip_str(str) +macro ip_str(str::String) return parse(IPAddr, str) end diff --git a/stdlib/Test/src/logging.jl b/stdlib/Test/src/logging.jl index 8df7b016a8944..bfd2b5b826f91 100644 --- a/stdlib/Test/src/logging.jl +++ b/stdlib/Test/src/logging.jl @@ -107,8 +107,8 @@ function Logging.handle_message(logger::TestLogger, level, msg, _module, if maxlog isa Core.BuiltinInts @lock logger.lock begin remaining = get!(logger.message_limits, id, Int(maxlog)::Int) + remaining == 0 && return logger.message_limits[id] = remaining - 1 - remaining > 0 || return end end end diff --git a/test/abstractarray.jl b/test/abstractarray.jl index 8d0c83897611b..e7469a7a331ed 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -835,6 +835,26 @@ include("generic_map_tests.jl") generic_map_tests(map, map!) @test_throws ArgumentError map!(-, [1]) +@testset "#30624" begin + ### unstructured + @test map!(+, ones(3), ones(3), ones(3), [1]) == [3, 1, 1] + @test map!(+, ones(3), [1], ones(3), ones(3)) == [3, 1, 1] + @test map!(+, [1], [1], [], []) == [1] + @test map!(+, [[1]], [1], [], []) == [[1]] + + # TODO: decide if input axes & lengths should be validated + # @test_throws BoundsError map!(+, ones(1), ones(2)) + # @test_throws BoundsError map!(+, ones(1), ones(2, 2)) + + @test map!(+, ones(3), view(ones(2, 3), 1:2, 2:3), ones(3)) == [2, 2, 2] + @test map!(+, ones(3), ones(2, 2), ones(3)) == [2, 2, 2] + + ### structured (all mapped arguments are <:AbstractArray equal ndims > 1) + @test map!(+, ones(4), ones(2, 2), ones(2, 2)) == [2, 2, 2, 2] + @test map!(+, ones(4), ones(2, 2), ones(1, 2)) == [2, 2, 1, 1] + # @test_throws BoundsError map!(+, ones(3), ones(2, 2), ones(2, 2)) +end + test_UInt_indexing(TestAbstractArray) test_13315(TestAbstractArray) test_checksquare() diff --git a/test/ambiguous.jl b/test/ambiguous.jl index 496da6ac37fc6..27d4655a04497 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -288,6 +288,30 @@ for f in (Ambig8.f, Ambig8.g) @test f(Int8(0)) == 4 @test_throws MethodError f(0) @test_throws MethodError f(pi) + let ambig = Ref{Int32}(0) + ms = Base._methods_by_ftype(Tuple{typeof(f), Union{Int,AbstractIrrational}}, nothing, 10, Base.get_world_counter(), false, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + @test ms isa Vector + @test length(ms) == 2 + @test ambig[] == 1 + end + let ambig = Ref{Int32}(0) + ms = Base._methods_by_ftype(Tuple{typeof(f), Union{Int,AbstractIrrational}}, nothing, -1, Base.get_world_counter(), false, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + @test ms isa Vector + @test length(ms) == 2 + @test ambig[] == 1 + end + let ambig = Ref{Int32}(0) + ms = Base._methods_by_ftype(Tuple{typeof(f), Union{Int,AbstractIrrational}}, nothing, 10, Base.get_world_counter(), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + @test ms isa Vector + @test length(ms) == 3 + @test ambig[] == 1 + end + let ambig = Ref{Int32}(0) + ms = Base._methods_by_ftype(Tuple{typeof(f), Union{Int,AbstractIrrational}}, nothing, -1, Base.get_world_counter(), true, Ref{UInt}(typemin(UInt)), Ref{UInt}(typemax(UInt)), ambig) + @test ms isa Vector + @test length(ms) == 3 + @test ambig[] == 1 + end end module Ambig9 diff --git a/test/arrayops.jl b/test/arrayops.jl index ba02847094b0e..27867080b8c21 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -1685,6 +1685,12 @@ end end end +@testset "reverse zero dims" begin + a = fill(3) + @test a == reverse(a) + @test a === reverse!(a) +end + @testset "isdiag, istril, istriu" begin # Scalar @test isdiag(3) diff --git a/test/boundscheck_exec.jl b/test/boundscheck_exec.jl index f2eb2ea630893..dddd6d30c6f12 100644 --- a/test/boundscheck_exec.jl +++ b/test/boundscheck_exec.jl @@ -239,6 +239,13 @@ if bc_opt != bc_off @test_throws BoundsError BadVector20469([1,2,3])[:] end +# Accumulate: do not set inbounds context for user-supplied functions +if bc_opt != bc_off + Base.@propagate_inbounds op58200(a, b) = (1, 2)[a] + (1, 2)[b] + @test_throws BoundsError accumulate(op58200, 1:10) + @test_throws BoundsError Base.accumulate_pairwise(op58200, 1:10) +end + # Ensure iteration over arrays is vectorizable function g27079(X) r = 0 diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index 00aae0e748bc9..21cf2e8fca5db 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -873,3 +873,9 @@ end # Core.getptls() special handling @test !occursin("call ptr @jlplt", get_llvm(Core.getptls, Tuple{})) #It should lower to a direct load of the ptls and not a ccall + + +struct Vec56937 x::NTuple{8, VecElement{Int}} end + +x56937 = Ref(Vec56937(ntuple(_->VecElement(1),8))) +@test x56937[].x[1] == VecElement{Int}(1) # shouldn't crash diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 916756eb6bfb7..777f1d5340e11 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -2675,6 +2675,26 @@ vacond(cnd, va...) = cnd ? va : 0 vacond(isa(x, Tuple{Int,Int}), x, x) end |> only == Union{Int,Tuple{Any,Any}} +let A = Core.Const(true) + B = Core.InterConditional(2, Tuple, Union{}) + C = Core.InterConditional(2, Any, Union{}) + L = ipo_lattice(Core.Compiler.NativeInterpreter()) + @test !⊑(L, A, B) + @test ⊑(L, B, A) + @test tmerge(L, A, B) == C + @test ⊑(L, A, C) +end +function tail_is_ntuple((@nospecialize t::Tuple)) + if unknown + t isa Tuple + else + tail_is_ntuple(t) + end +end +tail_is_ntuple_val((@nospecialize t::Tuple)) = Val(tail_is_ntuple(t)) +@test Base.return_types(tail_is_ntuple, (Tuple,)) |> only === Bool +@test Base.return_types(tail_is_ntuple_val, (Tuple,)) |> only === Val{true} + # https://github.com/JuliaLang/julia/issues/47435 is_closed_ex(e::InvalidStateException) = true is_closed_ex(e) = false diff --git a/test/core.jl b/test/core.jl index 1938e0801a100..26e16f20f0073 100644 --- a/test/core.jl +++ b/test/core.jl @@ -389,8 +389,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} @@ -429,6 +429,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 @@ -4125,7 +4137,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) @@ -5665,6 +5699,13 @@ let ni128 = sizeof(FP128test) ÷ sizeof(Int), @test reinterpret(UInt128, arr[2].fp) == expected end +# make sure VecElement Tuple has the C alignment and ABI for supported types +primitive type Int24 24 end +@test Base.datatype_alignment(NTuple{10,VecElement{Int16}}) == 32 +@test Base.datatype_alignment(NTuple{10,VecElement{Int24}}) == 4 +@test Base.datatype_alignment(NTuple{10,VecElement{Int64}}) == 128 +@test Base.datatype_alignment(NTuple{10,VecElement{Int128}}) == 256 + # issue #21516 struct T21516 x::Vector{Float64} @@ -8089,3 +8130,8 @@ let widen_diagonal(x::UnionAll) = Base.rewrap_unionall(Base.widen_diagonal(Base. @test Tuple === widen_diagonal(Union{Tuple{Vararg{S}}, Tuple{Vararg{T}}} where {S, T}) @test Tuple{Vararg{Val{<:Set}}} == widen_diagonal(Tuple{Vararg{T}} where T<:Val{<:Set}) end + +myfun57023a(::Type{T}) where {T} = (x = @ccall mycfun()::Ptr{T}; x) +@test only(code_lowered(myfun57023a)).has_fcall +myfun57023b(::Type{T}) where {T} = (x = @cfunction myfun57023a Ptr{T} (Ref{T},); x) +@test only(code_lowered(myfun57023b)).has_fcall diff --git a/test/int.jl b/test/int.jl index f79bc5a9781d0..73e86252fab19 100644 --- a/test/int.jl +++ b/test/int.jl @@ -358,6 +358,23 @@ end end end end + # exhaustive UInt8/Int8 tests for mixed signedness + for f in (mod, rem) + for i in -128:127 + for j in 0:255 + if iszero(i) + @test_throws DivideError f(UInt8(j), Int8(i)) + else + @test f(UInt8(j), Int8(i)) == f(j, i) + end + if iszero(j) + @test_throws DivideError f(Int8(i), UInt8(j)) + else + @test f(Int8(i), UInt8(j)) == f(i,j) + end + end + end + end end @testset "Underscores in big_str" begin diff --git a/test/intfuncs.jl b/test/intfuncs.jl index ceaac235a3da9..1cc6705812927 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -278,6 +278,14 @@ end end @testset "nextpow/prevpow" begin + fs = (prevpow, nextpow) + types = (Int8, BigInt, BigFloat) + for f ∈ fs, P ∈ types, R ∈ types, p ∈ 1:20, r ∈ 2:5 + q = P(p) + n = R(r) + @test f(r, p) == f(n, q) + end + @test nextpow(2, 3) == 4 @test nextpow(2, 4) == 4 @test nextpow(2, 7) == 8 @@ -291,7 +299,14 @@ end @test prevpow(10, 101.0) === 100 @test prevpow(10.0, 101) === 100.0 @test_throws DomainError prevpow(0, 3) - @test_throws DomainError prevpow(0, 3) + @test_throws DomainError prevpow(3, 0) + + # "argument is beyond the range of type of the base" + @test_throws DomainError prevpow(Int8(3), 243) + @test_throws DomainError nextpow(Int8(3), 243) + + # "result is beyond the range of type of the base" + @test_throws OverflowError nextpow(Int8(3), 82) end @testset "ndigits/ndigits0z" begin diff --git a/test/iterators.jl b/test/iterators.jl index a60ec32bb9ac0..786dcfd950624 100644 --- a/test/iterators.jl +++ b/test/iterators.jl @@ -959,6 +959,12 @@ end @test Iterators.tail_if_any((1,)) == () end +@testset "issue #58109" begin + i = Iterators.map(identity, 3) + j = Iterators.map(sqrt, 7) + @test (@inferred Base.IteratorSize(i)) === @inferred Base.IteratorSize(eltype([i, j])) +end + @testset "IteratorSize trait for zip" begin @test Base.IteratorSize(zip()) == Base.IsInfinite() # for zip of empty tuple @test Base.IteratorSize(zip((1,2,3), repeated(0))) == Base.HasLength() # for zip of ::HasLength and ::IsInfinite diff --git a/test/llvmpasses/late-lower-gc.ll b/test/llvmpasses/late-lower-gc.ll index 36e581993c176..b8733b8ab9a56 100644 --- a/test/llvmpasses/late-lower-gc.ll +++ b/test/llvmpasses/late-lower-gc.ll @@ -3,8 +3,6 @@ ; RUN: opt -enable-new-pm=0 --opaque-pointers=0 -load libjulia-codegen%shlibext -LateLowerGCFrame -S %s | FileCheck %s -check-prefixes=CHECK,TYPED ; RUN: opt -enable-new-pm=1 --opaque-pointers=0 --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LateLowerGCFrame)' -S %s | FileCheck %s -check-prefixes=CHECK,TYPED -; RUN: opt -enable-new-pm=0 --opaque-pointers=1 -load libjulia-codegen%shlibext -LateLowerGCFrame -S %s | FileCheck %s --check-prefixes=CHECK,OPAQUE -; RUN: opt -enable-new-pm=1 --opaque-pointers=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LateLowerGCFrame)' -S %s | FileCheck %s --check-prefixes=CHECK,OPAQUE @tag = external addrspace(10) global {}, align 16 @@ -127,6 +125,21 @@ top: ret void } +; Confirm that `invariant.load` on other loads survive +define void @gc_keep_invariant(float addrspace(1)* %0) { +top: +; CHECK-LABEL: @gc_keep_invariant + %pgcstack = call {}*** @julia.get_pgcstack() + %1 = bitcast {}*** %pgcstack to {}** + %current_task = getelementptr inbounds {}*, {}** %1, i64 -12 +; TYPED: %current_task = getelementptr inbounds {}*, {}** %1, i64 -12 +; OPAQUE: %current_task = getelementptr inbounds ptr, ptr %1, i64 -12 + %2 = load float, float addrspace(1)* %0, align 4, !invariant.load !1 +; TYPED-NEXT: %2 = load float, float addrspace(1)* %0, align 4, !invariant.load +; OPAQUE-NEXT: %2 = load float, ptr addrspace(1) %0, align 4, !invariant.load + ret void +} + define i32 @callee_root({} addrspace(10)* %v0, {} addrspace(10)* %v1) { top: ; CHECK-LABEL: @callee_root @@ -193,7 +206,7 @@ define void @decayar([2 x {} addrspace(10)* addrspace(11)*] %ar) { %l0 = load {} addrspace(10)*, {} addrspace(10)* addrspace(11)* %e0 %e1 = extractvalue [2 x {} addrspace(10)* addrspace(11)*] %ar, 1 %l1 = load {} addrspace(10)*, {} addrspace(10)* addrspace(11)* %e1 - %r = call i32 @callee_root({} addrspace(10)* %l0, {} addrspace(10)* %l1) + %r = call i32 @callee_root({} addrspace(10)* %l0, {} addrspace(10)* %l1) ret void } diff --git a/test/llvmpasses/remove-addrspaces.ll b/test/llvmpasses/remove-addrspaces.ll index 472f49d1ac03e..d0e77e4a5d7ae 100644 --- a/test/llvmpasses/remove-addrspaces.ll +++ b/test/llvmpasses/remove-addrspaces.ll @@ -3,9 +3,10 @@ ; RUN: opt -enable-new-pm=0 --opaque-pointers=0 -load libjulia-codegen%shlibext -RemoveJuliaAddrspaces -S %s | FileCheck %s --check-prefixes=CHECK,TYPED ; RUN: opt -enable-new-pm=1 --opaque-pointers=0 --load-pass-plugin=libjulia-codegen%shlibext -passes='RemoveJuliaAddrspaces' -S %s | FileCheck %s --check-prefixes=CHECK,TYPED -; RUN: opt -enable-new-pm=0 --opaque-pointers=1 -load libjulia-codegen%shlibext -RemoveJuliaAddrspaces -S %s | FileCheck %s --check-prefixes=CHECK,OPAQUE -; RUN: opt -enable-new-pm=1 --opaque-pointers=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='RemoveJuliaAddrspaces' -S %s | FileCheck %s --check-prefixes=CHECK,OPAQUE +; COM: check that the addrspace of the global itself is removed +; OPAQUE: @ejl_enz_runtime_exc = external global {} +@ejl_enz_runtime_exc = external addrspace(10) global {} ; COM: check that package image fptrs work @pjlsys_BoundsError_32 = internal global {} addrspace(10)* ({}***, {} addrspace(10)*, [1 x i64] addrspace(11)*)* null @@ -123,6 +124,12 @@ define void @byval_type([1 x {} addrspace(10)*] addrspace(11)* byval([1 x {} add ret void } +define private fastcc void @diffejulia__mapreduce_97() { +L6: +; OPAQUE: store atomic ptr @ejl_enz_runtime_exc, ptr null unordered + store atomic {} addrspace(10)* @ejl_enz_runtime_exc, {} addrspace(10)* addrspace(10)* null unordered, align 8 + unreachable +} ; COM: check that function attributes are preserved on declarations too declare void @convergent_function() #0 diff --git a/test/math.jl b/test/math.jl index 8bc981993f5c8..bd00f3417ecd3 100644 --- a/test/math.jl +++ b/test/math.jl @@ -1455,6 +1455,14 @@ end @test 8.758520413376658e-5^70.55863059215994 == 5.052076767078296e-287 end +@testset "special function `::Real` fallback shouldn't recur without bound, issue #57789" begin + mutable struct Issue57789 <: Real end + Base.float(::Issue57789) = Issue57789() + for f ∈ (sin, sinpi, log, exp) + @test_throws MethodError f(Issue57789()) + end +end + # Test that sqrt behaves correctly and doesn't exhibit fp80 double rounding. # This happened on old glibc versions. # Test case from https://sourceware.org/bugzilla/show_bug.cgi?id=14032. diff --git a/test/precompile.jl b/test/precompile.jl index cd5bdc5a02492..ac155bb5d7af2 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -1854,6 +1854,29 @@ precompile_test_harness("Issue #50538") do load_path end +precompile_test_harness("Pre-compile Core methods") do load_path + # Core methods should support pre-compilation as external CI's like anything else + # https://github.com/JuliaLang/julia/issues/58497 + write(joinpath(load_path, "CorePrecompilation.jl"), + """ + module CorePrecompilation + struct Foo end + precompile(Tuple{Type{Vector{Foo}}, UndefInitializer, Tuple{Int}}) + end + """) + ji, ofile = Base.compilecache(Base.PkgId("CorePrecompilation")) + @eval using CorePrecompilation + invokelatest() do + let tt = Tuple{Type{Vector{CorePrecompilation.Foo}}, UndefInitializer, Tuple{Int}}, + match = first(Base._methods_by_ftype(tt, -1, Base.get_world_counter())), + mi = Core.Compiler.specialize_method(match) + @test isdefined(mi, :cache) + @test mi.cache.max_world === typemax(UInt) + @test mi.cache.invoke != C_NULL + end + end +end + empty!(Base.DEPOT_PATH) append!(Base.DEPOT_PATH, original_depot_path) empty!(Base.LOAD_PATH) diff --git a/test/reflection.jl b/test/reflection.jl index c13e7d88d8cfd..efd52f8df3b03 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -825,6 +825,7 @@ f(x::Int; y=3) = x + y @test hasmethod(f, Tuple{Int}) @test hasmethod(f, Tuple{Int}, ()) @test hasmethod(f, Tuple{Int}, (:y,)) +@test !hasmethod(f, Tuple{Int}, (:x,)) @test !hasmethod(f, Tuple{Int}, (:jeff,)) @test !hasmethod(f, Tuple{Int}, (:y,), world=typemin(UInt)) g(; b, c, a) = a + b + c diff --git a/test/regex.jl b/test/regex.jl index e5f1428527512..ca411b26bbacc 100644 --- a/test/regex.jl +++ b/test/regex.jl @@ -245,3 +245,11 @@ end @test match(re, "ababc").match === SubString("ababc", 3:5) end end + +@testset "#57817: Don't free Regex during exit finalizer calls" begin + # this shouldn't segfault + cmd = `$(Base.julia_cmd()) -t2 --startup-file=no -e 're = Regex(""); Threads.@spawn match(re, "", 1, UInt32(0))'` + for i in 1:10 + @test success(pipeline(cmd, stderr=stderr)) + end +end diff --git a/test/show.jl b/test/show.jl index 9b6becbf95b1d..ae4d65967ba0b 100644 --- a/test/show.jl +++ b/test/show.jl @@ -696,7 +696,7 @@ let oldout = stdout, olderr = stderr redirect_stderr(olderr) close(wrout) close(wrerr) - @test fetch(out) == "Int64 <: Signed\nTESTA\nTESTB\nΑ1Β2\"A\"\nA\n123\"C\"\n" + @test fetch(out) == "Int64 <: Signed\nTESTA\nTESTB\nΑ1Β2\"A\"\nA\n123.0000000000000000\"C\"\n" @test fetch(err) == "TESTA\nTESTB\nΑ1Β2\"A\"\n" finally redirect_stdout(oldout) @@ -1446,12 +1446,20 @@ end @test static_shown(:+) == ":+" @test static_shown(://) == "://" @test static_shown(://=) == "://=" -@test static_shown(Symbol("")) == "Symbol(\"\")" -@test static_shown(Symbol("a/b")) == "Symbol(\"a/b\")" -@test static_shown(Symbol("a-b")) == "Symbol(\"a-b\")" +@test static_shown(Symbol("")) == ":var\"\"" +@test static_shown(Symbol("a/b")) == ":var\"a/b\"" +@test static_shown(Symbol("a-b")) == ":var\"a-b\"" @test static_shown(UnionAll) == "UnionAll" - @test static_shown(QuoteNode(:x)) == ":(:x)" +@test static_shown(:!) == ":!" +@test static_shown("\"") == "\"\\\"\"" +@test static_shown("\$") == "\"\\\$\"" +@test static_shown("\\") == "\"\\\\\"" +@test static_shown("a\x80b") == "\"a\\x80b\"" +@test static_shown("a\x80\$\\b") == "\"a\\x80\\\$\\\\b\"" +@test static_shown(GlobalRef(Main, :var"a#b")) == "Main.var\"a#b\"" +@test static_shown(GlobalRef(Main, :+)) == "Main.:(+)" +@test static_shown((a = 3, ! = 4, var"a b" = 5)) == "(a=3, (!)=4, var\"a b\"=5)" # PR #38049 @test static_shown(sum) == "Base.sum" @@ -1481,8 +1489,61 @@ struct var"%X%" end # Invalid name without '#' typeof(+), var"#f#", typeof(var"#f#"), + + # Integers should round-trip (#52677) + 1, UInt(1), + Int8(1), Int16(1), Int32(1), Int64(1), + UInt8(1), UInt16(1), UInt32(1), UInt64(1), + + # Float round-trip + Float16(1), Float32(1), Float64(1), + Float16(1.5), Float32(1.5), Float64(1.5), + Float16(0.4893243538921085), Float32(0.4893243538921085), Float64(0.4893243538921085), + # Examples that require the full 5, 9, and 17 digits of precision + Float16(0.00010014), Float32(1.00000075f-36), Float64(-1.561051336605761e-182), + floatmax(Float16), floatmax(Float32), floatmax(Float64), + floatmin(Float16), floatmin(Float32), floatmin(Float64), + Float16(0.0), 0.0f0, 0.0, + Float16(-0.0), -0.0f0, -0.0, + Inf16, Inf32, Inf, + -Inf16, -Inf32, -Inf, + nextfloat(Float16(0)), nextfloat(Float32(0)), nextfloat(Float64(0)), + NaN16, NaN32, NaN, + Float16(1e3), 1f7, 1e16, + Float16(-1e3), -1f7, -1e16, + Float16(1e4), 1f8, 1e17, + Float16(-1e4), -1f8, -1e17, + + # Pointers should round-trip + Ptr{Cvoid}(0), Ptr{Cvoid}(typemax(UInt)), Ptr{Any}(0), Ptr{Any}(typemax(UInt)), + + # :var"" escaping rules differ from strings (#58484) + :foo, + :var"bar baz", + :var"a $b", # No escaping for $ in raw string + :var"a\b", # No escaping for backslashes in middle + :var"a\\", # Backslashes must be escaped at the end + :var"a\\\\", + :var"a\"b", + :var"a\"", + :var"\\\"", + :+, :var"+-", + :(=), :(:), :(::), # Requires quoting + Symbol("a\nb"), + + Val(Float16(1.0)), Val(1f0), Val(1.0), + Val(:abc), Val(:(=)), Val(:var"a\b"), + + Val(1), Val(Int8(1)), Val(Int16(1)), Val(Int32(1)), Val(Int64(1)), Val(Int128(1)), + Val(UInt(1)), Val(UInt8(1)), Val(UInt16(1)), Val(UInt32(1)), Val(UInt64(1)), Val(UInt128(1)), + + # BROKEN + # Symbol("a\xffb"), + # User-defined primitive types + # Non-canonical NaNs + # BFloat16 ) - @test v == eval(Meta.parse(static_shown(v))) + @test v === eval(Meta.parse(static_shown(v))) end end diff --git a/test/spawn.jl b/test/spawn.jl index 1fab652199ee0..0458ea4466331 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -4,7 +4,7 @@ # Cross Platform tests for spawn. # ################################### -using Random, Sockets +using Random, Sockets, SHA using Downloads: Downloads, download valgrind_off = ccall(:jl_running_on_valgrind, Cint, ()) == 0 @@ -21,9 +21,11 @@ sleepcmd = `sleep` lscmd = `ls` havebb = false +busybox_hash_correct(file) = bytes2ex(open(SHA.sha256, file)) == "ed2f95da9555268e93c7af52feb48e148534ee518b9128f65dda9a2767b61b9e" + function _tryonce_download_from_cache(desired_url::AbstractString) cache_url = "https://cache.julialang.org/$(desired_url)" - cache_output_filename = joinpath(mktempdir(), "myfile") + cache_output_filename = joinpath(mktempdir(), "busybox" * (Sys.iswindows() ? ".exe" : "")) cache_response = Downloads.request( cache_url; output = cache_output_filename, @@ -32,9 +34,14 @@ function _tryonce_download_from_cache(desired_url::AbstractString) ) if cache_response isa Downloads.Response if Downloads.status_ok(cache_response.proto, cache_response.status) - return cache_output_filename + if busybox_hash_correct(cache_output_filename) + return cache_output_filename + else + @warn "The busybox executable downloaded from the cache has an incorrect hash" cache_output_filename bytes2hex(open(SHA.sha256, cache_output_filename)) + end end end + @warn "Could not download from cache at $cache_url, falling back to primary source at $desired_url" return Downloads.download(desired_url; timeout = 60) end @@ -46,7 +53,11 @@ function download_from_cache(desired_url::AbstractString) end if Sys.iswindows() - busybox = download_from_cache("https://frippery.org/files/busybox/busybox.exe") + # See https://frippery.org/files/busybox/ + # latest as of 2024-09-20 18:08 + busybox = download_from_cache("https://frippery.org/files/busybox/busybox-w32-FRP-5467-g9376eebd8.exe") + busybox_hash_correct(busybox) || error("The busybox executable downloaded has an incorrect hash") + havebb = try # use busybox-w32 on windows, if available success(`$busybox`) true @@ -609,7 +620,9 @@ end @test reduce(&, [`$echocmd abc`, `$echocmd def`, `$echocmd hij`]) == `$echocmd abc` & `$echocmd def` & `$echocmd hij` # readlines(::Cmd), accidentally broken in #20203 -@test sort(readlines(`$lscmd -A`)) == sort(readdir()) +let str = "foo\nbar" + @test readlines(`$echocmd $str`) == split(str) +end # issue #19864 (PR #20497) let c19864 = readchomp(pipeline(ignorestatus( diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 13f2f5197187a..e7c65909fabc9 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -872,6 +872,11 @@ end end end end + + @testset "return type infers to `Int`" begin + @test all(==(Int), Base.return_types(prevind, Tuple{AbstractString, Vararg})) + @test all(==(Int), Base.return_types(nextind, Tuple{AbstractString, Vararg})) + end end @testset "first and last" begin diff --git a/test/subtype.jl b/test/subtype.jl index 9d1ce866c7a6b..4552e0c58c1ee 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -1689,9 +1689,7 @@ CovType{T} = Union{AbstractArray{T,2}, # issue #31703 @testintersect(Pair{<:Any, Ref{Tuple{Ref{Ref{Tuple{Int}}},Ref{Float64}}}}, Pair{T, S} where S<:(Ref{A} where A<:(Tuple{C,Ref{T}} where C<:(Ref{D} where D<:(Ref{E} where E<:Tuple{FF}) where FF<:B)) where B) where T, - Pair{T, Ref{Tuple{Ref{Ref{Tuple{Int}}},Ref{Float64}}}} where T) -# TODO: should be able to get this result -# Pair{Float64, Ref{Tuple{Ref{Ref{Tuple{Int}}},Ref{Float64}}}} + Pair{Float64, Ref{Tuple{Ref{Ref{Tuple{Int}}},Ref{Float64}}}}) module I31703 using Test, LinearAlgebra @@ -1743,8 +1741,7 @@ end Tuple{Type{SA{2, L}}, Type{SA{2, L}}} where L) @testintersect(Tuple{Type{SA{2, L}}, Type{SA{2, 16}}} where L, Tuple{Type{<:SA{N, L}}, Type{<:SA{N, L}}} where {N,L}, - # TODO: this could be narrower - Tuple{Type{SA{2, L}}, Type{SA{2, 16}}} where L) + Tuple{Type{SA{2, 16}}, Type{SA{2, 16}}}) # issue #31993 @testintersect(Tuple{Type{<:AbstractVector{T}}, Int} where T, @@ -1849,9 +1846,9 @@ c32703(::Type{<:Str{C}}, str::Str{C}) where {C<:CSE} = str Tuple{Type{<:Str{C}}, Str{C}} where {C<:CSE}, Union{}) @test c32703(UTF16Str, ASCIIStr()) == 42 -@test_broken typeintersect(Tuple{Vector{Vector{Float32}},Matrix,Matrix}, - Tuple{Vector{V},Matrix{Int},Matrix{S}} where {S, V<:AbstractVector{S}}) == - Tuple{Array{Array{Float32,1},1},Array{Int,2},Array{Float32,2}} +@testintersect(Tuple{Vector{Vector{Float32}},Matrix,Matrix}, + Tuple{Vector{V},Matrix{Int},Matrix{S}} where {S, V<:AbstractVector{S}}, + Tuple{Array{Array{Float32,1},1},Array{Int,2},Array{Float32,2}}) @testintersect(Tuple{Pair{Int, DataType}, Any}, Tuple{Pair{A, B} where B<:Type, Int} where A, @@ -2088,8 +2085,7 @@ let A = Tuple{Any, Type{Ref{_A}} where _A}, I = typeintersect(A, B) @test I != Union{} @test Tuple{Type{Ref{Integer}}, Type{Ref{Integer}}} <: I - # TODO: this intersection result seems too wide (I == B) ? - @test_broken !<:(Tuple{Type{Int}, Type{Int}}, I) + @test !<:(Tuple{Type{Int}, Type{Int}}, I) end @testintersect(Tuple{Type{T}, T} where T<:(Tuple{Vararg{_A, _B}} where _B where _A), @@ -2447,6 +2443,11 @@ end abstract type P47654{A} end @test Wrapper47654{P47654, Vector{Union{P47654,Nothing}}} <: Wrapper47654 +#issue 41561 +@testintersect(Tuple{Vector{VT}, Vector{VT}} where {N1, VT<:AbstractVector{N1}}, + Tuple{Vector{VN} where {N, VN<:AbstractVector{N}}, Vector{Vector{Float64}}}, + Tuple{Vector{Vector{Float64}}, Vector{Vector{Float64}}}) + @testset "known subtype/intersect issue" begin #issue 45874 let S = Pair{Val{P}, AbstractVector{<:Union{P,<:AbstractMatrix{P}}}} where P, @@ -2454,9 +2455,6 @@ abstract type P47654{A} end @test S <: T end - #issue 41561 - @test_broken typeintersect(Tuple{Vector{VT}, Vector{VT}} where {N1, VT<:AbstractVector{N1}}, - Tuple{Vector{VN} where {N, VN<:AbstractVector{N}}, Vector{Vector{Float64}}}) !== Union{} #issue 40865 @test Tuple{Set{Ref{Int}}, Set{Ref{Int}}} <: Tuple{Set{KV}, Set{K}} where {K,KV<:Union{K,Ref{K}}} @test Tuple{Set{Val{Int}}, Set{Val{Int}}} <: Tuple{Set{KV}, Set{K}} where {K,KV<:Union{K,Val{K}}} @@ -2695,3 +2693,45 @@ end Val{Tuple{T,R,S}} where {T,R<:Vector{T},S<:Vector{R}}, Val{Tuple{Int, Vector{Int}, T}} where T<:Vector{Vector{Int}}, ) + +#issue 57429 +@testintersect( + Pair{<:Any, <:Tuple{Int}}, + Pair{N, S} where {N, NTuple{N,Int}<:S<:NTuple{M,Int} where {M}}, + !Union{} +) +@testintersect( + Pair{N, T} where {N,NTuple{N,Int}<:T<:NTuple{N,Int}}, + Pair{N, T} where {N,NTuple{N,Int}<:T<:Tuple{Int,Vararg{Int}}}, + !Union{} +) + +#issue 57852 +@testintersect( + Tuple{Type{T}, Type{<:F}, Type{<:F}} where {T, F<:Union{String, T}}, + Tuple{Type{Complex{T}} where T, Type{Complex{T}} where T, Type{String}}, + Tuple{Type{Complex{T}}, Type{Complex{T}}, Type{String}} where T +) +@testintersect( + Tuple{Type{T}, Type{<:Union{F, Nothing}}, Type{<:Union{F, Nothing}}} where {T, F<:Union{String, T}}, + Tuple{Type{Complex{T}} where T, Type{Complex{T}} where T, Type{String}}, + Tuple{Type{Complex{T}}, Type{Complex{T}}, Type{String}} where T +) + +#issue 58129 +for k in 1:500 + @eval struct $(Symbol(:T58129, k)){T} end +end +let Tvar = TypeVar(:Tvar) + V = UnionAll(Tvar, Union{(@eval($(Symbol(:T58129, k)){$Tvar}) for k in 1:500)...}) + @test Set{<:V} <: AbstractSet{<:V} +end +let Tvar1 = TypeVar(:Tvar1), Tvar2 = TypeVar(:Tvar2) + V1 = UnionAll(Tvar1, Union{(@eval($(Symbol(:T58129, k)){$Tvar1}) for k in 1:100)...}) + V2 = UnionAll(Tvar2, Union{(@eval($(Symbol(:T58129, k)){$Tvar2}) for k in 1:100)...}) + @test Set{<:V2} <: AbstractSet{<:V1} +end + +#issue 58115 +@test Tuple{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{ Union{Tuple{}, Tuple{Tuple{}}}}}}}}}}}}} , Tuple{}} <: + Tuple{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Tuple{Vararg{Union{Tuple{}, Tuple{Tuple{}}}}}}}}}}}}}}}, Tuple{}}