diff --git a/Make.inc b/Make.inc index 2a23bd3d37b51..e0bc8ba224a6d 100644 --- a/Make.inc +++ b/Make.inc @@ -1393,7 +1393,7 @@ ifeq ($(OS), WINNT) HAVE_SSP := 1 OSLIBS += -Wl,--export-all-symbols -Wl,--version-script=$(BUILDROOT)/src/julia.expmap \ $(NO_WHOLE_ARCHIVE) -lpsapi -lkernel32 -lws2_32 -liphlpapi -lwinmm -ldbghelp -luserenv -lsecur32 -latomic -lole32 -JLDFLAGS += -Wl,--stack,8388608 +JLDFLAGS += -Wl,--stack,8388608 --disable-auto-import --disable-runtime-pseudo-reloc ifeq ($(ARCH),i686) JLDFLAGS += -Wl,--large-address-aware endif diff --git a/Makefile b/Makefile index 2d4116147693d..6fbbe194aa615 100644 --- a/Makefile +++ b/Makefile @@ -657,7 +657,7 @@ win-extras: ifeq ($(USE_SYSTEM_LLVM), 1) LLVM_SIZE := llvm-size$(EXE) else -LLVM_SIZE := $(build_depsbindir)/llvm-size$(EXE) +LLVM_SIZE := PATH=$(build_bindir):$$PATH; $(build_depsbindir)/llvm-size$(EXE) endif build-stats: ifeq ($(USE_BINARYBUILDER_LLVM),1) diff --git a/NEWS.md b/NEWS.md index 1279f46df609e..11424d5df1381 100644 --- a/NEWS.md +++ b/NEWS.md @@ -55,12 +55,13 @@ Command-line option changes difference between defining a `main` function and executing the code directly at the end of the script ([#50974]). * The `--compiled-modules` and `--pkgimages` flags can now be set to `existing`, which will cause Julia to consider loading existing cache files, but not to create new ones ([#50586], [#52573]). +* The `--project` argument now accepts `@script` to give a path to a directory with a Project.toml relative to the passed script file. `--project=@script/foo` for the `foo` subdirectory. If no path is given after (i.e. `--project=@script`) then (like `--project=@.`) the directory and its parents are searched for a Project.toml ([#50864] and [#53352]) Multi-threading changes ----------------------- * `Threads.@threads` now supports the `:greedy` scheduler, intended for non-uniform workloads ([#52096]). -* A new exported struct `Lockable{T, L<:AbstractLock}` makes it easy to bundle a resource and its lock together ([#52898]). +* A new public (but unexported) struct `Base.Lockable{T, L<:AbstractLock}` makes it easy to bundle a resource and its lock together ([#52898]). New library functions --------------------- @@ -87,6 +88,7 @@ New library functions * `Sys.username()` can be used to return the current user's username ([#51897]). * `Sys.isreadable(), Sys.iswritable()` can be used to check if the current user has access permissions that permit reading and writing, respectively. ([#53320]). +* `wrap(Array, m::Union{MemoryRef{T}, Memory{T}}, dims)` is the safe counterpart to `unsafe_wrap` ([#52049]). * `GC.logging_enabled()` can be used to test whether GC logging has been enabled via `GC.enable_logging` ([#51647]). * `IdSet` is now exported from Base and considered public ([#53262]). * `@time` now reports a count of any lock conflicts where a `ReentrantLock` had to wait, plus a new macro diff --git a/base/Enums.jl b/base/Enums.jl index 45a1b66753484..6e9efd8ccde38 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -90,7 +90,7 @@ end # give Enum types scalar behavior in broadcasting Base.broadcastable(x::Enum) = Ref(x) -@noinline enum_argument_error(typename, x) = throw(ArgumentError(string("invalid value for Enum $(typename): $x"))) +@noinline enum_argument_error(typename, x) = throw(ArgumentError(LazyString("invalid value for Enum ", typename, ": ", x))) """ @enum EnumName[::BaseType] value1[=x] value2[=y] @@ -143,7 +143,7 @@ julia> Symbol(apple) """ macro enum(T::Union{Symbol,Expr}, syms...) if isempty(syms) - throw(ArgumentError("no arguments given for Enum $T")) + throw(ArgumentError(LazyString("no arguments given for Enum ", T))) end basetype = Int32 typename = T @@ -151,10 +151,11 @@ macro enum(T::Union{Symbol,Expr}, syms...) typename = T.args[1] basetype = Core.eval(__module__, T.args[2]) if !isa(basetype, DataType) || !(basetype <: Integer) || !isbitstype(basetype) - throw(ArgumentError("invalid base type for Enum $typename, $T=::$basetype; base type must be an integer primitive type")) + throw(ArgumentError( + LazyString("invalid base type for Enum ", typename, ", ", T, "=::", basetype, "; base type must be an integer primitive type"))) end elseif !isa(T, Symbol) - throw(ArgumentError("invalid type expression for enum $T")) + throw(ArgumentError(LazyString("invalid type expression for enum ", T))) end values = Vector{basetype}() seen = Set{Symbol}() @@ -169,32 +170,32 @@ macro enum(T::Union{Symbol,Expr}, syms...) s isa LineNumberNode && continue if isa(s, Symbol) if i == typemin(basetype) && !isempty(values) - throw(ArgumentError("overflow in value \"$s\" of Enum $typename")) + throw(ArgumentError(LazyString("overflow in value \"", s, "\" of Enum ", typename))) end elseif isa(s, Expr) && (s.head === :(=) || s.head === :kw) && length(s.args) == 2 && isa(s.args[1], Symbol) i = Core.eval(__module__, s.args[2]) # allow exprs, e.g. uint128"1" if !isa(i, Integer) - throw(ArgumentError("invalid value for Enum $typename, $s; values must be integers")) + throw(ArgumentError(LazyString("invalid value for Enum ", typename, ", ", s, "; values must be integers"))) end i = convert(basetype, i) s = s.args[1] hasexpr = true else - throw(ArgumentError(string("invalid argument for Enum ", typename, ": ", s))) + throw(ArgumentError(LazyString("invalid argument for Enum ", typename, ": ", s))) end s = s::Symbol if !Base.isidentifier(s) - throw(ArgumentError("invalid name for Enum $typename; \"$s\" is not a valid identifier")) + throw(ArgumentError(LazyString("invalid name for Enum ", typename, "; \"", s, "\" is not a valid identifier"))) end if hasexpr && haskey(namemap, i) - throw(ArgumentError("both $s and $(namemap[i]) have value $i in Enum $typename; values must be unique")) + throw(ArgumentError(LazyString("both ", s, " and ", namemap[i], " have value ", i, " in Enum ", typename, "; values must be unique"))) end namemap[i] = s push!(values, i) if s in seen - throw(ArgumentError("name \"$s\" in Enum $typename is not unique")) + throw(ArgumentError(LazyString("name \"", s, "\" in Enum ", typename, " is not unique"))) end push!(seen, s) if length(values) == 1 diff --git a/base/Makefile b/base/Makefile index 6abf9b285ce34..c3899d5a90d47 100644 --- a/base/Makefile +++ b/base/Makefile @@ -249,12 +249,12 @@ endif ifneq (,$(LIBGFORTRAN_VERSION)) $(eval $(call symlink_system_library,CSL,libgfortran,$(LIBGFORTRAN_VERSION))) endif -$(eval $(call symlink_system_library,CSL,libquadmath,0)) $(eval $(call symlink_system_library,CSL,libstdc++,6)) -# We allow libssp, libatomic and libgomp to fail as they are not available on all systems +# We allow libssp, libatomic, libgomp and libquadmath to fail as they are not available on all systems $(eval $(call symlink_system_library,CSL,libssp,0,ALLOW_FAILURE)) $(eval $(call symlink_system_library,CSL,libatomic,1,ALLOW_FAILURE)) $(eval $(call symlink_system_library,CSL,libgomp,1,ALLOW_FAILURE)) +$(eval $(call symlink_system_library,CSL,libquadmath,0,ALLOW_FAILURE)) $(eval $(call symlink_system_library,PCRE,libpcre2-8)) $(eval $(call symlink_system_library,DSFMT,libdSFMT)) $(eval $(call symlink_system_library,LIBBLASTRAMPOLINE,libblastrampoline)) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index 56390de1eb5d3..058297b3c3152 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -748,7 +748,7 @@ julia> checkindex(Bool, 1:20, 21) false ``` """ -checkindex(::Type{Bool}, inds, i) = throw(ArgumentError("unable to check bounds for indices of type $(typeof(i))")) +checkindex(::Type{Bool}, inds, i) = throw(ArgumentError(LazyString("unable to check bounds for indices of type ", typeof(i)))) checkindex(::Type{Bool}, inds::AbstractUnitRange, i::Real) = (first(inds) <= i) & (i <= last(inds)) checkindex(::Type{Bool}, inds::IdentityUnitRange, i::Real) = checkindex(Bool, inds.indices, i) checkindex(::Type{Bool}, inds::OneTo{T}, i::T) where {T<:BitInteger} = unsigned(i - one(i)) < unsigned(last(inds)) @@ -1519,12 +1519,14 @@ much more common case where aliasing does not occur. By default, unaliascopy(A::Array) = copy(A) unaliascopy(A::AbstractArray)::typeof(A) = (@noinline; _unaliascopy(A, copy(A))) _unaliascopy(A::T, C::T) where {T} = C -_unaliascopy(A, C) = throw(ArgumentError(""" - an array of type `$(typename(typeof(A)).wrapper)` shares memory with another argument - and must make a preventative copy of itself in order to maintain consistent semantics, - but `copy(::$(typeof(A)))` returns a new array of type `$(typeof(C))`. - To fix, implement: - `Base.unaliascopy(A::$(typename(typeof(A)).wrapper))::typeof(A)`""")) +function _unaliascopy(A, C) + Aw = typename(typeof(A)).wrapper + throw(ArgumentError(LazyString("an array of type `", Aw, "` shares memory with another argument ", + "and must make a preventative copy of itself in order to maintain consistent semantics, ", + "but `copy(::", typeof(A), ")` returns a new array of type `", typeof(C), "`.\n", + """To fix, implement: + `Base.unaliascopy(A::""", Aw, ")::typeof(A)`"))) +end unaliascopy(A) = A """ diff --git a/base/array.jl b/base/array.jl index 19d79479bef39..190b37cc12cf6 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1312,8 +1312,7 @@ end append!(a::AbstractVector, iter) = _append!(a, IteratorSize(iter), iter) push!(a::AbstractVector, iter...) = append!(a, iter) - -append!(a::AbstractVector, iter...) = foldl(append!, iter, init=a) +append!(a::AbstractVector, iter...) = (for v in iter; append!(a, v); end; return a) function _append!(a::AbstractVector, ::Union{HasLength,HasShape}, iter) n = Int(length(iter))::Int @@ -1372,10 +1371,9 @@ function prepend!(a::Vector{T}, items::Union{AbstractVector{<:T},Tuple}) where T return a end -prepend!(a::Vector, iter) = _prepend!(a, IteratorSize(iter), iter) -pushfirst!(a::Vector, iter...) = prepend!(a, iter) - -prepend!(a::AbstractVector, iter...) = foldr((v, a) -> prepend!(a, v), iter, init=a) +prepend!(a::AbstractVector, iter) = _prepend!(a, IteratorSize(iter), iter) +pushfirst!(a::AbstractVector, iter...) = prepend!(a, iter) +prepend!(a::AbstractVector, iter...) = (for v = reverse(iter); prepend!(a, v); end; return a) function _prepend!(a::Vector, ::Union{HasLength,HasShape}, iter) @_terminates_locally_meta @@ -3062,3 +3060,54 @@ intersect(r::AbstractRange, v::AbstractVector) = intersect(v, r) _getindex(v, i) end end + +""" + wrap(Array, m::Union{Memory{T}, MemoryRef{T}}, dims) + +Create an array of size `dims` using `m` as the underlying memory. This can be thought of as a safe version +of [`unsafe_wrap`](@ref) utilizing `Memory` or `MemoryRef` instead of raw pointers. +""" +function wrap end + +# validity checking for _wrap calls, separate from allocation of Array so that it can be more likely to inline into the caller +function _wrap(ref::MemoryRef{T}, dims::NTuple{N, Int}) where {T, N} + mem = ref.mem + mem_len = length(mem) + 1 - memoryrefoffset(ref) + len = Core.checked_dims(dims...) + @boundscheck mem_len >= len || invalid_wrap_err(mem_len, dims, len) + if N != 1 && !(ref === GenericMemoryRef(mem) && len === mem_len) + mem = ccall(:jl_genericmemory_slice, Memory{T}, (Any, Ptr{Cvoid}, Int), mem, ref.ptr_or_offset, len) + ref = MemoryRef(mem) + end + return ref +end + +@noinline invalid_wrap_err(len, dims, proddims) = throw(DimensionMismatch( + "Attempted to wrap a MemoryRef of length $len with an Array of size dims=$dims, which is invalid because prod(dims) = $proddims > $len, so that the array would have more elements than the underlying memory can store.")) + +@eval @propagate_inbounds function wrap(::Type{Array}, m::MemoryRef{T}, dims::NTuple{N, Integer}) where {T, N} + dims = convert(Dims, dims) + ref = _wrap(m, dims) + $(Expr(:new, :(Array{T, N}), :ref, :dims)) +end + +@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}, dims::NTuple{N, Integer}) where {T, N} + dims = convert(Dims, dims) + ref = _wrap(MemoryRef(m), dims) + $(Expr(:new, :(Array{T, N}), :ref, :dims)) +end +@eval @propagate_inbounds function wrap(::Type{Array}, m::MemoryRef{T}, l::Integer) where {T} + dims = (Int(l),) + ref = _wrap(m, dims) + $(Expr(:new, :(Array{T, 1}), :ref, :dims)) +end +@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}, l::Integer) where {T} + dims = (Int(l),) + ref = _wrap(MemoryRef(m), (l,)) + $(Expr(:new, :(Array{T, 1}), :ref, :dims)) +end +@eval @propagate_inbounds function wrap(::Type{Array}, m::Memory{T}) where {T} + ref = MemoryRef(m) + dims = (length(m),) + $(Expr(:new, :(Array{T, 1}), :ref, :dims)) +end diff --git a/base/boot.jl b/base/boot.jl index bfee3c17336bc..e8156b325a843 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -282,7 +282,8 @@ macro _foldable_meta() #=:notaskstate=#true, #=:inaccessiblememonly=#true, #=:noub=#true, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end macro inline() Expr(:meta, :inline) end diff --git a/base/c.jl b/base/c.jl index eb552d3507662..c1b34579e0a0b 100644 --- a/base/c.jl +++ b/base/c.jl @@ -344,7 +344,7 @@ function ccall_macro_lower(convention, func, rettype, types, args, nreq) check = quote if !isa(func, Ptr{Cvoid}) name = $name - throw(ArgumentError("interpolated function `$name` was not a Ptr{Cvoid}, but $(typeof(func))")) + throw(ArgumentError(LazyString("interpolated function `", name, "` was not a Ptr{Cvoid}, but ", typeof(func)))) end end push!(statements, check) diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index b13df848ce605..ba8a352c1b528 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -491,7 +491,7 @@ function add_call_backedges!(interp::AbstractInterpreter, @nospecialize(rettype) # ignore the `:nonoverlayed` property if `interp` doesn't use overlayed method table # since it will never be tainted anyway if !isoverlayed(method_table(interp)) - all_effects = Effects(all_effects; nonoverlayed=false) + all_effects = Effects(all_effects; nonoverlayed=ALWAYS_FALSE) end all_effects === Effects() && return nothing end @@ -889,7 +889,15 @@ function concrete_eval_eligible(interp::AbstractInterpreter, mi = result.edge if mi !== nothing && is_foldable(effects) if f !== nothing && is_all_const_arg(arginfo, #=start=#2) - if is_nonoverlayed(interp) || is_nonoverlayed(effects) + if (is_nonoverlayed(interp) || is_nonoverlayed(effects) || + # Even if overlay methods are involved, when `:consistent_overlay` is + # explicitly applied, we can still perform concrete evaluation using the + # original methods for executing them. + # While there's a chance that the non-overlayed counterparts may raise + # non-egal exceptions, it will not impact the compilation validity, since: + # - the results of the concrete evaluation will not be inlined + # - the exception types from the concrete evaluation will not be propagated + is_consistent_overlay(effects)) return :concrete_eval end # disable concrete-evaluation if this function call is tainted by some overlayed @@ -2770,7 +2778,7 @@ function override_effects(effects::Effects, override::EffectsOverride) notaskstate = override.notaskstate ? true : effects.notaskstate, inaccessiblememonly = override.inaccessiblememonly ? ALWAYS_TRUE : effects.inaccessiblememonly, noub = override.noub ? ALWAYS_TRUE : - override.noub_if_noinbounds && effects.noub !== ALWAYS_TRUE ? NOUB_IF_NOINBOUNDS : + (override.noub_if_noinbounds && effects.noub !== ALWAYS_TRUE) ? NOUB_IF_NOINBOUNDS : effects.noub) end diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index 8b9c26be2ec81..4e2fe3da3496f 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -47,10 +47,11 @@ struct EffectsOverride inaccessiblememonly::Bool noub::Bool noub_if_noinbounds::Bool + consistent_overlay::Bool end function EffectsOverride( override::EffectsOverride = - EffectsOverride(false, false, false, false, false, false, false, false, false); + EffectsOverride(false, false, false, false, false, false, false, false, false, false); consistent::Bool = override.consistent, effect_free::Bool = override.effect_free, nothrow::Bool = override.nothrow, @@ -59,7 +60,8 @@ function EffectsOverride( notaskstate::Bool = override.notaskstate, inaccessiblememonly::Bool = override.inaccessiblememonly, noub::Bool = override.noub, - noub_if_noinbounds::Bool = override.noub_if_noinbounds) + noub_if_noinbounds::Bool = override.noub_if_noinbounds, + consistent_overlay::Bool = override.consistent_overlay) return EffectsOverride( consistent, effect_free, @@ -69,9 +71,10 @@ function EffectsOverride( notaskstate, inaccessiblememonly, noub, - noub_if_noinbounds) + noub_if_noinbounds, + consistent_overlay) end -const NUM_EFFECTS_OVERRIDES = 9 # sync with julia.h +const NUM_EFFECTS_OVERRIDES = 10 # sync with julia.h # essential files and libraries include("essentials.jl") diff --git a/base/compiler/effects.jl b/base/compiler/effects.jl index a3d30baef9efa..0375b8dba922c 100644 --- a/base/compiler/effects.jl +++ b/base/compiler/effects.jl @@ -43,16 +43,21 @@ following meanings: except that it may access or modify mutable memory pointed to by its call arguments. This may later be refined to `ALWAYS_TRUE` in a case when call arguments are known to be immutable. This state corresponds to LLVM's `inaccessiblemem_or_argmemonly` function attribute. -- `noub::UInt8`: indicates that the method will not execute any undefined behavior (for any input). - Note that undefined behavior may technically cause the method to violate any other effect - assertions (such as `:consistent` or `:effect_free`) as well, but we do not model this, - and they assume the absence of undefined behavior. - * `ALWAYS_TRUE`: this method is guaranteed to not execute any undefined behavior. +- `noub::UInt8`: + * `ALWAYS_TRUE`: this method is guaranteed to not execute any undefined behavior (for any input). * `ALWAYS_FALSE`: this method may execute undefined behavior. * `NOUB_IF_NOINBOUNDS`: this method is guaranteed to not execute any undefined behavior if the caller does not set nor propagate the `@inbounds` context. -- `nonoverlayed::Bool`: indicates that any methods that may be called within this method - are not defined in an [overlayed method table](@ref OverlayMethodTable). + Note that undefined behavior may technically cause the method to violate any other effect + assertions (such as `:consistent` or `:effect_free`) as well, but we do not model this, + and they assume the absence of undefined behavior. +- `nonoverlayed::UInt8`: + * `ALWAYS_TRUE`: this method is guaranteed to not invoke any methods that defined in an + [overlayed method table](@ref OverlayMethodTable). + * `CONSISTENT_OVERLAY`: this method may invoke overlayed methods, but all such overlayed + methods are `:consistent` with their non-overlayed original counterparts + (see [`Base.@assume_effects`](@ref) for the exact definition of `:consistenct`-cy). + * `ALWAYS_FALSE`: this method may invoke overlayed methods. Note that the representations above are just internal implementation details and thus likely to change in the future. See [`Base.@assume_effects`](@ref) for more detailed explanation @@ -94,8 +99,10 @@ The output represents the state of different effect properties in the following - `+u` (green): `true` - `-u` (red): `false` - `?u` (yellow): `NOUB_IF_NOINBOUNDS` - -Additionally, if the `nonoverlayed` property is false, a red prime symbol (′) is displayed after the tuple. +8. `:nonoverlayed` (`o`): + - `+o` (green): `ALWAYS_TRUE` + - `-o` (red): `ALWAYS_FALSE` + - `?o` (yellow): `CONSISTENT_OVERLAY` """ struct Effects consistent::UInt8 @@ -105,7 +112,7 @@ struct Effects notaskstate::Bool inaccessiblememonly::UInt8 noub::UInt8 - nonoverlayed::Bool + nonoverlayed::UInt8 function Effects( consistent::UInt8, effect_free::UInt8, @@ -114,7 +121,7 @@ struct Effects notaskstate::Bool, inaccessiblememonly::UInt8, noub::UInt8, - nonoverlayed::Bool) + nonoverlayed::UInt8) return new( consistent, effect_free, @@ -150,10 +157,13 @@ const INACCESSIBLEMEM_OR_ARGMEMONLY = 0x01 << 1 # :noub bits const NOUB_IF_NOINBOUNDS = 0x01 << 1 -const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, ALWAYS_TRUE, true) -const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, ALWAYS_TRUE, true) -const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, true) # unknown mostly, but it's not overlayed at least (e.g. it's not a call) -const _EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, false) # unknown really +# :nonoverlayed bits +const CONSISTENT_OVERLAY = 0x01 << 1 + +const EFFECTS_TOTAL = Effects(ALWAYS_TRUE, ALWAYS_TRUE, true, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE) +const EFFECTS_THROWS = Effects(ALWAYS_TRUE, ALWAYS_TRUE, false, true, true, ALWAYS_TRUE, ALWAYS_TRUE, ALWAYS_TRUE) +const EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_TRUE) # unknown mostly, but it's not overlayed at least (e.g. it's not a call) +const _EFFECTS_UNKNOWN = Effects(ALWAYS_FALSE, ALWAYS_FALSE, false, false, false, ALWAYS_FALSE, ALWAYS_FALSE, ALWAYS_FALSE) # unknown really function Effects(effects::Effects = _EFFECTS_UNKNOWN; consistent::UInt8 = effects.consistent, @@ -163,7 +173,7 @@ function Effects(effects::Effects = _EFFECTS_UNKNOWN; notaskstate::Bool = effects.notaskstate, inaccessiblememonly::UInt8 = effects.inaccessiblememonly, noub::UInt8 = effects.noub, - nonoverlayed::Bool = effects.nonoverlayed) + nonoverlayed::UInt8 = effects.nonoverlayed) return Effects( consistent, effect_free, @@ -229,8 +239,11 @@ function is_better_effects(new::Effects, old::Effects) elseif new.noub != old.noub return false end - if new.nonoverlayed - any_improved |= !old.nonoverlayed + if new.nonoverlayed == ALWAYS_TRUE + any_improved |= old.nonoverlayed != ALWAYS_TRUE + elseif new.nonoverlayed == CONSISTENT_OVERLAY + old.nonoverlayed == ALWAYS_TRUE && return false + any_improved |= old.nonoverlayed != CONSISTENT_OVERLAY elseif new.nonoverlayed != old.nonoverlayed return false end @@ -265,7 +278,7 @@ is_notaskstate(effects::Effects) = effects.notaskstate is_inaccessiblememonly(effects::Effects) = effects.inaccessiblememonly === ALWAYS_TRUE is_noub(effects::Effects) = effects.noub === ALWAYS_TRUE is_noub_if_noinbounds(effects::Effects) = effects.noub === NOUB_IF_NOINBOUNDS -is_nonoverlayed(effects::Effects) = effects.nonoverlayed +is_nonoverlayed(effects::Effects) = effects.nonoverlayed === ALWAYS_TRUE # implies `is_notaskstate` & `is_inaccessiblememonly`, but not explicitly checked here is_foldable(effects::Effects) = @@ -295,6 +308,8 @@ is_effect_free_if_inaccessiblememonly(effects::Effects) = !iszero(effects.effect is_inaccessiblemem_or_argmemonly(effects::Effects) = effects.inaccessiblememonly === INACCESSIBLEMEM_OR_ARGMEMONLY +is_consistent_overlay(effects::Effects) = effects.nonoverlayed === CONSISTENT_OVERLAY + function encode_effects(e::Effects) return ((e.consistent % UInt32) << 0) | ((e.effect_free % UInt32) << 3) | @@ -315,7 +330,7 @@ function decode_effects(e::UInt32) _Bool((e >> 7) & 0x01), UInt8((e >> 8) & 0x03), UInt8((e >> 10) & 0x03), - _Bool((e >> 12) & 0x01)) + UInt8((e >> 12) & 0x03)) end function encode_effects_override(eo::EffectsOverride) @@ -329,6 +344,7 @@ function encode_effects_override(eo::EffectsOverride) eo.inaccessiblememonly && (e |= (0x0001 << 6)) eo.noub && (e |= (0x0001 << 7)) eo.noub_if_noinbounds && (e |= (0x0001 << 8)) + eo.consistent_overlay && (e |= (0x0001 << 9)) return e end @@ -342,7 +358,8 @@ function decode_effects_override(e::UInt16) !iszero(e & (0x0001 << 5)), !iszero(e & (0x0001 << 6)), !iszero(e & (0x0001 << 7)), - !iszero(e & (0x0001 << 8))) + !iszero(e & (0x0001 << 8)), + !iszero(e & (0x0001 << 9))) end decode_statement_effects_override(ssaflag::UInt32) = diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index f08fbac8af360..9dd676685cfda 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -329,7 +329,10 @@ mutable struct InferenceState end if def isa Method - ipo_effects = Effects(ipo_effects; nonoverlayed=is_nonoverlayed(def)) + nonoverlayed = is_nonoverlayed(def) ? ALWAYS_TRUE : + is_effect_overridden(def, :consistent_overlay) ? CONSISTENT_OVERLAY : + ALWAYS_FALSE + ipo_effects = Effects(ipo_effects; nonoverlayed) end restrict_abstract_call_sites = isa(def, Module) diff --git a/base/compiler/ssair/domtree.jl b/base/compiler/ssair/domtree.jl index bbae3a4d8c0fc..5c78809675a56 100644 --- a/base/compiler/ssair/domtree.jl +++ b/base/compiler/ssair/domtree.jl @@ -657,6 +657,8 @@ end Compute the nearest common (post-)dominator of `a` and `b`. """ function nearest_common_dominator(domtree::GenericDomTree, a::BBNumber, b::BBNumber) + a == 0 && return a + b == 0 && return b alevel = domtree.nodes[a].level blevel = domtree.nodes[b].level # W.l.g. assume blevel <= alevel diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index 6f20ca6b20ec8..152fc3be3d1c3 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1619,6 +1619,7 @@ function try_resolve_finalizer!(ir::IRCode, idx::Int, finalizer_idx::Int, defuse end all(check_defuse, defuse.uses) || return nothing all(check_defuse, defuse.defs) || return nothing + bb_insert_block != 0 || return nothing # verify post-dominator of all uses exists # Check #3 dominates(domtree, finalizer_bb, bb_insert_block) || return nothing diff --git a/base/compiler/ssair/show.jl b/base/compiler/ssair/show.jl index 3936a82a6560e..873ab2c68f7a8 100644 --- a/base/compiler/ssair/show.jl +++ b/base/compiler/ssair/show.jl @@ -1019,8 +1019,9 @@ function Base.show(io::IO, e::Effects) printstyled(io, effectbits_letter(e, :inaccessiblememonly, 'm'); color=effectbits_color(e, :inaccessiblememonly)) print(io, ',') printstyled(io, effectbits_letter(e, :noub, 'u'); color=effectbits_color(e, :noub)) + print(io, ',') + printstyled(io, effectbits_letter(e, :nonoverlayed, 'o'); color=effectbits_color(e, :nonoverlayed)) print(io, ')') - e.nonoverlayed || printstyled(io, '′'; color=:red) end @specialize diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index fc1c537f740cd..83575cd6f9278 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -244,16 +244,48 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) typeinf_nocycle(interp, frame) || return false # frame is now part of a higher cycle # with no active ip's, frame is done frames = frame.callers_in_cycle - isempty(frames) && push!(frames, frame) - valid_worlds = WorldRange() + if isempty(frames) + push!(frames, frame) + finish_nocycle(interp, frame) + elseif length(frames) == 1 + @assert frames[1] === frame "invalid callers_in_cycle" + finish_nocycle(interp, frame) + else + finish_cycle(interp, frames) + end + empty!(frames) + return true +end + +function finish_nocycle(::AbstractInterpreter, frame::InferenceState) + frame.dont_work_on_me = true + finish(frame, frame.interp) + opt = frame.result.src + if opt isa OptimizationState # implies `may_optimize(caller.interp) === true` + optimize(frame.interp, opt, frame.result) + end + finish!(frame.interp, frame) + if is_cached(frame) + cache_result!(frame.interp, frame.result) + end + return nothing +end + +function finish_cycle(::AbstractInterpreter, frames::Vector{InferenceState}) + cycle_valid_worlds = WorldRange() + cycle_valid_effects = EFFECTS_TOTAL for caller in frames @assert !(caller.dont_work_on_me) caller.dont_work_on_me = true - # might might not fully intersect these earlier, so do that now - valid_worlds = intersect(caller.valid_worlds, valid_worlds) + # converge the world age range and effects for this cycle here: + # all frames in the cycle should have the same bits of `valid_worlds` and `effects` + # that are simply the intersection of each partial computation, without having + # dependencies on each other (unlike rt and exct) + cycle_valid_worlds = intersect(cycle_valid_worlds, caller.valid_worlds) + cycle_valid_effects = merge_effects(cycle_valid_effects, caller.ipo_effects) end for caller in frames - caller.valid_worlds = valid_worlds + adjust_cycle_frame!(caller, cycle_valid_worlds, cycle_valid_effects) finish(caller, caller.interp) end for caller in frames @@ -268,8 +300,22 @@ function _typeinf(interp::AbstractInterpreter, frame::InferenceState) cache_result!(caller.interp, caller.result) end end - empty!(frames) - return true + return nothing +end + +function adjust_cycle_frame!(sv::InferenceState, cycle_valid_worlds::WorldRange, cycle_valid_effects::Effects) + sv.valid_worlds = cycle_valid_worlds + sv.ipo_effects = cycle_valid_effects + # traverse the callees of this cycle that are tracked within `sv.cycle_backedges` + # and adjust their statements so that they are consistent with the new `cycle_valid_effects` + new_flags = flags_for_effects(cycle_valid_effects) + for (callee, pc) in sv.cycle_backedges + old_currpc = callee.currpc + callee.currpc = pc + set_curr_ssaflag!(callee, new_flags, IR_FLAGS_EFFECTS) + callee.currpc = old_currpc + end + return nothing end function is_result_constabi_eligible(result::InferenceResult) @@ -446,6 +492,9 @@ function adjust_effects(ipo_effects::Effects, def::Method) elseif is_effect_overridden(override, :noub_if_noinbounds) && ipo_effects.noub !== ALWAYS_TRUE ipo_effects = Effects(ipo_effects; noub=NOUB_IF_NOINBOUNDS) end + if is_effect_overridden(override, :consistent_overlay) + ipo_effects = Effects(ipo_effects; nonoverlayed=CONSISTENT_OVERLAY) + end return ipo_effects end @@ -872,7 +921,8 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize update_valid_age!(caller, frame.valid_worlds) isinferred = is_inferred(frame) edge = isinferred ? mi : nothing - effects = isinferred ? frame.result.ipo_effects : adjust_effects(Effects(), method) # effects are adjusted already within `finish` for ipo_effects + effects = isinferred ? frame.result.ipo_effects : # effects are adjusted already within `finish` for ipo_effects + adjust_effects(effects_for_cycle(frame.ipo_effects), method) exc_bestguess = refine_exception_type(frame.exc_bestguess, effects) # propagate newly inferred source to the inliner, allowing efficient inlining w/o deserialization: # note that this result is cached globally exclusively, we can use this local result destructively @@ -887,11 +937,16 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize # return the current knowledge about this cycle frame = frame::InferenceState update_valid_age!(caller, frame.valid_worlds) - effects = adjust_effects(Effects(), method) + effects = adjust_effects(effects_for_cycle(frame.ipo_effects), method) exc_bestguess = refine_exception_type(frame.exc_bestguess, effects) return EdgeCallResult(frame.bestguess, exc_bestguess, nothing, effects) end +# The `:terminates` effect bit must be conservatively tainted unless recursion cycle has +# been fully resolved. As for other effects, there's no need to taint them at this moment +# because they will be tainted as we try to resolve the cycle. +effects_for_cycle(effects::Effects) = Effects(effects; terminates=false) + function cached_return_type(code::CodeInstance) rettype = code.rettype isdefined(code, :rettype_const) || return rettype diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index d4785df98ec21..91c9e44f1913b 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -1766,7 +1766,8 @@ The task will run in the "world age" from the parent at construction when [`sche !!! warning By default tasks will have the sticky bit set to true `t.sticky`. This models the historic default for [`@async`](@ref). Sticky tasks can only be run on the worker thread - they are first scheduled on. To obtain the behavior of [`Threads.@spawn`](@ref) set the sticky + they are first scheduled on, and when scheduled will make the task that they were scheduled + from sticky. To obtain the behavior of [`Threads.@spawn`](@ref) set the sticky bit manually to `false`. # Examples @@ -2490,12 +2491,17 @@ cases. See also [`setproperty!`](@ref Base.setproperty!) and [`getglobal`](@ref) # Examples -```jldoctest -julia> module M end; +```jldoctest; filter = r"Stacktrace:(\\n \\[[0-9]+\\].*)*" +julia> module M; global a; end; julia> M.a # same as `getglobal(M, :a)` ERROR: UndefVarError: `a` not defined in `M` -Suggestion: check for spelling errors or missing imports. +Suggestion: add an appropriate import or assignment. This global was declared but not assigned. +Stacktrace: + [1] getproperty(x::Module, f::Symbol) + @ Base ./Base.jl:42 + [2] top-level scope + @ none:1 julia> setglobal!(M, :a, 1) 1 diff --git a/base/essentials.jl b/base/essentials.jl index 5fdc718085df0..45f0829da66df 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -212,7 +212,8 @@ macro _total_meta() #=:notaskstate=#true, #=:inaccessiblememonly=#true, #=:noub=#true, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :foldable` (supposed to be used for bootstrapping) macro _foldable_meta() @@ -225,7 +226,8 @@ macro _foldable_meta() #=:notaskstate=#true, #=:inaccessiblememonly=#true, #=:noub=#true, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :terminates_locally` (supposed to be used for bootstrapping) macro _terminates_locally_meta() @@ -238,7 +240,8 @@ macro _terminates_locally_meta() #=:notaskstate=#false, #=:inaccessiblememonly=#false, #=:noub=#false, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :terminates_globally` (supposed to be used for bootstrapping) macro _terminates_globally_meta() @@ -251,7 +254,8 @@ macro _terminates_globally_meta() #=:notaskstate=#false, #=:inaccessiblememonly=#false, #=:noub=#false, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :terminates_globally :notaskstate` (supposed to be used for bootstrapping) macro _terminates_globally_notaskstate_meta() @@ -264,7 +268,8 @@ macro _terminates_globally_notaskstate_meta() #=:notaskstate=#true, #=:inaccessiblememonly=#false, #=:noub=#false, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :terminates_globally :noub` (supposed to be used for bootstrapping) macro _terminates_globally_noub_meta() @@ -277,7 +282,8 @@ macro _terminates_globally_noub_meta() #=:notaskstate=#false, #=:inaccessiblememonly=#false, #=:noub=#true, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :effect_free :terminates_locally` (supposed to be used for bootstrapping) macro _effect_free_terminates_locally_meta() @@ -290,7 +296,8 @@ macro _effect_free_terminates_locally_meta() #=:notaskstate=#false, #=:inaccessiblememonly=#false, #=:noub=#false, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :nothrow :noub` (supposed to be used for bootstrapping) macro _nothrow_noub_meta() @@ -303,7 +310,8 @@ macro _nothrow_noub_meta() #=:notaskstate=#false, #=:inaccessiblememonly=#false, #=:noub=#true, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping) macro _nothrow_meta() @@ -316,7 +324,8 @@ macro _nothrow_meta() #=:notaskstate=#false, #=:inaccessiblememonly=#false, #=:noub=#false, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :nothrow` (supposed to be used for bootstrapping) macro _noub_meta() @@ -329,7 +338,8 @@ macro _noub_meta() #=:notaskstate=#false, #=:inaccessiblememonly=#false, #=:noub=#true, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :notaskstate` (supposed to be used for bootstrapping) macro _notaskstate_meta() @@ -342,7 +352,8 @@ macro _notaskstate_meta() #=:notaskstate=#true, #=:inaccessiblememonly=#false, #=:noub=#false, - #=:noub_if_noinbounds=#false)) + #=:noub_if_noinbounds=#false, + #=:consistent_overlay=#false)) end # can be used in place of `@assume_effects :noub_if_noinbounds` (supposed to be used for bootstrapping) macro _noub_if_noinbounds_meta() @@ -355,7 +366,8 @@ macro _noub_if_noinbounds_meta() #=:notaskstate=#false, #=:inaccessiblememonly=#false, #=:noub=#false, - #=:noub_if_noinbounds=#true)) + #=:noub_if_noinbounds=#true, + #=:consistent_overlay=#false)) end # another version of inlining that propagates an inbounds context diff --git a/base/experimental.jl b/base/experimental.jl index 8dbdcacd65376..58c7258120f3f 100644 --- a/base/experimental.jl +++ b/base/experimental.jl @@ -318,7 +318,7 @@ function show_error_hints(io, ex, args...) isnothing(hinters) && return for handler in hinters try - Base.invokelatest(handler, io, ex, args...) + @invokelatest handler(io, ex, args...) catch err tn = typeof(handler).name @error "Hint-handler $handler for $(typeof(ex)) in $(tn.module) caused an error" @@ -330,17 +330,99 @@ end include("opaque_closure.jl") """ - Experimental.@overlay mt [function def] + Base.Experimental.@overlay mt def Define a method and add it to the method table `mt` instead of to the global method table. This can be used to implement a method override mechanism. Regular compilation will not consider these methods, and you should customize the compilation flow to look in these method tables (e.g., using [`Core.Compiler.OverlayMethodTable`](@ref)). + +!!! note + Please be aware that when defining overlay methods using `@overlay`, it is not necessary + to have an original method that corresponds exactly in terms of how the method dispatches. + This means that the method overlay mechanism enabled by `@overlay` is not implemented by + replacing the methods themselves, but through an additional and prioritized method + lookup during the method dispatch. + + Considering this, it is important to understand that in compilations using an overlay + method table like the following, the method dispatched by `callx(x)` is not the regular + method `callx(::Float64)`, but the overlay method `callx(x::Real)`: + ```julia + callx(::Real) = :real + @overlay SOME_OVERLAY_MT callx(::Real) = :overlay_real + callx(::Float64) = :float64 + + # some overlay callsite + let x::Float64 + callx(x) #> :overlay_real + end + ``` """ macro overlay(mt, def) - def = macroexpand(__module__, def) # to expand @inline, @generated, etc - is_function_def(def) || error("@overlay requires a function definition") - return esc(overlay_def!(mt, def)) + inner = Base.unwrap_macrocalls(def) + is_function_def(inner) || error("@overlay requires a function definition") + overlay_def!(mt, inner) + return esc(def) +end + +""" + Base.Experimental.@consistent_overlay mt def + +This macro operates almost identically to [`Base.Experimental.@overlay`](@ref), defining a +new overlay method. The key difference with this macro is that it informs the compiler that +the invocation of the overlay method it defines is `:consistent` with a regular, +non-overlayed method call. + +More formally, when evaluating a generic function call ``f(x)`` at a specific world age +``i``, if a regular method call ``fᵢ(x)`` is redirected to an overlay method call ``fᵢ′(x)`` +defined by this macro, ``fᵢ(x)`` and ``fᵢ′(x)`` are considered `:consistent` if the following +conditions are met: +- If ``fᵢ(x)`` returns a value ``y``, then ``fᵢ′(x)`` also returns some value ``yᵢ``, and ``y ≡ yᵢ`` holds. +- If ``fᵢ(x)`` throws an exception, then ``fᵢ′(x)`` also throws some exception. + +For a detailed definition of `:consistent`-cy, consult the corresponding section in +[`Base.@assume_effects`](@ref). + +!!! note + Note that the requirements for `:consistent`-cy include not only that the return values + are egal, but also that the manner of termination is the same. However, it's important + to aware that when they throw exceptions, the exceptions themselves don't necessarily + have to be egal. In other words, if ``fᵢ(x)`` throws an exception, ``fᵢ′(x)`` is + required to also throw one, but the exact exceptions may differ. + +!!! note + Please note that the `:consistent`-cy requirement applies not to method itself but to + _method invocation_. This means that for the use of `@consistent_overlay`, it is + necessary for method invocations with the native regular compilation and those with + a compilation with overlay method table to be `:consistent`. + + For example, it is important to understand that, `@consistent_overlay` can be used like + the following: + ```julia + callsin(x::Real) = x < 0 ? error(x) : sin(x) + @consistent_overlay SOME_OVERLAY_MT callsin(x::Float64) = + x < 0 ? error_somehow(x) : sin(x) + ``` + However, be aware that this `@consistent_overlay` will immediately become invalid if a + new method for `callsin` is defined subsequently, such as: + ```julia + callsin(x::Float64) = cos(x) + ``` + + This specifically implies that the use of `@consistent_overlay` should be restricted as + much as possible to cases where a regular method with a concrete signature is replaced + by an overlay method with the same concrete signature. + + This constraint is closely related to the note in [`Base.Experimental.@overlay`](@ref); + you are advised to consult that as well. +""" +macro consistent_overlay(mt, def) + inner = Base.unwrap_macrocalls(def) + is_function_def(inner) || error("@consistent_overlay requires a function definition") + overlay_def!(mt, inner) + override = Core.Compiler.EffectsOverride(; consistent_overlay=true) + Base.pushmeta!(def::Expr, Base.form_purity_expr(override)) + return esc(def) end function overlay_def!(mt, @nospecialize ex) @@ -367,11 +449,11 @@ let new_mt(name::Symbol, mod::Module) = begin end """ - Experimental.@MethodTable(name) + Base.Experimental.@MethodTable name Create a new MethodTable in the current module, bound to `name`. This method table can be -used with the [`Experimental.@overlay`](@ref) macro to define methods for a function without -adding them to the global method table. +used with the [`Base.Experimental.@overlay`](@ref) macro to define methods for a function +without adding them to the global method table. """ :@MethodTable diff --git a/base/exports.jl b/base/exports.jl index 152831dc6043a..ffbe0dd6830ba 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -1083,6 +1083,7 @@ public # Types AbstractLock, + AbstractPipe, AsyncCondition, CodeUnits, Event, @@ -1090,6 +1091,7 @@ public Fix2, Generator, ImmutableDict, + Lockable, OneTo, LogRange, AnnotatedString, @@ -1117,6 +1119,7 @@ public split_rest, tail, checked_length, + elsize, # Loading DL_LOAD_PATH, diff --git a/base/expr.jl b/base/expr.jl index b90f75e75a76f..dc85f3591efa1 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -484,7 +484,7 @@ CodeInfo( !!! compat "Julia 1.10" The usage within a function body requires at least Julia 1.10. -!!! compact "Julia 1.11" +!!! compat "Julia 1.11" The code block annotation requires at least Julia 1.11. !!! warning @@ -530,7 +530,7 @@ The `:consistent` setting asserts that for egal (`===`) inputs: !!! note The `:consistent`-cy assertion is made world-age wise. More formally, write - ``fᵢ`` for the evaluation of ``f`` in world-age ``i``, then we require: + ``fᵢ`` for the evaluation of ``f`` in world-age ``i``, then this setting requires: ```math ∀ i, x, y: x ≡ y → fᵢ(x) ≡ fᵢ(y) ``` @@ -742,7 +742,7 @@ macro assume_effects(args...) lastex = args[end] override = compute_assumed_settings(args[begin:end-1]) if is_function_def(unwrap_macrocalls(lastex)) - return esc(pushmeta!(lastex, form_purity_expr(override))) + return esc(pushmeta!(lastex::Expr, form_purity_expr(override))) elseif isexpr(lastex, :macrocall) && lastex.args[1] === Symbol("@ccall") lastex.args[1] = GlobalRef(Base, Symbol("@ccall_effects")) insert!(lastex.args, 3, Core.Compiler.encode_effects_override(override)) @@ -767,7 +767,7 @@ function compute_assumed_settings(settings) for setting in settings override = compute_assumed_setting(override, setting) override === nothing && - throw(ArgumentError("@assume_effects $setting not supported")) + throw(ArgumentError("`@assume_effects $setting` not supported")) end return override end @@ -815,10 +815,11 @@ function compute_assumed_setting(override::EffectsOverride, @nospecialize(settin end function form_purity_expr(override::EffectsOverride) - return Expr(:purity, - override.consistent, override.effect_free, override.nothrow, - override.terminates_globally, override.terminates_locally, override.notaskstate, - override.inaccessiblememonly, override.noub, override.noub_if_noinbounds) + ex = Expr(:purity) + for i = 1:Core.Compiler.NUM_EFFECTS_OVERRIDES + push!(ex.args, getfield(override, i)) + end + return ex end """ diff --git a/base/genericmemory.jl b/base/genericmemory.jl index c1dc215a68d33..64a647cdc545a 100644 --- a/base/genericmemory.jl +++ b/base/genericmemory.jl @@ -3,9 +3,21 @@ ## genericmemory.jl: Managed Memory """ - GenericMemory{kind::Symbol, T, addrspace=Core.CPU} <: AbstractVector{T} + GenericMemory{kind::Symbol, T, addrspace=Core.CPU} <: DenseVector{T} -One-dimensional dense array with elements of type `T`. +Fixed-size [`DenseVector{T}`](@ref DenseVector). + +`kind` can currently be either `:not_atomic` or `:atomic`. For details on what `:atomic` implies, see [`AtomicMemory`](@ref) + +`addrspace` can currently only be set to Core.CPU. It is designed to to permit extension by other systems +such as GPUs, which might define values such as: +``` +module CUDA +const Generic = bitcast(Core.AddrSpace{CUDA}, 0) +const Global = bitcast(Core.AddrSpace{CUDA}, 1) +end +``` +The exact semantics of these other addrspaces is defined by the specific backend, but will error if the user is attempting to access these on the CPU. !!! compat "Julia 1.11" This type requires Julia 1.11 or later. @@ -15,7 +27,7 @@ GenericMemory """ Memory{T} == GenericMemory{:not_atomic, T, Core.CPU} -One-dimensional dense array with elements of type `T`. +Fixed-size [`DenseVector{T}`](@ref DenseVector). !!! compat "Julia 1.11" This type requires Julia 1.11 or later. @@ -25,8 +37,18 @@ Memory """ AtomicMemory{T} == GenericMemory{:atomic, T, Core.CPU} -One-dimensional dense array with elements of type `T`, where each element is -independently atomic when accessed, and cannot be set non-atomically. +Fixed-size [`DenseVector{T}`](@ref DenseVector). +Access to its any of its elements is performed atomically (with `:monotonic` ordering). +Setting any of the elements must be accomplished using the `@atomic` macro and explicitly specifying ordering. + +!!! warning + Each element is independently atomic when accessed, and cannot be set non-atomically. + Currently the `@atomic` macro and higher level interface have not been completed, + but the building blocks for a future implementation are the internal intrinsics + `Core.memoryrefget`, `Core.memoryrefset!`, `Core.memoryref_isassigned`, `Core.memoryrefswap!`, + `Core.memoryrefmodify!`, and `Core.memoryrefreplace!`. + +For details, see [Atomic Operations](@ref man-atomic-operations) !!! compat "Julia 1.11" This type requires Julia 1.11 or later. @@ -288,28 +310,3 @@ function indcopy(sz::Dims, I::GenericMemory) src = eltype(I)[I[i][_findin(I[i], i < n ? (1:sz[i]) : (1:s))] for i = 1:n] dst, src end - -# Wrapping a memory region in an Array -@eval begin # @eval for the Array construction. Block for the docstring. - function reshape(m::GenericMemory{M, T}, dims::Vararg{Int, N}) where {M, T, N} - len = Core.checked_dims(dims...) - length(m) == len || throw(DimensionMismatch("parent has $(length(m)) elements, which is incompatible with size $(dims)")) - ref = MemoryRef(m) - $(Expr(:new, :(Array{T, N}), :ref, :dims)) - end - - """ - view(m::GenericMemory{M, T}, inds::Union{UnitRange, OneTo}) - - Create a vector `v::Vector{T}` backed by the specified indices of `m`. It is only safe to - resize `v` if `m` is subseqently not used. - """ - function view(m::GenericMemory{M, T}, inds::Union{UnitRange, OneTo}) where {M, T} - isempty(inds) && return T[] # needed to allow view(Memory{T}(undef, 0), 2:1) - @boundscheck checkbounds(m, inds) - ref = MemoryRef(m, first(inds)) # @inbounds would be safe here but does not help performance. - dims = (Int(length(inds)),) - $(Expr(:new, :(Array{T, 1}), :ref, :dims)) - end -end -view(m::GenericMemory, inds::Colon) = view(m, eachindex(m)) diff --git a/base/indices.jl b/base/indices.jl index 0bd46f9787dab..bf4fd8b5a7cb6 100644 --- a/base/indices.jl +++ b/base/indices.jl @@ -310,9 +310,9 @@ to_index(I::AbstractArray{Bool}) = LogicalIndex(I) to_index(I::AbstractArray) = I to_index(I::AbstractArray{Union{}}) = I to_index(I::AbstractArray{<:Union{AbstractArray, Colon}}) = - throw(ArgumentError("invalid index: $(limitrepr(I)) of type $(typeof(I))")) + throw(ArgumentError(LazyString("invalid index: ", limitrepr(I), " of type ", typeof(I)))) to_index(::Colon) = throw(ArgumentError("colons must be converted by to_indices(...)")) -to_index(i) = throw(ArgumentError("invalid index: $(limitrepr(i)) of type $(typeof(i))")) +to_index(i) = throw(ArgumentError(LazyString("invalid index: ", limitrepr(i), " of type ", typeof(i)))) # The general to_indices is mostly defined in multidimensional.jl, but this # definition is required for bootstrap: diff --git a/base/initdefs.jl b/base/initdefs.jl index 56c2c0c587272..96bdc7957bcca 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -272,7 +272,7 @@ function load_path_expand(env::AbstractString)::Union{String, Nothing} env == "@" && return active_project(false) env == "@." && return current_project() env == "@stdlib" && return Sys.STDLIB - if startswith(env, "@scriptdir") + if startswith(env, "@script") if @isdefined(PROGRAM_FILE) dir = dirname(PROGRAM_FILE) else @@ -283,7 +283,12 @@ function load_path_expand(env::AbstractString)::Union{String, Nothing} end dir = dirname(ARGS[1]) end - return abspath(replace(env, "@scriptdir" => dir)) + if env == "@script" # complete match, not startswith, so search upwards + return current_project(dir) + else + # starts with, so assume relative path is after + return abspath(replace(env, "@script" => dir)) + end end env = replace(env, '#' => VERSION.major, count=1) env = replace(env, '#' => VERSION.minor, count=1) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 3f1c7f4367b4f..e27c5f665b3a8 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -954,7 +954,7 @@ julia> bitstring(2.2) ``` """ function bitstring(x::T) where {T} - isprimitivetype(T) || throw(ArgumentError("$T not a primitive type")) + isprimitivetype(T) || throw(ArgumentError(LazyString(T, " not a primitive type"))) sz = sizeof(T) * 8 str = StringMemory(sz) i = sz @@ -1054,7 +1054,7 @@ julia> digits!([2, 2, 2, 2, 2, 2], 10, base = 2) function digits!(a::AbstractVector{T}, n::Integer; base::Integer = 10) where T<:Integer 2 <= abs(base) || throw(DomainError(base, "base must be ≥ 2 or ≤ -2")) hastypemax(T) && abs(base) - 1 > typemax(T) && - throw(ArgumentError("type $T too small for base $base")) + throw(ArgumentError(LazyString("type ", T, " too small for base ", base))) isempty(a) && return a if base > 0 diff --git a/base/io.jl b/base/io.jl index fb883234be4df..a0b80f6f3e056 100644 --- a/base/io.jl +++ b/base/io.jl @@ -1426,7 +1426,7 @@ previously marked position. Throw an error if the stream is not marked. See also [`mark`](@ref), [`unmark`](@ref), [`ismarked`](@ref). """ function reset(io::T) where T<:IO - ismarked(io) || throw(ArgumentError("$T not marked")) + ismarked(io) || throw(ArgumentError(LazyString(T, " not marked"))) m = io.mark seek(io, m) io.mark = -1 # must be after seek, or seek may fail diff --git a/base/iobuffer.jl b/base/iobuffer.jl index f9585b0599919..38713908bef9c 100644 --- a/base/iobuffer.jl +++ b/base/iobuffer.jl @@ -42,7 +42,7 @@ end # allocate Vector{UInt8}s for IOBuffer storage that can efficiently become Strings StringMemory(n::Integer) = unsafe_wrap(Memory{UInt8}, _string_n(n)) -StringVector(n::Integer) = view(StringMemory(n), 1:n)::Vector{UInt8} +StringVector(n::Integer) = wrap(Array, StringMemory(n)) # IOBuffers behave like Files. They are typically readable and writable. They are seekable. (They can be appendable). @@ -466,7 +466,7 @@ function take!(io::IOBuffer) if nbytes == 0 || io.reinit data = StringVector(0) elseif io.writable - data = view(io.data, io.offset+1:nbytes+io.offset) + data = wrap(Array, MemoryRef(io.data, io.offset + 1), nbytes) else data = copyto!(StringVector(nbytes), 1, io.data, io.offset + 1, nbytes) end @@ -475,7 +475,7 @@ function take!(io::IOBuffer) if nbytes == 0 data = StringVector(0) elseif io.writable - data = view(io.data, io.ptr:io.ptr+nbytes-1) + data = wrap(Array, MemoryRef(io.data, io.ptr), nbytes) else data = read!(io, data) end @@ -501,7 +501,11 @@ state. This should only be used internally for performance-critical It might save an allocation compared to `take!` (if the compiler elides the Array allocation), as well as omits some checks. """ -_unsafe_take!(io::IOBuffer) = view(io.data, io.offset+1:io.size) +_unsafe_take!(io::IOBuffer) = + wrap(Array, io.size == io.offset ? + MemoryRef(Memory{UInt8}()) : + MemoryRef(io.data, io.offset + 1), + io.size - io.offset) function write(to::IO, from::GenericIOBuffer) written::Int = bytesavailable(from) diff --git a/base/iterators.jl b/base/iterators.jl index a0b3a6cd4672d..8fa09e6d7e406 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -15,7 +15,7 @@ using .Base: AbstractRange, AbstractUnitRange, UnitRange, LinearIndices, TupleOrBottom, (:), |, +, -, *, !==, !, ==, !=, <=, <, >, >=, missing, any, _counttuple, eachindex, ntuple, zero, prod, reduce, in, firstindex, lastindex, - tail, fieldtypes, min, max, minimum, zero, oneunit, promote, promote_shape + tail, fieldtypes, min, max, minimum, zero, oneunit, promote, promote_shape, LazyString using Core: @doc if Base !== Core.Compiler @@ -1099,7 +1099,7 @@ _prod_size(t::Tuple) = (_prod_size1(t[1], IteratorSize(t[1]))..., _prod_size(tai _prod_size1(a, ::HasShape) = size(a) _prod_size1(a, ::HasLength) = (length(a),) _prod_size1(a, A) = - throw(ArgumentError("Cannot compute size for object of type $(typeof(a))")) + throw(ArgumentError(LazyString("Cannot compute size for object of type ", typeof(a)))) axes(P::ProductIterator) = _prod_indices(P.iterators) _prod_indices(::Tuple{}) = () @@ -1107,7 +1107,7 @@ _prod_indices(t::Tuple) = (_prod_axes1(t[1], IteratorSize(t[1]))..., _prod_indic _prod_axes1(a, ::HasShape) = axes(a) _prod_axes1(a, ::HasLength) = (OneTo(length(a)),) _prod_axes1(a, A) = - throw(ArgumentError("Cannot compute indices for object of type $(typeof(a))")) + throw(ArgumentError(LazyString("Cannot compute indices for object of type ", typeof(a)))) ndims(p::ProductIterator) = length(axes(p)) length(P::ProductIterator) = reduce(checked_mul, size(P); init=1) diff --git a/base/linking.jl b/base/linking.jl index 2d68ea730c0fb..4bf0198962346 100644 --- a/base/linking.jl +++ b/base/linking.jl @@ -110,7 +110,7 @@ function ld() # LLD supports mingw style linking flavor = "gnu" m = Sys.ARCH == :x86_64 ? "i386pep" : "i386pe" - default_args = `-m $m -Bdynamic --enable-auto-image-base --allow-multiple-definition` + default_args = `-m $m -Bdynamic --enable-auto-image-base --allow-multiple-definition --disable-auto-import --disable-runtime-pseudo-reloc` elseif Sys.isapple() flavor = "darwin" arch = Sys.ARCH == :aarch64 ? :arm64 : Sys.ARCH diff --git a/base/loading.jl b/base/loading.jl index 8c5767df811d6..f210d2f67c9ba 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -669,7 +669,7 @@ function manifest_uuid_path(env::String, pkg::PkgId)::Union{Nothing,String,Missi # if `pkg` matches the project, return the project itself return project_file_path(project_file) end - mby_ext = project_file_ext_path(project_file, pkg.name) + mby_ext = project_file_ext_path(project_file, pkg) mby_ext === nothing || return mby_ext # look for manifest file and `where` stanza return explicit_manifest_uuid_path(project_file, pkg) @@ -684,7 +684,7 @@ function manifest_uuid_path(env::String, pkg::PkgId)::Union{Nothing,String,Missi if parent_project_file !== nothing parentproj = project_file_name_uuid(parent_project_file, parentid.name) if parentproj == parentid - mby_ext = project_file_ext_path(parent_project_file, pkg.name) + mby_ext = project_file_ext_path(parent_project_file, pkg) mby_ext === nothing || return mby_ext end end @@ -700,13 +700,13 @@ function find_ext_path(project_path::String, extname::String) return joinpath(project_path, "ext", extname * ".jl") end -function project_file_ext_path(project_file::String, name::String) +function project_file_ext_path(project_file::String, ext::PkgId) d = parsed_toml(project_file) p = project_file_path(project_file) exts = get(d, "extensions", nothing)::Union{Dict{String, Any}, Nothing} if exts !== nothing - if name in keys(exts) - return find_ext_path(p, name) + if ext.name in keys(exts) && ext.uuid == uuid5(UUID(d["uuid"]::String), ext.name) + return find_ext_path(p, ext.name) end end return nothing @@ -792,9 +792,7 @@ function implicit_env_project_file_extension(dir::String, ext::PkgId) for pkg in readdir(dir; join=true) project_file = env_project_file(pkg) project_file isa String || continue - proj = project_file_name_uuid(project_file, "") - uuid5(proj.uuid, ext.name) == ext.uuid || continue - path = project_file_ext_path(project_file, ext.name) + path = project_file_ext_path(project_file, ext) if path !== nothing return path, project_file end @@ -2186,15 +2184,16 @@ function collect_manifest_warnings() if !isempty(unsuitable_manifests) msg *= """ - Note that the following manifests in the load path were resolved with a different - julia version, which may be the cause of the error: + julia version, which may be the cause of the error. Try to re-resolve them in the + current version, or consider deleting them if that fails: $(join(unsuitable_manifests, "\n ")) """ end if !isempty(dev_manifests) msg *= """ - - Note that the following manifests in the load path were resolved a potentially - different DEV version of the current version, which may be the cause - of the error: + - Note that the following manifests in the load path were resolved with a potentially + different DEV version of the current version, which may be the cause of the error. + Try to re-resolve them in the current version, or consider deleting them if that fails: $(join(dev_manifests, "\n ")) """ end @@ -2455,51 +2454,52 @@ function _require_from_serialized(uuidkey::PkgId, path::String, ocachepath::Unio end # load a serialized file directly from append_bundled_depot_path for uuidkey without stalechecks -function require_stdlib(uuidkey::PkgId, ext::Union{Nothing, String}=nothing) +function require_stdlib(package_uuidkey::PkgId, ext::Union{Nothing, String}=nothing) @lock require_lock begin - if root_module_exists(uuidkey) - return loaded_modules[uuidkey] + # the PkgId of the ext, or package if not an ext + this_uuidkey = ext isa String ? PkgId(uuid5(package_uuidkey.uuid, ext), ext) : package_uuidkey + if root_module_exists(this_uuidkey) + return loaded_modules[this_uuidkey] end # first since this is a stdlib, try to look there directly first env = Sys.STDLIB #sourcepath = "" if ext === nothing - sourcepath = normpath(env, uuidkey.name, "src", uuidkey.name * ".jl") + sourcepath = normpath(env, this_uuidkey.name, "src", this_uuidkey.name * ".jl") else - sourcepath = find_ext_path(normpath(joinpath(env, uuidkey.name)), ext) - uuidkey = PkgId(uuid5(uuidkey.uuid, ext), ext) + sourcepath = find_ext_path(normpath(joinpath(env, package_uuidkey.name)), ext) end - #mbypath = manifest_uuid_path(env, uuidkey) - #if mbypath isa String - # sourcepath = entry_path(mbypath, uuidkey.name) + #mbypath = manifest_uuid_path(env, this_uuidkey) + #if mbypath isa String && isfile_casesensitive(mbypath) + # sourcepath = mbypath #else # # if the user deleted the stdlib folder, we next try using their environment - # sourcepath = locate_package_env(uuidkey) + # sourcepath = locate_package_env(this_uuidkey) # if sourcepath !== nothing # sourcepath, env = sourcepath # end #end #if sourcepath === nothing # throw(ArgumentError(""" - # Package $(repr("text/plain", uuidkey)) is required but does not seem to be installed. + # Package $(repr("text/plain", this_uuidkey)) is required but does not seem to be installed. # """)) #end - set_pkgorigin_version_path(uuidkey, sourcepath) + set_pkgorigin_version_path(this_uuidkey, sourcepath) depot_path = append_bundled_depot_path!(empty(DEPOT_PATH)) - newm = start_loading(uuidkey) + newm = start_loading(this_uuidkey) newm === nothing || return newm try - newm = _require_search_from_serialized(uuidkey, sourcepath, UInt128(0), false; DEPOT_PATH=depot_path) + newm = _require_search_from_serialized(this_uuidkey, sourcepath, UInt128(0), false; DEPOT_PATH=depot_path) finally - end_loading(uuidkey, newm) + end_loading(this_uuidkey, newm) end if newm isa Module # After successfully loading, notify downstream consumers - insert_extension_triggers(env, uuidkey) - run_package_callbacks(uuidkey) + insert_extension_triggers(env, this_uuidkey) + run_package_callbacks(this_uuidkey) else # if the user deleted their bundled depot, next try to load it completely normally - newm = _require_prelocked(uuidkey) + newm = _require_prelocked(this_uuidkey) end return newm end diff --git a/base/multidimensional.jl b/base/multidimensional.jl index 146f8c160e8e9..519ac06e6e7ec 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -589,7 +589,7 @@ module IteratorsMD else # Given the fact that StepRange 1:2:4 === 1:2:3, we lost the original size information # and thus cannot calculate the correct linear indices when the steps are not 1. - throw(ArgumentError("LinearIndices for $(typeof(inds)) with non-1 step size is not yet supported.")) + throw(ArgumentError(LazyString("LinearIndices for ", typeof(inds), " with non-1 step size is not yet supported."))) end end @@ -1044,25 +1044,34 @@ end ### from abstractarray.jl -# In the common case where we have two views into the same parent, aliasing checks -# are _much_ easier and more important to get right -function mightalias(A::SubArray{T,<:Any,P}, B::SubArray{T,<:Any,P}) where {T,P} - if !_parentsmatch(A.parent, B.parent) - # We cannot do any better than the usual dataids check - return !_isdisjoint(dataids(A), dataids(B)) - end - # Now we know that A.parent === B.parent. This means that the indices of A - # and B are the same length and indexing into the same dimensions. We can - # just walk through them and check for overlaps: O(ndims(A)). We must finally - # ensure that the indices don't alias with either parent - return _indicesmightoverlap(A.indices, B.indices) || - !_isdisjoint(dataids(A.parent), _splatmap(dataids, B.indices)) || - !_isdisjoint(dataids(B.parent), _splatmap(dataids, A.indices)) +function mightalias(A::SubArray, B::SubArray) + # There are three ways that SubArrays might _problematically_ alias one another: + # 1. The parents are the same we can conservatively check if the indices might overlap OR + # 2. The parents alias eachother in a more complicated manner (and we can't trace indices) OR + # 3. One's parent is used in the other's indices + # Note that it's ok for just the indices to alias each other as those should not be mutated, + # so we can always do better than the default !_isdisjoint(dataids(A), dataids(B)) + if isbits(A.parent) || isbits(B.parent) + return false # Quick out for immutables + elseif _parentsmatch(A.parent, B.parent) + # Each SubArray unaliases its own parent from its own indices upon construction, so if + # the two parents are the same, then by construction one cannot alias the other's indices + # and therefore this is the only test we need to perform: + return _indicesmightoverlap(A.indices, B.indices) + else + A_parent_ids = dataids(A.parent) + B_parent_ids = dataids(B.parent) + return !_isdisjoint(A_parent_ids, B_parent_ids) || + !_isdisjoint(A_parent_ids, _splatmap(dataids, B.indices)) || + !_isdisjoint(B_parent_ids, _splatmap(dataids, A.indices)) + end end +# Test if two arrays are backed by exactly the same memory in exactly the same order _parentsmatch(A::AbstractArray, B::AbstractArray) = A === B -# Two reshape(::Array)s of the same size aren't `===` because they have different headers -_parentsmatch(A::Array, B::Array) = pointer(A) == pointer(B) && size(A) == size(B) +_parentsmatch(A::DenseArray, B::DenseArray) = elsize(A) == elsize(B) && pointer(A) == pointer(B) && size(A) == size(B) +_parentsmatch(A::StridedArray, B::StridedArray) = elsize(A) == elsize(B) && pointer(A) == pointer(B) && strides(A) == strides(B) +# Given two SubArrays with the same parent, check if the indices might overlap (returning true if unsure) _indicesmightoverlap(A::Tuple{}, B::Tuple{}) = true _indicesmightoverlap(A::Tuple{}, B::Tuple) = error("malformed subarray") _indicesmightoverlap(A::Tuple, B::Tuple{}) = error("malformed subarray") diff --git a/base/parse.jl b/base/parse.jl index 3f714934206fc..2530e5a46146a 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -386,7 +386,7 @@ function tryparse_internal(::Type{T}, s::AbstractString, raise::Bool; kwargs...) return result end @noinline _parse_failure(T, s::AbstractString, startpos = firstindex(s), endpos = lastindex(s)) = - throw(ArgumentError("cannot parse $(repr(s[startpos:endpos])) as $T")) + throw(ArgumentError(LazyString("cannot parse ", repr(s[startpos:endpos]), " as ", T))) tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int, raise::Bool) where T<:Integer = tryparse_internal(T, s, startpos, endpos, 10, raise) diff --git a/base/rational.jl b/base/rational.jl index b27baf5973d3b..36c26f3da8476 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -27,7 +27,7 @@ end checked_den(num::T, den::T) where T<:Integer = checked_den(T, num, den) checked_den(num::Integer, den::Integer) = checked_den(promote(num, den)...) -@noinline __throw_rational_argerror_zero(T) = throw(ArgumentError("invalid rational: zero($T)//zero($T)")) +@noinline __throw_rational_argerror_zero(T) = throw(ArgumentError(LazyString("invalid rational: zero(", T, ")//zero(", T, ")"))) function Rational{T}(num::Integer, den::Integer) where T<:Integer iszero(den) && iszero(num) && __throw_rational_argerror_zero(T) if T <: Union{Unsigned, Bool} diff --git a/base/reflection.jl b/base/reflection.jl index 550f9cf0bceaf..fd3e7931b2ce8 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -510,6 +510,17 @@ function datatype_nfields(dt::DataType) return unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).nfields end +""" + Base.datatype_npointers(dt::DataType) -> Int + +Return the number of pointers in the layout of a datatype. +""" +function datatype_npointers(dt::DataType) + @_foldable_meta + dt.layout == C_NULL && throw(UndefRefError()) + return unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).npointers +end + """ Base.datatype_pointerfree(dt::DataType) -> Bool @@ -518,9 +529,7 @@ Can be called on any `isconcretetype`. """ function datatype_pointerfree(dt::DataType) @_foldable_meta - dt.layout == C_NULL && throw(UndefRefError()) - npointers = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).npointers - return npointers == 0 + return datatype_npointers(dt) == 0 end """ diff --git a/base/reinterpretarray.jl b/base/reinterpretarray.jl index 8b4025e6903cd..d74a043293a3a 100644 --- a/base/reinterpretarray.jl +++ b/base/reinterpretarray.jl @@ -13,15 +13,16 @@ struct ReinterpretArray{T,N,S,A<:AbstractArray{S},IsReshaped} <: AbstractArray{T function throwbits(S::Type, T::Type, U::Type) @noinline - throw(ArgumentError("cannot reinterpret `$(S)` as `$(T)`, type `$(U)` is not a bits type")) + throw(ArgumentError(LazyString("cannot reinterpret `", S, "` as `", T, "`, type `", U, "` is not a bits type"))) end function throwsize0(S::Type, T::Type, msg) @noinline - throw(ArgumentError("cannot reinterpret a zero-dimensional `$(S)` array to `$(T)` which is of a $msg size")) + throw(ArgumentError(LazyString("cannot reinterpret a zero-dimensional `", S, "` array to `", T, + "` which is of a ", msg, " size"))) end function throwsingleton(S::Type, T::Type) @noinline - throw(ArgumentError("cannot reinterpret a `$(S)` array to `$(T)` which is a singleton type")) + throw(ArgumentError(LazyString("cannot reinterpret a `", S, "` array to `", T, "` which is a singleton type"))) end global reinterpret @@ -67,14 +68,14 @@ struct ReinterpretArray{T,N,S,A<:AbstractArray{S},IsReshaped} <: AbstractArray{T function reinterpret(::Type{T}, a::A) where {T,N,S,A<:AbstractArray{S, N}} function thrownonint(S::Type, T::Type, dim) @noinline - throw(ArgumentError(""" - cannot reinterpret an `$(S)` array to `$(T)` whose first dimension has size `$(dim)`. - The resulting array would have non-integral first dimension. - """)) + throw(ArgumentError(LazyString( + "cannot reinterpret an `", S, "` array to `", T, "` whose first dimension has size `", dim, + "`. The resulting array would have a non-integral first dimension."))) end function throwaxes1(S::Type, T::Type, ax1) @noinline - throw(ArgumentError("cannot reinterpret a `$(S)` array to `$(T)` when the first axis is $ax1. Try reshaping first.")) + throw(ArgumentError(LazyString("cannot reinterpret a `", S, "` array to `", T, + "` when the first axis is ", ax1, ". Try reshaping first."))) end isbitstype(T) || throwbits(S, T, T) isbitstype(S) || throwbits(S, T, S) @@ -99,15 +100,19 @@ struct ReinterpretArray{T,N,S,A<:AbstractArray{S},IsReshaped} <: AbstractArray{T function reinterpret(::typeof(reshape), ::Type{T}, a::A) where {T,S,A<:AbstractArray{S}} function throwintmult(S::Type, T::Type) @noinline - throw(ArgumentError("`reinterpret(reshape, T, a)` requires that one of `sizeof(T)` (got $(sizeof(T))) and `sizeof(eltype(a))` (got $(sizeof(S))) be an integer multiple of the other")) + throw(ArgumentError(LazyString("`reinterpret(reshape, T, a)` requires that one of `sizeof(T)` (got ", + sizeof(T), ") and `sizeof(eltype(a))` (got ", sizeof(S), ") be an integer multiple of the other"))) end function throwsize1(a::AbstractArray, T::Type) @noinline - throw(ArgumentError("`reinterpret(reshape, $T, a)` where `eltype(a)` is $(eltype(a)) requires that `axes(a, 1)` (got $(axes(a, 1))) be equal to 1:$(sizeof(T) ÷ sizeof(eltype(a))) (from the ratio of element sizes)")) + throw(ArgumentError(LazyString("`reinterpret(reshape, ", T, ", a)` where `eltype(a)` is ", eltype(a), + " requires that `axes(a, 1)` (got ", axes(a, 1), ") be equal to 1:", + sizeof(T) ÷ sizeof(eltype(a)), " (from the ratio of element sizes)"))) end function throwfromsingleton(S, T) @noinline - throw(ArgumentError("`reinterpret(reshape, $T, a)` where `eltype(a)` is $S requires that $T be a singleton type, since $S is one")) + throw(ArgumentError(LazyString("`reinterpret(reshape, ", T, ", a)` where `eltype(a)` is ", S, + " requires that ", T, " be a singleton type, since ", S, " is one"))) end isbitstype(T) || throwbits(S, T, T) isbitstype(S) || throwbits(S, T, S) @@ -851,8 +856,8 @@ end inpackedsize = packedsize(In) outpackedsize = packedsize(Out) inpackedsize == outpackedsize || - throw(ArgumentError("Packed sizes of types $Out and $In do not match; got $outpackedsize \ - and $inpackedsize, respectively.")) + throw(ArgumentError(LazyString("Packed sizes of types ", Out, " and ", In, + " do not match; got ", outpackedsize, " and ", inpackedsize, ", respectively."))) in = Ref{In}(x) out = Ref{Out}() if struct_subpadding(Out, In) diff --git a/base/sort.jl b/base/sort.jl index fc27d30499cfb..eb28daca9699f 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -2461,7 +2461,7 @@ function _sort!(v::AbstractVector, a::Algorithm, o::Ordering, kw) @getkw lo hi scratch legacy_dispatch_entry if legacy_dispatch_entry === a # This error prevents infinite recursion for unknown algorithms - throw(ArgumentError("Base.Sort._sort!(::$(typeof(v)), ::$(typeof(a)), ::$(typeof(o)), ::Any) is not defined")) + throw(ArgumentError(LazyString("Base.Sort._sort!(::", typeof(v), ", ::", typeof(a), ", ::", typeof(o), ", ::Any) is not defined"))) else sort!(v, lo, hi, a, o) scratch diff --git a/base/stream.jl b/base/stream.jl index fdb19f7fec45b..d46c5fabb1940 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -1604,7 +1604,7 @@ end skip(s::BufferStream, n) = skip(s.buffer, n) -function reseteof(x::BufferStream) +function reseteof(s::BufferStream) lock(s.cond) do s.status = StatusOpen nothing diff --git a/base/strings/string.jl b/base/strings/string.jl index d091baeb6c663..b2afce897a937 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -117,10 +117,7 @@ String(s::AbstractString) = print_to_string(s) @assume_effects :total String(s::Symbol) = unsafe_string(unsafe_convert(Ptr{UInt8}, s)) unsafe_wrap(::Type{Memory{UInt8}}, s::String) = ccall(:jl_string_to_genericmemory, Ref{Memory{UInt8}}, (Any,), s) -function unsafe_wrap(::Type{Vector{UInt8}}, s::String) - mem = unsafe_wrap(Memory{UInt8}, s) - view(mem, eachindex(mem)) -end +unsafe_wrap(::Type{Vector{UInt8}}, s::String) = wrap(Array, unsafe_wrap(Memory{UInt8}, s)) Vector{UInt8}(s::CodeUnits{UInt8,String}) = copyto!(Vector{UInt8}(undef, length(s)), s) Vector{UInt8}(s::String) = Vector{UInt8}(codeunits(s)) diff --git a/base/subarray.jl b/base/subarray.jl index b67b917a478b3..d7011c50421c0 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -424,7 +424,8 @@ substrides(strds::Tuple{}, ::Tuple{}) = () substrides(strds::NTuple{N,Int}, I::Tuple{ScalarIndex, Vararg{Any}}) where N = (substrides(tail(strds), tail(I))...,) substrides(strds::NTuple{N,Int}, I::Tuple{Slice, Vararg{Any}}) where N = (first(strds), substrides(tail(strds), tail(I))...) substrides(strds::NTuple{N,Int}, I::Tuple{AbstractRange, Vararg{Any}}) where N = (first(strds)*step(I[1]), substrides(tail(strds), tail(I))...) -substrides(strds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("strides is invalid for SubArrays with indices of type $(typeof(I[1]))")) +substrides(strds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError( + LazyString("strides is invalid for SubArrays with indices of type ", typeof(I[1])))) stride(V::SubArray, d::Integer) = d <= ndims(V) ? strides(V)[d] : strides(V)[end] * size(V)[end] @@ -436,7 +437,7 @@ compute_stride1(s, inds, I::Tuple{ScalarIndex, Vararg{Any}}) = (@inline; compute_stride1(s*length(inds[1]), tail(inds), tail(I))) compute_stride1(s, inds, I::Tuple{AbstractRange, Vararg{Any}}) = s*step(I[1]) compute_stride1(s, inds, I::Tuple{Slice, Vararg{Any}}) = s -compute_stride1(s, inds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError("invalid strided index type $(typeof(I[1]))")) +compute_stride1(s, inds, I::Tuple{Any, Vararg{Any}}) = throw(ArgumentError(LazyString("invalid strided index type ", typeof(I[1])))) elsize(::Type{<:SubArray{<:Any,<:Any,P}}) where {P} = elsize(P) diff --git a/base/summarysize.jl b/base/summarysize.jl index 2505824768099..4f2646c7641b7 100644 --- a/base/summarysize.jl +++ b/base/summarysize.jl @@ -8,6 +8,9 @@ struct SummarySize chargeall::Any end +nth_pointer_isdefined(obj, i::Int) = ccall(:jl_nth_pointer_isdefined, Cint, (Any, Csize_t), obj, i-1) != 0 +get_nth_pointer(obj, i::Int) = ccall(:jl_get_nth_pointer, Any, (Any, Csize_t), obj, i-1) + """ Base.summarysize(obj; exclude=Union{...}, chargeall=Union{...}) -> Int @@ -26,7 +29,7 @@ julia> Base.summarysize(1.0) 8 julia> Base.summarysize(Ref(rand(100))) -864 +848 julia> sizeof(Ref(rand(100))) 8 @@ -50,15 +53,28 @@ function summarysize(obj; val = x[i] end elseif isa(x, GenericMemory) - nf = length(x) - if @inbounds @inline isassigned(x, i) - val = x[i] + T = eltype(x) + if Base.allocatedinline(T) + np = datatype_npointers(T) + nf = length(x) * np + idx = (i-1) ÷ np + 1 + if @inbounds @inline isassigned(x, idx) + elt = x[idx] + p = (i-1) % np + 1 + if nth_pointer_isdefined(elt, p) + val = get_nth_pointer(elt, p) + end + end + else + nf = length(x) + if @inbounds @inline isassigned(x, i) + val = x[i] + end end else - nf = nfields(x) - ft = typeof(x).types - if !isbitstype(ft[i]) && isdefined(x, i) - val = getfield(x, i) + nf = datatype_npointers(typeof(x)) + if nth_pointer_isdefined(x, i) + val = get_nth_pointer(x, i) end end if nf > i @@ -82,7 +98,7 @@ end # and so is somewhat approximate. key = ccall(:jl_value_ptr, Ptr{Cvoid}, (Any,), obj) haskey(ss.seen, key) ? (return 0) : (ss.seen[key] = true) - if nfields(obj) > 0 + if datatype_npointers(typeof(obj)) > 0 push!(ss.frontier_x, obj) push!(ss.frontier_i, 1) end diff --git a/base/task.jl b/base/task.jl index ba96d7bca5095..553b0a94f098b 100644 --- a/base/task.jl +++ b/base/task.jl @@ -115,6 +115,13 @@ end Wrap an expression in a [`Task`](@ref) without executing it, and return the [`Task`](@ref). This only creates a task, and does not run it. +!!! warning + By default tasks will have the sticky bit set to true `t.sticky`. This models the + historic default for [`@async`](@ref). Sticky tasks can only be run on the worker thread + they are first scheduled on, and when scheduled will make the task that they were scheduled + from sticky. To obtain the behavior of [`Threads.@spawn`](@ref) set the sticky + bit manually to `false`. + # Examples ```jldoctest julia> a1() = sum(i for i in 1:1000); @@ -839,6 +846,13 @@ the woken task. It is incorrect to use `schedule` on an arbitrary `Task` that has already been started. See [the API reference](@ref low-level-schedule-wait) for more information. +!!! warning + By default tasks will have the sticky bit set to true `t.sticky`. This models the + historic default for [`@async`](@ref). Sticky tasks can only be run on the worker thread + they are first scheduled on, and when scheduled will make the task that they were scheduled + from sticky. To obtain the behavior of [`Threads.@spawn`](@ref) set the sticky + bit manually to `false`. + # Examples ```jldoctest julia> a5() = sum(i for i in 1:1000); diff --git a/base/toml_parser.jl b/base/toml_parser.jl index eb7ffda2f5940..1a72e4e7d4bd2 100644 --- a/base/toml_parser.jl +++ b/base/toml_parser.jl @@ -9,8 +9,8 @@ module TOML using Base: IdSet -# In case we do not have the Dates stdlib available # we parse DateTime into these internal structs, +# unless a different DateTime library is passed to the Parser constructor # note that these do not do any argument checking struct Date year::Int @@ -85,12 +85,10 @@ mutable struct Parser # Filled in in case we are parsing a file to improve error messages filepath::Union{String, Nothing} - # Gets populated with the Dates stdlib if it exists + # Optionally populate with the Dates stdlib to change the type of Date types returned Dates::Union{Module, Nothing} end -const DATES_PKGID = Base.PkgId(Base.UUID("ade2ca70-3891-5945-98fb-dc099432e06a"), "Dates") - function Parser(str::String; filepath=nothing) root = TOMLDict() l = Parser( @@ -109,7 +107,7 @@ function Parser(str::String; filepath=nothing) IdSet{TOMLDict}(), # defined_tables root, filepath, - isdefined(Base, :maybe_root_module) ? Base.maybe_root_module(DATES_PKGID) : nothing, + nothing ) startup(l) return l @@ -151,8 +149,6 @@ end # Errors # ########## -throw_internal_error(msg) = error("internal TOML parser error: $msg") - # Many functions return a ParserError. We want this to bubble up # all the way and have this error be returned to the user # if the parse is called with `raise=false`. This macro @@ -854,7 +850,7 @@ function parse_number_or_date_start(l::Parser) ate, contains_underscore = @try accept_batch_underscore(l, isdigit, readed_zero) read_underscore |= contains_underscore if (read_digit || ate) && ok_end_value(peek(l)) - return parse_int(l, contains_underscore) + return parse_integer(l, contains_underscore) end # Done with integers here @@ -900,11 +896,22 @@ end function parse_float(l::Parser, contains_underscore)::Err{Float64} s = take_string_or_substring(l, contains_underscore) v = Base.tryparse(Float64, s) - v === nothing && return(ParserError(ErrGenericValueError)) + v === nothing && return ParserError(ErrGenericValueError) + return v +end + +function parse_int(l::Parser, contains_underscore, base=nothing)::Err{Int64} + s = take_string_or_substring(l, contains_underscore) + v = try + Base.parse(Int64, s; base=base) + catch e + e isa Base.OverflowError && return ParserError(ErrOverflowError) + rethrow() + end return v end -for (name, T1, T2, n1, n2) in (("int", Int64, Int128, 17, 33), +for (name, T1, T2, n1, n2) in (("integer", Int64, Int128, 17, 33), ("hex", UInt64, UInt128, 18, 34), ("oct", UInt64, UInt128, 24, 45), ("bin", UInt64, UInt128, 66, 130), @@ -921,8 +928,8 @@ for (name, T1, T2, n1, n2) in (("int", Int64, Int128, 17, 33), Base.parse(BigInt, s; base) end catch e - e isa Base.OverflowError && return(ParserError(ErrOverflowError)) - error("internal parser error: did not correctly discredit $(repr(s)) as an int") + e isa Base.OverflowError && return ParserError(ErrOverflowError) + rethrow() end return v end @@ -1019,8 +1026,9 @@ function try_return_datetime(p, year, month, day, h, m, s, ms) if Dates !== nothing try return Dates.DateTime(year, month, day, h, m, s, ms) - catch - return ParserError(ErrParsingDateTime) + catch ex + ex isa ArgumentError && return ParserError(ErrParsingDateTime) + rethrow() end else return DateTime(year, month, day, h, m, s, ms) @@ -1032,8 +1040,9 @@ function try_return_date(p, year, month, day) if Dates !== nothing try return Dates.Date(year, month, day) - catch - return ParserError(ErrParsingDateTime) + catch ex + ex isa ArgumentError && return ParserError(ErrParsingDateTime) + rethrow() end else return Date(year, month, day) @@ -1054,8 +1063,9 @@ function try_return_time(p, h, m, s, ms) if Dates !== nothing try return Dates.Time(h, m, s, ms) - catch - return ParserError(ErrParsingDateTime) + catch ex + ex isa ArgumentError && return ParserError(ErrParsingDateTime) + rethrow() end else return Time(h, m, s, ms) @@ -1103,7 +1113,7 @@ function _parse_local_time(l::Parser, skip_hour=false)::Err{NTuple{4, Int64}} end # DateTime in base only manages 3 significant digits in fractional # second - fractional_second = parse_int(l, false) + fractional_second = parse_int(l, false)::Int64 # Truncate off the rest eventual digits accept_batch(l, isdigit) end diff --git a/base/tuple.jl b/base/tuple.jl index 3e39e8d63cb86..5ab5b4b1c7a26 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -76,8 +76,8 @@ keys(@nospecialize t::Tuple) = OneTo(length(t)) """ prevind(A, i) -Return the index before `i` in `A`. The returned index is often equivalent to `i -- 1` for an integer `i`. This function can be useful for generic code. +Return the index before `i` in `A`. The returned index is often equivalent to +`i - 1` for an integer `i`. This function can be useful for generic code. !!! warning The returned index might be out of bounds. Consider using @@ -110,8 +110,8 @@ function prevind end """ nextind(A, i) -Return the index after `i` in `A`. The returned index is often equivalent to `i -+ 1` for an integer `i`. This function can be useful for generic code. +Return the index after `i` in `A`. The returned index is often equivalent to +`i + 1` for an integer `i`. This function can be useful for generic code. !!! warning The returned index might be out of bounds. Consider using @@ -458,7 +458,7 @@ _totuple(::Type{Tuple{}}, itr, s...) = () function _totuple_err(@nospecialize T) @noinline - throw(ArgumentError("too few elements for tuple type $T")) + throw(ArgumentError(LazyString("too few elements for tuple type ", T))) end function _totuple(::Type{T}, itr, s::Vararg{Any,N}) where {T,N} diff --git a/cli/Makefile b/cli/Makefile index 4e32c53b9a6f0..8b46067820d85 100644 --- a/cli/Makefile +++ b/cli/Makefile @@ -17,8 +17,7 @@ LOADER_CFLAGS += -DGLIBCXX_LEAST_VERSION_SYMBOL=\"$(shell echo "$(CSL_NEXT_GLIBC endif ifeq ($(OS),WINNT) -LOADER_LDFLAGS += -municode -mconsole -nostdlib --disable-auto-import \ - --disable-runtime-pseudo-reloc -lntdll -lkernel32 -lpsapi +LOADER_LDFLAGS += -municode -mconsole -nostdlib -lntdll -lkernel32 -lpsapi else ifeq ($(OS),Linux) # textoff and notext are aliases to the same option which suppress the TEXTREL warning for i686 LOADER_LDFLAGS += -Wl,--no-as-needed -ldl -lpthread -rdynamic -lc -Wl,--as-needed -Wl,-z,notext diff --git a/cli/loader_lib.c b/cli/loader_lib.c index 02030cf2717a5..9f52249362bf7 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -125,6 +125,32 @@ static void * lookup_symbol(const void * lib_handle, const char * symbol_name) { #endif } +#if defined(_OS_WINDOWS_) +void win32_formatmessage(DWORD code, char *reason, int len) { + DWORD res; + LPWSTR errmsg; + res = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, code, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPWSTR)&errmsg, 0, NULL); + if (!res && (GetLastError() == ERROR_MUI_FILE_NOT_FOUND || + GetLastError() == ERROR_RESOURCE_TYPE_NOT_FOUND)) { + res = FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS | + FORMAT_MESSAGE_MAX_WIDTH_MASK, + NULL, code, + 0, (LPWSTR)&errmsg, 0, NULL); + } + res = WideCharToMultiByte(CP_UTF8, 0, errmsg, -1, reason, len, NULL, NULL); + reason[len - 1] = '\0'; + LocalFree(errmsg); +} +#endif + // Find the location of libjulia. char *lib_dir = NULL; JL_DLLEXPORT const char * jl_get_libdir() @@ -135,21 +161,21 @@ JL_DLLEXPORT const char * jl_get_libdir() } #if defined(_OS_WINDOWS_) // On Windows, we use GetModuleFileNameW - wchar_t *libjulia_path = utf8_to_wchar(LIBJULIA_NAME); HMODULE libjulia = NULL; - // Get a handle to libjulia. - if (!libjulia_path) { - jl_loader_print_stderr3("ERROR: Unable to convert path ", LIBJULIA_NAME, " to wide string!\n"); - exit(1); - } - libjulia = LoadLibraryW(libjulia_path); - if (libjulia == NULL) { - jl_loader_print_stderr3("ERROR: Unable to load ", LIBJULIA_NAME, "!\n"); + // Get a handle to libjulia + if (!GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCWSTR)jl_get_libdir, &libjulia)) { + DWORD err = GetLastError(); + jl_loader_print_stderr3("ERROR: could not locate library \"", LIBJULIA_NAME, "\"\n"); + + char msg[2048]; + win32_formatmessage(err, msg, sizeof(msg)); + jl_loader_print_stderr(msg); exit(1); } - free(libjulia_path); - libjulia_path = (wchar_t*)malloc(32768 * sizeof(wchar_t)); // max long path length + + wchar_t *libjulia_path = (wchar_t*)malloc(32768 * sizeof(wchar_t)); // max long path length if (!GetModuleFileNameW(libjulia, libjulia_path, 32768)) { jl_loader_print_stderr("ERROR: GetModuleFileName() failed\n"); exit(1); diff --git a/cli/trampolines/trampolines_aarch64.S b/cli/trampolines/trampolines_aarch64.S index 2d87ae6dcdb1c..ccb9a647ac6c3 100644 --- a/cli/trampolines/trampolines_aarch64.S +++ b/cli/trampolines/trampolines_aarch64.S @@ -5,9 +5,9 @@ #define XX(name) \ .global CNAME(name) SEP \ +CNAME(name)##: SEP \ .cfi_startproc SEP \ .p2align 2 SEP \ -CNAME(name)##: SEP \ adrp x16, PAGE(CNAME(name##_addr)) SEP \ ldr x16, [x16, PAGEOFF(CNAME(name##_addr))] SEP \ br x16 SEP \ diff --git a/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/md5 b/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/md5 new file mode 100644 index 0000000000000..9904464c82b3b --- /dev/null +++ b/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/md5 @@ -0,0 +1 @@ +390521058a478a131ca49d349c9b9383 diff --git a/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/sha512 b/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/sha512 new file mode 100644 index 0000000000000..a7fbe055c2251 --- /dev/null +++ b/deps/checksums/Distributed-6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781.tar.gz/sha512 @@ -0,0 +1 @@ +7f0f414d94739a25b7d713c46887e26cd349329828d42297f44928204b36d15ba9163ad6f670aba72ed9229557bb0f35ab4686429975d1f349fe12b1ba2b189f diff --git a/deps/checksums/Pkg-280f702c1ace34b51cd4551ed448b19516c36849.tar.gz/md5 b/deps/checksums/Pkg-280f702c1ace34b51cd4551ed448b19516c36849.tar.gz/md5 new file mode 100644 index 0000000000000..4abb92bd80663 --- /dev/null +++ b/deps/checksums/Pkg-280f702c1ace34b51cd4551ed448b19516c36849.tar.gz/md5 @@ -0,0 +1 @@ +abc7137d1f7e982957a605d3290f9e12 diff --git a/deps/checksums/Pkg-280f702c1ace34b51cd4551ed448b19516c36849.tar.gz/sha512 b/deps/checksums/Pkg-280f702c1ace34b51cd4551ed448b19516c36849.tar.gz/sha512 new file mode 100644 index 0000000000000..90c5e7cf45e19 --- /dev/null +++ b/deps/checksums/Pkg-280f702c1ace34b51cd4551ed448b19516c36849.tar.gz/sha512 @@ -0,0 +1 @@ +3dd9b0cf67a2846b23af0c666e4f4780a0dbc7348fa7d0689c2dccbe462a3b06240e3116b8e1c5c89747858d4c72370e0b3823ce635a09144901f338d74cc7fd diff --git a/deps/checksums/Pkg-f112626414a4f666306adb84936ea85aec77c89d.tar.gz/md5 b/deps/checksums/Pkg-f112626414a4f666306adb84936ea85aec77c89d.tar.gz/md5 deleted file mode 100644 index 423607f56c6dc..0000000000000 --- a/deps/checksums/Pkg-f112626414a4f666306adb84936ea85aec77c89d.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -1f7791c24085bfbe03e2ec6df9e3e095 diff --git a/deps/checksums/Pkg-f112626414a4f666306adb84936ea85aec77c89d.tar.gz/sha512 b/deps/checksums/Pkg-f112626414a4f666306adb84936ea85aec77c89d.tar.gz/sha512 deleted file mode 100644 index 8b0db150dfff8..0000000000000 --- a/deps/checksums/Pkg-f112626414a4f666306adb84936ea85aec77c89d.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -0541b8a46366022dddc54fba199619178cd297243ae56b3334b434c22aa8390fd0f50013b0549fb102a9895e091942736bd63b9967aff63d6f02b29d60ec83b0 diff --git a/doc/src/base/arrays.md b/doc/src/base/arrays.md index 20e8e81614b9e..f7dfdd502c845 100644 --- a/doc/src/base/arrays.md +++ b/doc/src/base/arrays.md @@ -30,6 +30,7 @@ Base.StridedArray Base.StridedVector Base.StridedMatrix Base.StridedVecOrMat +Base.GenericMemory Base.Memory Base.MemoryRef Base.Slices @@ -139,6 +140,7 @@ Base.reshape Base.dropdims Base.vec Base.SubArray +Base.wrap ``` ## Concatenation and permutation diff --git a/doc/src/manual/embedding.md b/doc/src/manual/embedding.md index 9df9a6c198003..f578e10764101 100644 --- a/doc/src/manual/embedding.md +++ b/doc/src/manual/embedding.md @@ -412,7 +412,7 @@ per pointer using ```c jl_module_t *mod = jl_main_module; jl_sym_t *var = jl_symbol("var"); -jl_binding_t *bp = jl_get_binding_wr(mod, var); +jl_binding_t *bp = jl_get_binding_wr(mod, var, 1); jl_checked_assignment(bp, mod, var, val); ``` diff --git a/src/Makefile b/src/Makefile index 5f58c7fa57627..f1c71c28a7265 100644 --- a/src/Makefile +++ b/src/Makefile @@ -126,8 +126,8 @@ ifeq ($(JULIACODEGEN),LLVM) ifneq ($(USE_SYSTEM_LLVM),0) # USE_SYSTEM_LLVM != 0 CG_LLVMLINK += $(LLVM_LDFLAGS) $(shell $(LLVM_CONFIG_HOST) --libs --system-libs) -LLVM_SHLIB_SYMBOL_VERSION := $(shell nm -D --with-symbol-versions $(shell $(LLVM_CONFIG_HOST) --libfiles --link-shared | awk '{print $1; exit}') | \ - grep _ZN4llvm3Any6TypeId | head -n 1 | sed -e 's/.*@//') +LLVM_SHLIB_SYMBOL_VERSION := $(shell readelf -W --dyn-syms $(shell $(LLVM_CONFIG_HOST) --libfiles --link-shared | awk '{print $1; exit}') | \ + grep _ZN4llvm3Any6TypeId | head -n 1 | sed -ne 's/.*@//p') # HACK: llvm-config doesn't correctly point to shared libs on all platforms # https://github.com/JuliaLang/julia/issues/29981 diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index c84ef4e756cb0..97734852f9479 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -1242,6 +1242,8 @@ static void materializePreserved(Module &M, Partition &partition) { GV.setInitializer(nullptr); GV.setLinkage(GlobalValue::ExternalLinkage); GV.setVisibility(GlobalValue::HiddenVisibility); + if (GV.getDLLStorageClass() != GlobalValue::DLLStorageClassTypes::DefaultStorageClass) + continue; // Don't mess with exported or imported globals GV.setDSOLocal(true); } @@ -1773,6 +1775,7 @@ void jl_dump_native_impl(void *native_code, if (jl_small_typeof_copy) { jl_small_typeof_copy->setVisibility(GlobalValue::HiddenVisibility); jl_small_typeof_copy->setDSOLocal(true); + jl_small_typeof_copy->setDLLStorageClass(GlobalValue::DLLStorageClassTypes::DefaultStorageClass); } } @@ -1804,16 +1807,18 @@ void jl_dump_native_impl(void *native_code, // reflect the address of the jl_RTLD_DEFAULT_handle variable // back to the caller, so that we can check for consistency issues GlobalValue *jlRTLD_DEFAULT_var = jl_emit_RTLD_DEFAULT_var(&metadataM); - addComdat(new GlobalVariable(metadataM, - jlRTLD_DEFAULT_var->getType(), - true, - GlobalVariable::ExternalLinkage, - jlRTLD_DEFAULT_var, - "jl_RTLD_DEFAULT_handle_pointer"), TheTriple); Type *T_size = DL.getIntPtrType(Context); Type *T_psize = T_size->getPointerTo(); + auto FT = FunctionType::get(Type::getInt8Ty(Context)->getPointerTo()->getPointerTo(), {}, false); + auto F = Function::Create(FT, Function::ExternalLinkage, "get_jl_RTLD_DEFAULT_handle_addr", metadataM); + llvm::IRBuilder<> builder(BasicBlock::Create(Context, "top", F)); + builder.CreateRet(jlRTLD_DEFAULT_var); + F->setLinkage(GlobalValue::ExternalLinkage); + if (TheTriple.isOSBinFormatCOFF()) + F->setDLLStorageClass(GlobalValue::DLLStorageClassTypes::DLLExportStorageClass); + if (TheTriple.isOSWindows()) { // Windows expect that the function `_DllMainStartup` is present in an dll. // Normal compilers use something like Zig's crtdll.c instead we provide a diff --git a/src/builtins.c b/src/builtins.c index 85f5ec206d795..37ea8d18b2340 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -1355,7 +1355,7 @@ JL_CALLABLE(jl_f_setglobal) jl_atomic_error("setglobal!: module binding cannot be written non-atomically"); else if (order >= jl_memory_order_seq_cst) jl_fence(); - jl_binding_t *b = jl_get_binding_wr(mod, var); + jl_binding_t *b = jl_get_binding_wr(mod, var, 0); jl_checked_assignment(b, mod, var, args[2]); // release store if (order >= jl_memory_order_seq_cst) jl_fence(); @@ -1393,7 +1393,7 @@ JL_CALLABLE(jl_f_set_binding_type) JL_TYPECHK(set_binding_type!, symbol, (jl_value_t*)s); jl_value_t *ty = nargs == 2 ? (jl_value_t*)jl_any_type : args[2]; JL_TYPECHK(set_binding_type!, type, ty); - jl_binding_t *b = jl_get_binding_wr(m, s); + jl_binding_t *b = jl_get_binding_wr(m, s, 0); jl_value_t *old_ty = NULL; if (jl_atomic_cmpswap_relaxed(&b->ty, &old_ty, ty)) { jl_gc_wb(b, ty); @@ -1420,7 +1420,7 @@ JL_CALLABLE(jl_f_swapglobal) if (order == jl_memory_order_notatomic) jl_atomic_error("swapglobal!: module binding cannot be written non-atomically"); // is seq_cst already, no fence needed - jl_binding_t *b = jl_get_binding_wr(mod, var); + jl_binding_t *b = jl_get_binding_wr(mod, var, 0); return jl_checked_swap(b, mod, var, args[2]); } @@ -1438,7 +1438,7 @@ JL_CALLABLE(jl_f_modifyglobal) JL_TYPECHK(modifyglobal!, symbol, (jl_value_t*)var); if (order == jl_memory_order_notatomic) jl_atomic_error("modifyglobal!: module binding cannot be written non-atomically"); - jl_binding_t *b = jl_get_binding_wr(mod, var); + jl_binding_t *b = jl_get_binding_wr(mod, var, 0); // is seq_cst already, no fence needed return jl_checked_modify(b, mod, var, args[2], args[3]); } @@ -1467,7 +1467,7 @@ JL_CALLABLE(jl_f_replaceglobal) jl_atomic_error("replaceglobal!: module binding cannot be written non-atomically"); if (failure_order == jl_memory_order_notatomic) jl_atomic_error("replaceglobal!: module binding cannot be accessed non-atomically"); - jl_binding_t *b = jl_get_binding_wr(mod, var); + jl_binding_t *b = jl_get_binding_wr(mod, var, 0); // is seq_cst already, no fence needed return jl_checked_replace(b, mod, var, args[2], args[3]); } @@ -1496,7 +1496,7 @@ JL_CALLABLE(jl_f_setglobalonce) jl_atomic_error("setglobalonce!: module binding cannot be written non-atomically"); if (failure_order == jl_memory_order_notatomic) jl_atomic_error("setglobalonce!: module binding cannot be accessed non-atomically"); - jl_binding_t *b = jl_get_binding_wr(mod, var); + jl_binding_t *b = jl_get_binding_wr(mod, var, 0); // is seq_cst already, no fence needed jl_value_t *old = jl_checked_assignonce(b, mod, var, args[2]); return old == NULL ? jl_true : jl_false; diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 998a8421c79f0..9fd7a368faf39 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -2740,7 +2740,7 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st else { ptindex = emit_struct_gep(ctx, cast(lt), staddr, byte_offset + fsz1); } - auto val = emit_unionload(ctx, addr, ptindex, jfty, fsz, al, tbaa, !jl_field_isconst(jt, idx), union_max, ctx.tbaa().tbaa_unionselbyte); + auto val = emit_unionload(ctx, addr, ptindex, jfty, fsz, al, tbaa, !jl_field_isconst(jt, idx), union_max, strct.tbaa); if (val.V && val.V != addr) { setNameWithField(ctx.emission_context, val.V, get_objname, jt, idx, Twine()); } diff --git a/src/codegen.cpp b/src/codegen.cpp index cb1957abf9e04..ad13cf8d1124e 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -536,9 +536,12 @@ struct JuliaVariable { if (GlobalValue *V = m->getNamedValue(name)) return cast(V); auto T_size = m->getDataLayout().getIntPtrType(m->getContext()); - return new GlobalVariable(*m, _type(T_size), + auto var = new GlobalVariable(*m, _type(T_size), isconst, GlobalVariable::ExternalLinkage, NULL, name); + if (Triple(m->getTargetTriple()).isOSWindows()) + var->setDLLStorageClass(GlobalValue::DLLStorageClassTypes::DLLImportStorageClass); // Cross-library imports must be explicit for COFF (Windows) + return var; } GlobalVariable *realize(jl_codectx_t &ctx); }; @@ -937,7 +940,7 @@ static const auto jlgetbindingwrorerror_func = new JuliaFunction<>{ [](LLVMContext &C) { auto T_pjlvalue = JuliaType::get_pjlvalue_ty(C); return FunctionType::get(T_pjlvalue, - {T_pjlvalue, T_pjlvalue}, false); + {T_pjlvalue, T_pjlvalue, getInt32Ty(C)}, false); }, nullptr, }; @@ -2084,7 +2087,7 @@ static Type *julia_type_to_llvm(jl_codectx_t &ctx, jl_value_t *jt, bool *isboxed static jl_returninfo_t get_specsig_function(jl_codectx_t &ctx, Module *M, Value *fval, StringRef name, jl_value_t *sig, jl_value_t *jlrettype, bool is_opaque_closure, bool gcstack_arg, BitVector *used_arguments=nullptr, size_t *args_begin=nullptr); static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaval = -1); static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t *s, - jl_binding_t **pbnd, bool assign); + jl_binding_t **pbnd, bool assign, bool alloc); static jl_cgval_t emit_checked_var(jl_codectx_t &ctx, Value *bp, jl_sym_t *name, jl_value_t *scope, bool isvol, MDNode *tbaa); static jl_cgval_t emit_sparam(jl_codectx_t &ctx, size_t i); static Value *emit_condition(jl_codectx_t &ctx, const jl_cgval_t &condV, const Twine &msg); @@ -2129,9 +2132,6 @@ static inline GlobalVariable *prepare_global_in(Module *M, GlobalVariable *G) proto->setInitializer(G->getInitializer()); } proto->copyAttributesFrom(G); - // DLLImport only needs to be set for the shadow module - // it just gets annoying in the JIT - proto->setDLLStorageClass(GlobalValue::DefaultStorageClass); return proto; } return cast(local); @@ -3176,7 +3176,7 @@ static jl_value_t *jl_ensure_rooted(jl_codectx_t &ctx, jl_value_t *val) static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *name, AtomicOrdering order) { jl_binding_t *bnd = NULL; - Value *bp = global_binding_pointer(ctx, mod, name, &bnd, false); + Value *bp = global_binding_pointer(ctx, mod, name, &bnd, false, false); if (bp == NULL) return jl_cgval_t(); bp = julia_binding_pvalue(ctx, bp); @@ -3195,10 +3195,10 @@ static jl_cgval_t emit_globalref(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t * static jl_cgval_t emit_globalop(jl_codectx_t &ctx, jl_module_t *mod, jl_sym_t *sym, jl_cgval_t rval, const jl_cgval_t &cmp, AtomicOrdering Order, AtomicOrdering FailOrder, bool issetglobal, bool isreplaceglobal, bool isswapglobal, bool ismodifyglobal, bool issetglobalonce, - const jl_cgval_t *modifyop) + const jl_cgval_t *modifyop, bool alloc) { jl_binding_t *bnd = NULL; - Value *bp = global_binding_pointer(ctx, mod, sym, &bnd, true); + Value *bp = global_binding_pointer(ctx, mod, sym, &bnd, true, alloc); if (bp == NULL) return jl_cgval_t(); if (bnd && !bnd->constp) { @@ -3608,7 +3608,8 @@ static bool emit_f_opglobal(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, isswapglobal, ismodifyglobal, issetglobalonce, - modifyop); + modifyop, + false); return true; } } @@ -5289,7 +5290,7 @@ static void emit_hasnofield_error_ifnot(jl_codectx_t &ctx, Value *ok, jl_sym_t * // if the reference currently bound or assign == true, // pbnd will also be assigned with the binding address static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t *s, - jl_binding_t **pbnd, bool assign) + jl_binding_t **pbnd, bool assign, bool alloc) { jl_binding_t *b = jl_get_module_binding(m, s, 1); if (assign) { @@ -5319,9 +5320,17 @@ static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t ctx.builder.CreateCondBr(iscached, have_val, not_found); not_found->insertInto(ctx.f); ctx.builder.SetInsertPoint(not_found); - Value *bval = ctx.builder.CreateCall(prepare_call(assign ? jlgetbindingwrorerror_func : jlgetbindingorerror_func), - { literal_pointer_val(ctx, (jl_value_t*)m), - literal_pointer_val(ctx, (jl_value_t*)s) }); + Value *bval = nullptr; + if (assign) { + bval = ctx.builder.CreateCall(prepare_call(jlgetbindingwrorerror_func), + { literal_pointer_val(ctx, (jl_value_t*)m), + literal_pointer_val(ctx, (jl_value_t*)s), + ConstantInt::get(getInt32Ty(ctx.builder.getContext()), alloc)}); + } else { + bval = ctx.builder.CreateCall(prepare_call(jlgetbindingorerror_func), + { literal_pointer_val(ctx, (jl_value_t*)m), + literal_pointer_val(ctx, (jl_value_t*)s)}); + } setName(ctx.emission_context, bval, jl_symbol_name(m->name) + StringRef(".") + jl_symbol_name(s) + ".found"); ctx.builder.CreateAlignedStore(bval, bindinggv, Align(sizeof(void*)))->setOrdering(AtomicOrdering::Release); ctx.builder.CreateBr(have_val); @@ -5338,7 +5347,8 @@ static Value *global_binding_pointer(jl_codectx_t &ctx, jl_module_t *m, jl_sym_t // this will fail at runtime, so defer to the runtime to create the error ctx.builder.CreateCall(prepare_call(jlgetbindingwrorerror_func), { literal_pointer_val(ctx, (jl_value_t*)m), - literal_pointer_val(ctx, (jl_value_t*)s) }); + literal_pointer_val(ctx, (jl_value_t*)s), + ConstantInt::get(getInt32Ty(ctx.builder.getContext()), alloc) }); CreateTrap(ctx.builder); return NULL; } @@ -5833,17 +5843,20 @@ static void emit_assignment(jl_codectx_t &ctx, jl_value_t *l, jl_value_t *r, ssi jl_module_t *mod; jl_sym_t *sym; + bool toplevel = jl_is_module(ctx.linfo->def.value); + bool alloc = toplevel; if (jl_is_symbol(l)) { mod = ctx.module; sym = (jl_sym_t*)l; } else { assert(jl_is_globalref(l)); + alloc &= jl_globalref_mod(l) == ctx.module; mod = jl_globalref_mod(l); sym = jl_globalref_name(l); } emit_globalop(ctx, mod, sym, rval_info, jl_cgval_t(), AtomicOrdering::Release, AtomicOrdering::NotAtomic, - true, false, false, false, false, nullptr); + true, false, false, false, false, nullptr, alloc); // Global variable. Does not need debug info because the debugger knows about // its memory location. } @@ -6288,7 +6301,7 @@ static jl_cgval_t emit_expr(jl_codectx_t &ctx, jl_value_t *expr, ssize_t ssaidx_ } if (jl_is_symbol(sym)) { jl_binding_t *bnd = NULL; - Value *bp = global_binding_pointer(ctx, mod, sym, &bnd, true); + Value *bp = global_binding_pointer(ctx, mod, sym, &bnd, true, true); if (bp) ctx.builder.CreateCall(prepare_call(jldeclareconst_func), { bp, literal_pointer_val(ctx, (jl_value_t*)mod), literal_pointer_val(ctx, (jl_value_t*)sym) }); diff --git a/src/datatype.c b/src/datatype.c index f9f7dec3d1b13..51115c032118c 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -2246,6 +2246,39 @@ JL_DLLEXPORT size_t jl_get_field_offset(jl_datatype_t *ty, int field) return jl_field_offset(ty, field - 1); } +jl_value_t *get_nth_pointer(jl_value_t *v, size_t i) +{ + jl_datatype_t *dt = (jl_datatype_t*)jl_typeof(v); + const jl_datatype_layout_t *ly = dt->layout; + uint32_t npointers = ly->npointers; + if (i >= npointers) + jl_bounds_error_int(v, i); + const uint8_t *ptrs8 = (const uint8_t *)jl_dt_layout_ptrs(ly); + const uint16_t *ptrs16 = (const uint16_t *)jl_dt_layout_ptrs(ly); + const uint32_t *ptrs32 = (const uint32_t*)jl_dt_layout_ptrs(ly); + uint32_t fld; + if (ly->flags.fielddesc_type == 0) + fld = ptrs8[i]; + else if (ly->flags.fielddesc_type == 1) + fld = ptrs16[i]; + else + fld = ptrs32[i]; + return jl_atomic_load_relaxed((_Atomic(jl_value_t*)*)(&((jl_value_t**)v)[fld])); +} + +JL_DLLEXPORT jl_value_t *jl_get_nth_pointer(jl_value_t *v, size_t i) +{ + jl_value_t *ptrf = get_nth_pointer(v, i); + if (__unlikely(ptrf == NULL)) + jl_throw(jl_undefref_exception); + return ptrf; +} + +JL_DLLEXPORT int jl_nth_pointer_isdefined(jl_value_t *v, size_t i) +{ + return get_nth_pointer(v, i) != NULL; +} + #ifdef __cplusplus } #endif diff --git a/src/gc-stacks.c b/src/gc-stacks.c index 0318162289f11..3f40e2fc15d81 100644 --- a/src/gc-stacks.c +++ b/src/gc-stacks.c @@ -22,13 +22,22 @@ // number of stacks to always keep available per pool #define MIN_STACK_MAPPINGS_PER_POOL 5 +#if defined(_OS_WINDOWS_) || (!defined(_OS_OPENBSD_) && !defined(JL_HAVE_UCONTEXT) && !defined(JL_HAVE_SIGALTSTACK)) +#define JL_USE_GUARD_PAGE 1 const size_t jl_guard_size = (4096 * 8); +#else +const size_t jl_guard_size = 0; +#endif + static _Atomic(uint32_t) num_stack_mappings = 0; #ifdef _OS_WINDOWS_ #define MAP_FAILED NULL static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT { + size_t guard_size = LLT_ALIGN(jl_guard_size, jl_page_size); + bufsz += guard_size; + void *stk = VirtualAlloc(NULL, bufsz, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (stk == NULL) return MAP_FAILED; @@ -37,6 +46,8 @@ static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT VirtualFree(stk, 0, MEM_RELEASE); return MAP_FAILED; } + stk = (char *)stk + guard_size; + jl_atomic_fetch_add(&num_stack_mappings, 1); return stk; } @@ -44,6 +55,12 @@ static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT static void free_stack(void *stkbuf, size_t bufsz) { +#ifdef JL_USE_GUARD_PAGE + size_t guard_size = LLT_ALIGN(jl_guard_size, jl_page_size); + bufsz += guard_size; + stkbuf = (char *)stkbuf - guard_size; +#endif + VirtualFree(stkbuf, 0, MEM_RELEASE); jl_atomic_fetch_add(&num_stack_mappings, -1); } @@ -52,15 +69,22 @@ static void free_stack(void *stkbuf, size_t bufsz) static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT { +#ifdef JL_USE_GUARD_PAGE + size_t guard_size = LLT_ALIGN(jl_guard_size, jl_page_size); + bufsz += guard_size; +#endif + void* stk = mmap(0, bufsz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (stk == MAP_FAILED) return MAP_FAILED; -#if !defined(JL_HAVE_UCONTEXT) && !defined(JL_HAVE_SIGALTSTACK) - // setup a guard page to detect stack overflow + +#ifdef JL_USE_GUARD_PAGE + // set up a guard page to detect stack overflow if (mprotect(stk, jl_guard_size, PROT_NONE) == -1) { munmap(stk, bufsz); return MAP_FAILED; } + stk = (char *)stk + guard_size; #endif jl_atomic_fetch_add(&num_stack_mappings, 1); return stk; @@ -68,6 +92,12 @@ static void *malloc_stack(size_t bufsz) JL_NOTSAFEPOINT static void free_stack(void *stkbuf, size_t bufsz) { +#ifdef JL_USE_GUARD_PAGE + size_t guard_size = LLT_ALIGN(jl_guard_size, jl_page_size); + bufsz += guard_size; + stkbuf = (char *)stkbuf - guard_size; +#endif + munmap(stkbuf, bufsz); jl_atomic_fetch_add(&num_stack_mappings, -1); } diff --git a/src/interpreter.c b/src/interpreter.c index c80986cd7ded0..b65950519ebc0 100644 --- a/src/interpreter.c +++ b/src/interpreter.c @@ -568,9 +568,13 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, else { jl_module_t *modu; jl_sym_t *sym; + // Plain assignment is allowed to create bindings at + // toplevel and only for the current module + int alloc = toplevel; if (jl_is_globalref(lhs)) { modu = jl_globalref_mod(lhs); sym = jl_globalref_name(lhs); + alloc &= modu == s->module; } else { assert(jl_is_symbol(lhs)); @@ -578,7 +582,7 @@ static jl_value_t *eval_body(jl_array_t *stmts, interpreter_state *s, size_t ip, sym = (jl_sym_t*)lhs; } JL_GC_PUSH1(&rhs); - jl_binding_t *b = jl_get_binding_wr(modu, sym); + jl_binding_t *b = jl_get_binding_wr(modu, sym, alloc); jl_checked_assignment(b, modu, sym, rhs); JL_GC_POP(); } diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index 3a98850ddca68..aacf1e495beff 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -628,10 +628,21 @@ static jl_cgval_t generic_bitcast(jl_codectx_t &ctx, ArrayRef 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"); + } 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/jl_exported_funcs.inc b/src/jl_exported_funcs.inc index db23bcbc33f6c..5ecff60813a4f 100644 --- a/src/jl_exported_funcs.inc +++ b/src/jl_exported_funcs.inc @@ -194,6 +194,7 @@ XX(jl_generating_output) \ XX(jl_generic_function_def) \ XX(jl_gensym) \ + XX(jl_getaffinity) \ XX(jl_getallocationgranularity) \ XX(jl_getnameinfo) \ XX(jl_getpagesize) \ @@ -407,6 +408,7 @@ XX(jl_safepoint_suspend_thread) \ XX(jl_safepoint_resume_thread) \ XX(jl_SC_CLK_TCK) \ + XX(jl_setaffinity) \ XX(jl_set_ARGS) \ XX(jl_set_const) \ XX(jl_set_errno) \ diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 7d9f8711ec714..17092ca4cf751 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -4232,6 +4232,7 @@ f(x) = yt(x) (put! globals ref #t) `(block (toplevel-only set_binding_type! ,(cadr e)) + (global ,ref) (call (core set_binding_type!) ,(cadr ref) (inert ,(caddr ref)) ,(caddr e)))) `(call (core typeassert) ,@(cdr e)))) fname lam namemap defined toplevel interp opaq globals locals)))) diff --git a/src/julia.h b/src/julia.h index c6b685733ea30..3a7d594562243 100644 --- a/src/julia.h +++ b/src/julia.h @@ -261,11 +261,12 @@ typedef union __jl_purity_overrides_t { uint16_t ipo_inaccessiblememonly : 1; uint16_t ipo_noub : 1; uint16_t ipo_noub_if_noinbounds : 1; + uint16_t ipo_consistent_overlay : 1; } overrides; uint16_t bits; } _jl_purity_overrides_t; -#define NUM_EFFECTS_OVERRIDES 9 +#define NUM_EFFECTS_OVERRIDES 10 #define NUM_IR_FLAGS 12 // This type describes a single function body @@ -439,13 +440,14 @@ typedef struct _jl_code_instance_t { // uint8_t ipo_inaccessiblememonly : 2; _Atomic(uint32_t) purity_bits; // purity_flags: - // uint8_t consistent : 2; + // uint8_t consistent : 3; // uint8_t effect_free : 2; - // uint8_t nothrow : 2; - // uint8_t terminates : 2; - // uint8_t nonoverlayed : 1; - // uint8_t notaskstate : 2; + // uint8_t nothrow : 1; + // uint8_t terminates : 1; + // uint8_t notaskstate : 1; // uint8_t inaccessiblememonly : 2; + // uint8_t noub : 2; + // uint8_t nonoverlayed : 2; jl_value_t *analysis_results; // Analysis results about this code (IPO-safe) // compilation state cache @@ -1913,7 +1915,7 @@ JL_DLLEXPORT jl_binding_t *jl_get_binding_or_error(jl_module_t *m, jl_sym_t *var JL_DLLEXPORT jl_value_t *jl_module_globalref(jl_module_t *m, jl_sym_t *var); JL_DLLEXPORT jl_value_t *jl_get_binding_type(jl_module_t *m, jl_sym_t *var); // get binding for assignment -JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var); +JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, int alloc); JL_DLLEXPORT jl_binding_t *jl_get_binding_for_method_def(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var); JL_DLLEXPORT int jl_boundp(jl_module_t *m, jl_sym_t *var); JL_DLLEXPORT int jl_defines_or_exports_p(jl_module_t *m, jl_sym_t *var); @@ -2339,8 +2341,8 @@ extern int had_exception; __eh_ct = jl_current_task; \ size_t __excstack_state = jl_excstack_state(__eh_ct); \ jl_enter_handler(__eh_ct, &__eh); \ - if (1) - /* TRY BLOCK; */ + for (i__try=1; i__try; i__try=0) + #define JL_CATCH \ if (!had_exception) \ jl_eh_restore_state_noexcept(__eh_ct, &__eh); \ diff --git a/src/julia_internal.h b/src/julia_internal.h index 81888f01cc95e..4050410a1b1b2 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -508,6 +508,8 @@ STATIC_INLINE uint8_t JL_CONST_FUNC jl_gc_szclass_align8(unsigned sz) JL_NOTSAFE #define GC_MAX_SZCLASS (2032-sizeof(void*)) static_assert(ARRAY_CACHE_ALIGN_THRESHOLD > GC_MAX_SZCLASS, ""); + +// Size does NOT include the type tag!! STATIC_INLINE jl_value_t *jl_gc_alloc_(jl_ptls_t ptls, size_t sz, void *ty) { jl_value_t *v; diff --git a/src/julia_threads.h b/src/julia_threads.h index 3f8f5391919b4..e69e7fae9b4d9 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -363,6 +363,9 @@ JL_DLLEXPORT int8_t jl_gc_is_in_finalizer(void) JL_NOTSAFEPOINT; JL_DLLEXPORT void jl_wakeup_thread(int16_t tid); +JL_DLLEXPORT int jl_getaffinity(int16_t tid, char *mask, int cpumasksize); +JL_DLLEXPORT int jl_setaffinity(int16_t tid, char *mask, int cpumasksize); + #ifdef __cplusplus } #endif diff --git a/src/llvm-alloc-opt.cpp b/src/llvm-alloc-opt.cpp index 5df4f52aca425..9638ca7e68c48 100644 --- a/src/llvm-alloc-opt.cpp +++ b/src/llvm-alloc-opt.cpp @@ -402,6 +402,8 @@ void Optimizer::insertLifetime(Value *ptr, Constant *sz, Instruction *orig) auto bb = use->getParent(); if (!bbs.insert(bb).second) continue; + if (pred_empty(bb)) + continue; // No predecessors so the block is dead assert(lifetime_stack.empty()); Lifetime::Frame cur{bb}; while (true) { diff --git a/src/llvm-final-gc-lowering.cpp b/src/llvm-final-gc-lowering.cpp index 5a53ce4d8e510..7349d5b6de927 100644 --- a/src/llvm-final-gc-lowering.cpp +++ b/src/llvm-final-gc-lowering.cpp @@ -211,7 +211,7 @@ void FinalLowerGC::lowerGCAllocBytes(CallInst *target, Function &F) } } else { auto size = builder.CreateZExtOrTrunc(target->getArgOperand(1), T_size); - size = builder.CreateAdd(size, ConstantInt::get(T_size, sizeof(void*))); + // allocTypedFunc does not include the type tag in the allocation size! newI = builder.CreateCall(allocTypedFunc, { ptls, size, type }); derefBytes = sizeof(void*); } diff --git a/src/macroexpand.scm b/src/macroexpand.scm index 424e921a35713..bbc21a081d56d 100644 --- a/src/macroexpand.scm +++ b/src/macroexpand.scm @@ -219,30 +219,26 @@ lst))) ;; get the name from a function formal argument expression, allowing `(escape x)` -(define (try-arg-name v) - (cond ((symbol? v) (list v)) +(define (try-arg-name v (escaped #f)) + (cond ((symbol? v) (if escaped '() (list v))) ((atom? v) '()) (else (case (car v) - ((|::|) (if (length= v 2) '() (try-arg-name (cadr v)))) - ((... kw =) (try-arg-name (cadr v))) - ((escape) (list v)) - ((hygienic-scope) (try-arg-name (cadr v))) + ((|::|) (if (length= v 2) '() (try-arg-name (cadr v) escaped))) + ((... kw =) (try-arg-name (cadr v) escaped)) + ((escape) (if escaped (list (cadr v)) '())) + ((hygienic-scope) (try-arg-name (cadr v) escaped)) + ((tuple) (apply nconc (map (lambda (e) (try-arg-name e escaped)) (cdr v)))) ((meta) ;; allow certain per-argument annotations (if (nospecialize-meta? v #t) - (try-arg-name (caddr v)) + (try-arg-name (caddr v) escaped) '())) (else '()))))) ;; get names from a formal argument list, specifying whether to include escaped ones (define (safe-arg-names lst (escaped #f)) (apply nconc - (map (lambda (v) - (let ((vv (try-arg-name v))) - (if (eq? escaped (and (pair? vv) (pair? (car vv)) (eq? (caar vv) 'escape))) - (if escaped (list (cadar vv)) vv) - '()))) - lst))) + (map (lambda (v) (try-arg-name v escaped)) lst))) ;; arg names, looking only at positional args (define (safe-llist-positional-args lst (escaped #f)) diff --git a/src/method.c b/src/method.c index ac5475d54a4d5..7e6c82b5221b2 100644 --- a/src/method.c +++ b/src/method.c @@ -347,15 +347,27 @@ static void jl_code_info_set_ir(jl_code_info_t *li, jl_expr_t *ir) li->constprop = 2; else if (jl_is_expr(ma) && ((jl_expr_t*)ma)->head == jl_purity_sym) { if (jl_expr_nargs(ma) == NUM_EFFECTS_OVERRIDES) { - li->purity.overrides.ipo_consistent = jl_unbox_bool(jl_exprarg(ma, 0)); - li->purity.overrides.ipo_effect_free = jl_unbox_bool(jl_exprarg(ma, 1)); - li->purity.overrides.ipo_nothrow = jl_unbox_bool(jl_exprarg(ma, 2)); - li->purity.overrides.ipo_terminates_globally = jl_unbox_bool(jl_exprarg(ma, 3)); - li->purity.overrides.ipo_terminates_locally = jl_unbox_bool(jl_exprarg(ma, 4)); - li->purity.overrides.ipo_notaskstate = jl_unbox_bool(jl_exprarg(ma, 5)); - li->purity.overrides.ipo_inaccessiblememonly = jl_unbox_bool(jl_exprarg(ma, 6)); - li->purity.overrides.ipo_noub = jl_unbox_bool(jl_exprarg(ma, 7)); - li->purity.overrides.ipo_noub_if_noinbounds = jl_unbox_bool(jl_exprarg(ma, 8)); + // N.B. this code allows multiple :purity expressions to be present in a single `:meta` node + int8_t consistent = jl_unbox_bool(jl_exprarg(ma, 0)); + if (consistent) li->purity.overrides.ipo_consistent = consistent; + int8_t effect_free = jl_unbox_bool(jl_exprarg(ma, 1)); + if (effect_free) li->purity.overrides.ipo_effect_free = effect_free; + int8_t nothrow = jl_unbox_bool(jl_exprarg(ma, 2)); + if (nothrow) li->purity.overrides.ipo_nothrow = nothrow; + int8_t terminates_globally = jl_unbox_bool(jl_exprarg(ma, 3)); + if (terminates_globally) li->purity.overrides.ipo_terminates_globally = terminates_globally; + int8_t terminates_locally = jl_unbox_bool(jl_exprarg(ma, 4)); + if (terminates_locally) li->purity.overrides.ipo_terminates_locally = terminates_locally; + int8_t notaskstate = jl_unbox_bool(jl_exprarg(ma, 5)); + if (notaskstate) li->purity.overrides.ipo_notaskstate = notaskstate; + int8_t inaccessiblememonly = jl_unbox_bool(jl_exprarg(ma, 6)); + if (inaccessiblememonly) li->purity.overrides.ipo_inaccessiblememonly = inaccessiblememonly; + int8_t noub = jl_unbox_bool(jl_exprarg(ma, 7)); + if (noub) li->purity.overrides.ipo_noub = noub; + int8_t noub_if_noinbounds = jl_unbox_bool(jl_exprarg(ma, 8)); + if (noub_if_noinbounds) li->purity.overrides.ipo_noub_if_noinbounds = noub_if_noinbounds; + int8_t consistent_overlay = jl_unbox_bool(jl_exprarg(ma, 9)); + if (consistent_overlay) li->purity.overrides.ipo_consistent_overlay = consistent_overlay; } } else diff --git a/src/module.c b/src/module.c index 44b89bc236afc..f33c9bc4f31d3 100644 --- a/src/module.c +++ b/src/module.c @@ -219,13 +219,16 @@ static void check_safe_newbinding(jl_module_t *m, jl_sym_t *var) static jl_module_t *jl_binding_dbgmodule(jl_binding_t *b, jl_module_t *m, jl_sym_t *var) JL_GLOBALLY_ROOTED; // get binding for assignment -JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var) +JL_DLLEXPORT jl_binding_t *jl_get_binding_wr(jl_module_t *m JL_PROPAGATES_ROOT, jl_sym_t *var, int alloc) { jl_binding_t *b = jl_get_module_binding(m, var, 1); jl_binding_t *b2 = jl_atomic_load_relaxed(&b->owner); if (b2 != b) { - if (b2 == NULL) + if (b2 == NULL) { check_safe_newbinding(m, var); + if (!alloc) + jl_errorf("Global %s.%s does not exist and cannot be assigned. Declare it using `global` before attempting assignment.", jl_symbol_name(m->name), jl_symbol_name(var)); + } if (b2 != NULL || (!jl_atomic_cmpswap(&b->owner, &b2, b) && b2 != b)) { jl_module_t *from = jl_binding_dbgmodule(b, m, var); if (from == m) @@ -701,13 +704,15 @@ JL_DLLEXPORT int jl_binding_resolved_p(jl_module_t *m, jl_sym_t *var) static uint_t bindingkey_hash(size_t idx, jl_value_t *data) { - jl_binding_t *b = (jl_binding_t*)jl_svecref(data, idx); + jl_binding_t *b = (jl_binding_t*)jl_svecref(data, idx); // This must always happen inside the lock jl_sym_t *var = b->globalref->name; return var->hash; } static int bindingkey_eq(size_t idx, const void *var, jl_value_t *data, uint_t hv) { + if (idx >= jl_svec_len(data)) + return 0; // We got a OOB access, probably due to a data race jl_binding_t *b = (jl_binding_t*)jl_svecref(data, idx); jl_sym_t *name = b->globalref->name; return var == name; @@ -782,7 +787,7 @@ JL_DLLEXPORT jl_value_t *jl_get_global(jl_module_t *m, jl_sym_t *var) JL_DLLEXPORT void jl_set_global(jl_module_t *m JL_ROOTING_ARGUMENT, jl_sym_t *var, jl_value_t *val JL_ROOTED_ARGUMENT) { - jl_binding_t *bp = jl_get_binding_wr(m, var); + jl_binding_t *bp = jl_get_binding_wr(m, var, 0); jl_checked_assignment(bp, m, var, val); } diff --git a/src/scheduler.c b/src/scheduler.c index e95c221548f9e..0c0c3be9fb7fd 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -459,140 +459,151 @@ JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *trypoptask, jl_value_t *q, } continue; } - task = get_next_task(trypoptask, q); // note: this should not yield - if (ptls != ct->ptls) { - // sigh, a yield was detected, so let's go ahead and handle it anyway by starting over - ptls = ct->ptls; - if (set_not_sleeping(ptls)) { - JL_PROBE_RT_SLEEP_CHECK_TASK_WAKE(ptls); + volatile int isrunning = 1; + JL_TRY { + task = get_next_task(trypoptask, q); // note: this should not yield + if (ptls != ct->ptls) { + // sigh, a yield was detected, so let's go ahead and handle it anyway by starting over + ptls = ct->ptls; + if (set_not_sleeping(ptls)) { + JL_PROBE_RT_SLEEP_CHECK_TASK_WAKE(ptls); + } + continue; // jump to JL_CATCH } - if (task) - return task; - continue; - } - if (task) { - if (set_not_sleeping(ptls)) { - JL_PROBE_RT_SLEEP_CHECK_TASK_WAKE(ptls); + if (task) { + if (set_not_sleeping(ptls)) { + JL_PROBE_RT_SLEEP_CHECK_TASK_WAKE(ptls); + } + continue; // jump to JL_CATCH } - return task; - } - // IO is always permitted, but outside a threaded region, only - // thread 0 will process messages. - // Inside a threaded region, any thread can listen for IO messages, - // and one thread should win this race and watch the event loop, - // but we bias away from idle threads getting parked here. - // - // The reason this works is somewhat convoluted, and closely tied to [^store_buffering_1]: - // - After decrementing _threadedregion, the thread is required to - // call jl_wakeup_thread(0), that will kick out any thread who is - // already there, and then eventually thread 0 will get here. - // - Inside a _threadedregion, there must exist at least one - // thread that has a happens-before relationship on the libuv lock - // before reaching this decision point in the code who will see - // the lock as unlocked and thus must win this race here. - int uvlock = 0; - if (jl_atomic_load_relaxed(&_threadedregion)) { - uvlock = jl_mutex_trylock(&jl_uv_mutex); - } - else if (ptls->tid == 0) { - uvlock = 1; - JL_UV_LOCK(); - } - else { - // Since we might have started some IO work, we might need - // to ensure tid = 0 will go watch that new event source. - // If trylock would have succeeded, that may have been our - // responsibility, so need to make sure thread 0 will take care - // of us. - if (jl_atomic_load_relaxed(&jl_uv_mutex.owner) == NULL) // aka trylock - jl_wakeup_thread(0); - } - if (uvlock) { - int enter_eventloop = may_sleep(ptls); - int active = 0; - if (jl_atomic_load_relaxed(&jl_uv_n_waiters) != 0) - // if we won the race against someone who actually needs - // the lock to do real work, we need to let them have it instead - enter_eventloop = 0; - if (enter_eventloop) { - uv_loop_t *loop = jl_global_event_loop(); - loop->stop_flag = 0; - JULIA_DEBUG_SLEEPWAKE( ptls->uv_run_enter = cycleclock() ); - active = uv_run(loop, UV_RUN_ONCE); - JULIA_DEBUG_SLEEPWAKE( ptls->uv_run_leave = cycleclock() ); - jl_gc_safepoint(); + // IO is always permitted, but outside a threaded region, only + // thread 0 will process messages. + // Inside a threaded region, any thread can listen for IO messages, + // and one thread should win this race and watch the event loop, + // but we bias away from idle threads getting parked here. + // + // The reason this works is somewhat convoluted, and closely tied to [^store_buffering_1]: + // - After decrementing _threadedregion, the thread is required to + // call jl_wakeup_thread(0), that will kick out any thread who is + // already there, and then eventually thread 0 will get here. + // - Inside a _threadedregion, there must exist at least one + // thread that has a happens-before relationship on the libuv lock + // before reaching this decision point in the code who will see + // the lock as unlocked and thus must win this race here. + int uvlock = 0; + if (jl_atomic_load_relaxed(&_threadedregion)) { + uvlock = jl_mutex_trylock(&jl_uv_mutex); } - JL_UV_UNLOCK(); - // optimization: check again first if we may have work to do. - // Otherwise we got a spurious wakeup since some other thread - // that just wanted to steal libuv from us. We will just go - // right back to sleep on the individual wake signal to let - // them take it from us without conflict. - if (active || !may_sleep(ptls)) { - if (set_not_sleeping(ptls)) { - JL_PROBE_RT_SLEEP_CHECK_UV_WAKE(ptls); + else if (ptls->tid == 0) { + uvlock = 1; + JL_UV_LOCK(); + } + else { + // Since we might have started some IO work, we might need + // to ensure tid = 0 will go watch that new event source. + // If trylock would have succeeded, that may have been our + // responsibility, so need to make sure thread 0 will take care + // of us. + if (jl_atomic_load_relaxed(&jl_uv_mutex.owner) == NULL) // aka trylock + jl_wakeup_thread(0); + } + if (uvlock) { + int enter_eventloop = may_sleep(ptls); + int active = 0; + if (jl_atomic_load_relaxed(&jl_uv_n_waiters) != 0) + // if we won the race against someone who actually needs + // the lock to do real work, we need to let them have it instead + enter_eventloop = 0; + if (enter_eventloop) { + uv_loop_t *loop = jl_global_event_loop(); + loop->stop_flag = 0; + JULIA_DEBUG_SLEEPWAKE( ptls->uv_run_enter = cycleclock() ); + active = uv_run(loop, UV_RUN_ONCE); + JULIA_DEBUG_SLEEPWAKE( ptls->uv_run_leave = cycleclock() ); + jl_gc_safepoint(); + } + JL_UV_UNLOCK(); + // optimization: check again first if we may have work to do. + // Otherwise we got a spurious wakeup since some other thread + // that just wanted to steal libuv from us. We will just go + // right back to sleep on the individual wake signal to let + // them take it from us without conflict. + if (active || !may_sleep(ptls)) { + if (set_not_sleeping(ptls)) { + JL_PROBE_RT_SLEEP_CHECK_UV_WAKE(ptls); + } + start_cycles = 0; + continue; // jump to JL_CATCH + } + if (!enter_eventloop && !jl_atomic_load_relaxed(&_threadedregion) && ptls->tid == 0) { + // thread 0 is the only thread permitted to run the event loop + // so it needs to stay alive, just spin-looping if necessary + if (set_not_sleeping(ptls)) { + JL_PROBE_RT_SLEEP_CHECK_UV_WAKE(ptls); + } + start_cycles = 0; + continue; // jump to JL_CATCH } - start_cycles = 0; - continue; } - if (!enter_eventloop && !jl_atomic_load_relaxed(&_threadedregion) && ptls->tid == 0) { - // thread 0 is the only thread permitted to run the event loop - // so it needs to stay alive, just spin-looping if necessary - if (set_not_sleeping(ptls)) { - JL_PROBE_RT_SLEEP_CHECK_UV_WAKE(ptls); + + // any thread which wants us running again will have to observe + // sleep_check_state==sleeping and increment nrunning for us + int wasrunning = jl_atomic_fetch_add_relaxed(&nrunning, -1); + assert(wasrunning); + isrunning = 0; + if (wasrunning == 1) { + // This was the last running thread, and there is no thread with !may_sleep + // so make sure tid 0 is notified to check wait_empty + // TODO: this also might be a good time to check again that + // libuv's queue is truly empty, instead of during delete_thread + if (ptls->tid != 0) { + uv_mutex_lock(&ptls->sleep_lock); + uv_cond_wait(&ptls->wake_signal, &ptls->sleep_lock); + uv_mutex_unlock(&ptls->sleep_lock); } - start_cycles = 0; - continue; } - } - // any thread which wants us running again will have to observe - // sleep_check_state==sleeping and increment nrunning for us - int wasrunning = jl_atomic_fetch_add_relaxed(&nrunning, -1); - assert(wasrunning); - if (wasrunning == 1) { - // This was the last running thread, and there is no thread with !may_sleep - // so make sure tid 0 is notified to check wait_empty - // TODO: this also might be a good time to check again that - // libuv's queue is truly empty, instead of during delete_thread - if (ptls->tid != 0) { - uv_mutex_lock(&ptls->sleep_lock); + // the other threads will just wait for an individual wake signal to resume + JULIA_DEBUG_SLEEPWAKE( ptls->sleep_enter = cycleclock() ); + int8_t gc_state = jl_gc_safe_enter(ptls); + uv_mutex_lock(&ptls->sleep_lock); + while (may_sleep(ptls)) { + task = wait_empty; + if (ptls->tid == 0 && task && jl_atomic_load_relaxed(&nrunning) == 0) { + wasrunning = jl_atomic_fetch_add_relaxed(&nrunning, 1); + assert(!wasrunning); + wasrunning = !set_not_sleeping(ptls); + assert(!wasrunning); + JL_PROBE_RT_SLEEP_CHECK_TASK_WAKE(ptls); + if (!ptls->finalizers_inhibited) + ptls->finalizers_inhibited++; // this annoyingly is rather sticky (we should like to reset it at the end of jl_task_wait_empty) + break; + } + // else should we warn the user of certain deadlock here if tid == 0 && nrunning == 0? uv_cond_wait(&ptls->wake_signal, &ptls->sleep_lock); - uv_mutex_unlock(&ptls->sleep_lock); } - } - - // the other threads will just wait for an individual wake signal to resume - JULIA_DEBUG_SLEEPWAKE( ptls->sleep_enter = cycleclock() ); - int8_t gc_state = jl_gc_safe_enter(ptls); - uv_mutex_lock(&ptls->sleep_lock); - while (may_sleep(ptls)) { - task = wait_empty; - if (ptls->tid == 0 && task && jl_atomic_load_relaxed(&nrunning) == 0) { - wasrunning = jl_atomic_fetch_add_relaxed(&nrunning, 1); - assert(!wasrunning); - wasrunning = !set_not_sleeping(ptls); - assert(!wasrunning); - JL_PROBE_RT_SLEEP_CHECK_TASK_WAKE(ptls); - if (!ptls->finalizers_inhibited) - ptls->finalizers_inhibited++; // this annoyingly is rather sticky (we should like to reset it at the end of jl_task_wait_empty) - break; + assert(jl_atomic_load_relaxed(&ptls->sleep_check_state) == not_sleeping); + assert(jl_atomic_load_relaxed(&nrunning)); + start_cycles = 0; + uv_mutex_unlock(&ptls->sleep_lock); + JULIA_DEBUG_SLEEPWAKE( ptls->sleep_leave = cycleclock() ); + jl_gc_safe_leave(ptls, gc_state); // contains jl_gc_safepoint + if (task) { + assert(task == wait_empty); + wait_empty = NULL; + continue; } - // else should we warn the user of certain deadlock here if tid == 0 && nrunning == 0? - uv_cond_wait(&ptls->wake_signal, &ptls->sleep_lock); } - assert(jl_atomic_load_relaxed(&ptls->sleep_check_state) == not_sleeping); - assert(jl_atomic_load_relaxed(&nrunning)); - start_cycles = 0; - uv_mutex_unlock(&ptls->sleep_lock); - JULIA_DEBUG_SLEEPWAKE( ptls->sleep_leave = cycleclock() ); - jl_gc_safe_leave(ptls, gc_state); // contains jl_gc_safepoint - if (task) { - assert(task == wait_empty); - wait_empty = NULL; - return task; + JL_CATCH { + // probably SIGINT, but possibly a user mistake in trypoptask + if (!isrunning) + jl_atomic_fetch_add_relaxed(&nrunning, 1); + set_not_sleeping(ptls); + jl_rethrow(); } + if (task) + return task; } else { // maybe check the kernel for new messages too diff --git a/src/staticdata.c b/src/staticdata.c index 261042b775c14..76bb488731a92 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -461,6 +461,7 @@ void *native_functions; // opaque jl_native_code_desc_t blob used for fetching // table of struct field addresses to rewrite during saving static htable_t field_replace; +static htable_t relocatable_ext_cis; // array of definitions for the predefined function pointers // (reverse of fptr_to_id) @@ -693,7 +694,8 @@ static int needs_uniquing(jl_value_t *v) JL_NOTSAFEPOINT static void record_field_change(jl_value_t **addr, jl_value_t *newval) JL_NOTSAFEPOINT { - ptrhash_put(&field_replace, (void*)addr, newval); + if (*addr != newval) + ptrhash_put(&field_replace, (void*)addr, newval); } static jl_value_t *get_replaceable_field(jl_value_t **addr, int mutabl) JL_GC_DISABLED @@ -836,6 +838,8 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ // TODO: if (ci in ci->defs->cache) record_field_change((jl_value_t**)&ci->next, NULL); } + if (jl_atomic_load_relaxed(&ci->inferred) && !is_relocatable_ci(&relocatable_ext_cis, ci)) + record_field_change((jl_value_t**)&ci->inferred, jl_nothing); } if (immediate) // must be things that can be recursively handled, and valid as type parameters @@ -1631,6 +1635,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED jl_atomic_store_release(&newci->min_world, 1); jl_atomic_store_release(&newci->max_world, 0); } + newci->relocatability = 0; } jl_atomic_store_relaxed(&newci->invoke, NULL); jl_atomic_store_relaxed(&newci->specsigflags, 0); @@ -2573,7 +2578,7 @@ static void jl_prepare_serialization_data(jl_array_t *mod_array, jl_array_t *new *edges = jl_alloc_vec_any(0); *method_roots_list = jl_alloc_vec_any(0); // Collect the new method roots for external specializations - jl_collect_new_roots(*method_roots_list, *new_ext_cis, worklist_key); + jl_collect_new_roots(&relocatable_ext_cis, *method_roots_list, *new_ext_cis, worklist_key); jl_collect_edges(*edges, *ext_targets, *new_ext_cis, world); } assert(edges_map == NULL); // jl_collect_edges clears this when done @@ -2974,6 +2979,7 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli assert((ct->reentrant_timing & 0b1110) == 0); ct->reentrant_timing |= 0b1000; if (worklist) { + htable_new(&relocatable_ext_cis, 0); jl_prepare_serialization_data(mod_array, newly_inferred, jl_worklist_key(worklist), &extext_methods, &new_ext_cis, &method_roots_list, &ext_targets, &edges); if (!emit_split) { @@ -2990,6 +2996,8 @@ JL_DLLEXPORT void jl_create_system_image(void **_native_data, jl_array_t *workli jl_save_system_image_to_stream(ff, mod_array, worklist, extext_methods, new_ext_cis, method_roots_list, ext_targets, edges); if (_native_data != NULL) native_functions = NULL; + if (worklist) + htable_free(&relocatable_ext_cis); // make sure we don't run any Julia code concurrently before this point // Re-enable running julia code for postoutput hooks, atexit, etc. jl_gc_enable_finalizers(ct, 1); @@ -3042,10 +3050,10 @@ JL_DLLEXPORT void jl_preload_sysimg_so(const char *fname) // Allow passing in a module handle directly, rather than a path JL_DLLEXPORT void jl_set_sysimg_so(void *handle) { - void* *jl_RTLD_DEFAULT_handle_pointer; + void** (*get_jl_RTLD_DEFAULT_handle_addr)(void) = NULL; if (handle != jl_RTLD_DEFAULT_handle) { - int symbol_found = jl_dlsym(handle, "jl_RTLD_DEFAULT_handle_pointer", (void **)&jl_RTLD_DEFAULT_handle_pointer, 0); - if (!symbol_found || (void*)&jl_RTLD_DEFAULT_handle != *jl_RTLD_DEFAULT_handle_pointer) + int symbol_found = jl_dlsym(handle, "get_jl_RTLD_DEFAULT_handle_addr", (void **)&get_jl_RTLD_DEFAULT_handle_addr, 0); + if (!symbol_found || (void*)&jl_RTLD_DEFAULT_handle != (get_jl_RTLD_DEFAULT_handle_addr())) jl_error("System image file failed consistency check: maybe opened the wrong version?"); } if (jl_options.cpu_target == NULL) diff --git a/src/staticdata_utils.c b/src/staticdata_utils.c index 05fce7cfc4630..8d3d071054083 100644 --- a/src/staticdata_utils.c +++ b/src/staticdata_utils.c @@ -209,6 +209,17 @@ static int has_backedge_to_worklist(jl_method_instance_t *mi, htable_t *visited, return found; } +static int is_relocatable_ci(htable_t *relocatable_ext_cis, jl_code_instance_t *ci) +{ + if (!ci->relocatability) + return 0; + jl_method_instance_t *mi = ci->def; + jl_method_t *m = mi->def.method; + if (!ptrhash_has(relocatable_ext_cis, ci) && jl_object_in_image((jl_value_t*)m) && (!jl_is_method(m) || jl_object_in_image((jl_value_t*)m->module))) + return 0; + return 1; +} + // Given the list of CodeInstances that were inferred during the build, select // those that are (1) external, (2) still valid, (3) are inferred to be called // from the worklist or explicitly added by a `precompile` statement, and @@ -258,7 +269,7 @@ static jl_array_t *queue_external_cis(jl_array_t *list) } // New roots for external methods -static void jl_collect_new_roots(jl_array_t *roots, jl_array_t *new_ext_cis, uint64_t key) +static void jl_collect_new_roots(htable_t *relocatable_ext_cis, jl_array_t *roots, jl_array_t *new_ext_cis, uint64_t key) { htable_t mset; htable_new(&mset, 0); @@ -269,6 +280,7 @@ static void jl_collect_new_roots(jl_array_t *roots, jl_array_t *new_ext_cis, uin jl_method_t *m = ci->def->def.method; assert(jl_is_method(m)); ptrhash_put(&mset, (void*)m, (void*)m); + ptrhash_put(relocatable_ext_cis, (void*)ci, (void*)ci); } int nwithkey; void *const *table = mset.table; diff --git a/src/subtype.c b/src/subtype.c index 688a39ca87ef0..3fdacfbb7a4bb 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -2814,6 +2814,50 @@ static jl_value_t *omit_bad_union(jl_value_t *u, jl_tvar_t *t) return res; } +// TODO: fuse with reachable_var? +static int has_typevar_via_flatten_env(jl_value_t *x, jl_tvar_t *t, jl_ivarbinding_t *allvars, int8_t *checked) { + if (jl_is_unionall(x)) { + jl_tvar_t *var = ((jl_unionall_t *)x)->var; + if (has_typevar_via_flatten_env(var->lb, t, allvars, checked) || + has_typevar_via_flatten_env(var->ub, t, allvars, checked)) + return 1; + return has_typevar_via_flatten_env(((jl_unionall_t *)x)->body, t, allvars, checked); + } + else if (jl_is_uniontype(x)) { + return has_typevar_via_flatten_env(((jl_uniontype_t *)x)->a, t, allvars, checked) || + has_typevar_via_flatten_env(((jl_uniontype_t *)x)->b, t, allvars, checked); + } + else if (jl_is_vararg(x)) { + jl_vararg_t *v = (jl_vararg_t *)x; + return (v->T && has_typevar_via_flatten_env(v->T, t, allvars, checked)) || + (v->N && has_typevar_via_flatten_env(v->N, t, allvars, checked)); + } + else if (jl_is_datatype(x)) { + for (size_t i = 0; i < jl_nparams(x); i++) { + if (has_typevar_via_flatten_env(jl_tparam(x, i), t, allvars, checked)) + return 1; + } + return 0; + } + else if (jl_is_typevar(x)) { + if (t == (jl_tvar_t *)x) + return 1; + size_t ind = 0; + jl_ivarbinding_t *itemp = allvars; + while (itemp && *itemp->var != (jl_tvar_t *)x) + { + ind++; + itemp = itemp->next; + } + if (itemp == NULL || checked[ind]) + return 0; + checked[ind] = 1; + return has_typevar_via_flatten_env(*itemp->lb, t, allvars, checked) || + has_typevar_via_flatten_env(*itemp->ub, t, allvars, checked); + } + return 0; +} + // Caller might not have rooted `res` static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbinding_t *vb, jl_unionall_t *u, jl_stenv_t *e) { @@ -2911,8 +2955,11 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind // remove/replace/rewrap free occurrences of this var in the environment int wrapped = 0; jl_ivarbinding_t *pwrap = NULL; + int vcount = icount + current_env_length(e); + int8_t *checked = (int8_t *)alloca(vcount); for (jl_ivarbinding_t *btemp = allvars, *pbtemp = NULL; btemp != NULL; btemp = btemp->next) { int bdepth0 = btemp->root->depth0; + int innerflag = 0; ivar = *btemp->var; ilb = *btemp->lb; iub = *btemp->ub; @@ -2934,18 +2981,9 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind else if (ilb == (jl_value_t*)vb->var) { *btemp->lb = vb->lb; } - else if (bdepth0 == vb->depth0 && !jl_has_typevar(vb->lb, ivar) && !jl_has_typevar(vb->ub, ivar)) { - // if our variable is T, and some outer variable has constraint S = Ref{T}, - // move the `where T` outside `where S` instead of putting it here. issue #21243. - if (newvar != vb->var) - *btemp->lb = jl_substitute_var(ilb, vb->var, (jl_value_t*)newvar); - if (!wrapped) pwrap = pbtemp; - wrapped = 1; - } else { - *btemp->lb = jl_new_struct(jl_unionall_type, vb->var, ilb); + innerflag |= 1; } - assert((jl_value_t*)ivar != *btemp->lb); } if (jl_has_typevar(iub, vb->var)) { assert(btemp->root->var == ivar || bdepth0 == vb->depth0); @@ -2971,14 +3009,35 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind // Tuple{Float64, T3, T4} where {S3, T3<:Tuple{S3}, T4<:S3} *btemp->ub = vb->ub; } - else if (bdepth0 == vb->depth0 && !jl_has_typevar(vb->lb, ivar) && !jl_has_typevar(vb->ub, ivar)) { - if (newvar != vb->var) - *btemp->ub = jl_substitute_var(iub, vb->var, (jl_value_t*)newvar); - if (!wrapped) pwrap = pbtemp; - wrapped = 1; + else { + innerflag |= 2; } - else - *btemp->ub = jl_new_struct(jl_unionall_type, vb->var, iub); + if (innerflag) { + memset(checked, 0, vcount); + if (bdepth0 != vb->depth0 || + has_typevar_via_flatten_env(vb->lb, ivar, allvars, checked) || + has_typevar_via_flatten_env(vb->ub, ivar, allvars, checked)) { + if (innerflag & 1) + *btemp->lb = jl_new_struct(jl_unionall_type, vb->var, ilb); + if (innerflag & 2) + *btemp->ub = jl_new_struct(jl_unionall_type, vb->var, iub); + } + else { + assert(btemp->root != vb); + // if our variable is T, and some outer variable has constraint S = Ref{T}, + // move the `where T` outside `where S` instead of putting it here. issue #21243. + if (newvar != vb->var) { + if (innerflag & 1) + *btemp->lb = jl_substitute_var(ilb, vb->var, (jl_value_t*)newvar); + if (innerflag & 2) + *btemp->ub = jl_substitute_var(iub, vb->var, (jl_value_t*)newvar); + } + if (!wrapped) + pwrap = pbtemp; + wrapped = 1; + } + } + assert((jl_value_t*)ivar != *btemp->lb); assert((jl_value_t*)ivar != *btemp->ub); } pbtemp = btemp; @@ -2997,6 +3056,7 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind pwrap->next = inew; else allvars = inew; + vcount++; } // Re-sort the innervar inside the (reversed) var list. @@ -3004,17 +3064,23 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind // If this is slow, we could possibly switch to a simpler graph sort, such as Tarjan's SCC. if (icount > 0) { jl_ivarbinding_t *pib1 = NULL; +#ifndef NDEBUG + size_t sort_count = 0; +#endif while (1) { jl_ivarbinding_t *ib1 = pib1 == NULL ? allvars : pib1->next; if (ib1 == NULL) break; - if (jl_has_free_typevars(*ib1->lb) || jl_has_free_typevars(*ib1->ub)) { + assert((++sort_count) <= (vcount * (vcount + 1)) >> 1); + int lbfree = jl_has_free_typevars(*ib1->lb); + int ubfree = jl_has_free_typevars(*ib1->ub); + if (lbfree || ubfree) { int changed = 0; jl_ivarbinding_t *pib2 = ib1, *ib2 = ib1->next; while (ib2 != NULL) { int isinnervar = ib2->root->var != *ib2->var; if (isinnervar && ib1->root->depth0 == ib2->root->depth0 && - (jl_has_typevar(*ib1->lb, *ib2->var) || - jl_has_typevar(*ib1->ub, *ib2->var))) { + ((lbfree && jl_has_typevar(*ib1->lb, *ib2->var)) || + (ubfree && jl_has_typevar(*ib1->ub, *ib2->var)))) { pib2->next = ib2->next; ib2->next = ib1; ib2->root = ib1->root; @@ -3052,6 +3118,13 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind if (jl_has_typevar(iub, ivar)) *btemp2->ub = jl_substitute_var(iub, ivar, nivar); } + if (!wrapped && !varval) { + // newvar also needs bounds substitution. + if (jl_has_typevar(vb->lb, ivar)) + vb->lb = jl_substitute_var(vb->lb, ivar, nivar); + if (jl_has_typevar(vb->ub, ivar)) + vb->ub = jl_substitute_var(vb->ub, ivar, nivar); + } *btemp1->var = (jl_tvar_t *)nivar; } } @@ -3067,11 +3140,13 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind } if (root != vb) icount--; if (root->innervars != NULL) { - size_t len = jl_array_nrows(root->innervars); + jl_array_t *rinnervars = root->innervars; + JL_GC_PROMISE_ROOTED(rinnervars); + size_t len = jl_array_nrows(rinnervars); if (icount > len) - jl_array_grow_end(root->innervars, icount - len); + jl_array_grow_end(rinnervars, icount - len); if (icount < len) - jl_array_del_end(root->innervars, len - icount); + jl_array_del_end(rinnervars, len - icount); } else if (icount > 0) { root->innervars = jl_alloc_array_1d(jl_array_any_type, icount); @@ -3104,6 +3179,9 @@ static jl_value_t *finish_unionall(jl_value_t *res JL_MAYBE_UNROOTED, jl_varbind res = jl_bottom_type; } else { + // re-fresh newvar if bounds changed. + if (vb->lb != newvar->lb || vb->ub != newvar->ub) + newvar = jl_new_typevar(newvar->name, vb->lb, vb->ub); if (newvar != vb->var) res = jl_substitute_var(res, vb->var, (jl_value_t*)newvar); varval = (jl_value_t*)newvar; diff --git a/src/threading.c b/src/threading.c index dcfc6ac7e93bb..dfc75b0af581e 100644 --- a/src/threading.c +++ b/src/threading.c @@ -732,7 +732,7 @@ void jl_init_threading(void) jl_atomic_store_release(&jl_all_tls_states, (jl_ptls_t*)calloc(jl_all_tls_states_size, sizeof(jl_ptls_t))); jl_atomic_store_release(&jl_n_threads, jl_all_tls_states_size); jl_n_gcthreads = ngcthreads; - gc_first_tid = nthreads; + gc_first_tid = nthreads + nthreadsi; } static uv_barrier_t thread_init_done; @@ -960,6 +960,52 @@ JL_DLLEXPORT int jl_alignment(size_t sz) return jl_gc_alignment(sz); } +// Return values: +// 0 == success +// 1 == invalid thread id provided +// 2 == ptls2 was NULL +// <0 == uv_thread_getaffinity exit code +JL_DLLEXPORT int jl_getaffinity(int16_t tid, char *mask, int cpumasksize) { + int nthreads = jl_atomic_load_acquire(&jl_n_threads); + if (tid < 0 || tid >= nthreads) + return 1; + + // TODO: use correct lock. system_id is only legal if the thread is alive. + jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[tid]; + if (ptls2 == NULL) + return 2; + uv_thread_t uvtid = ptls2->system_id; + + int ret_uv = uv_thread_getaffinity(&uvtid, mask, cpumasksize); + if (ret_uv != 0) + return ret_uv; + + return 0; // success +} + +// Return values: +// 0 == success +// 1 == invalid thread id provided +// 2 == ptls2 was NULL +// <0 == uv_thread_getaffinity exit code +JL_DLLEXPORT int jl_setaffinity(int16_t tid, char *mask, int cpumasksize) { + int nthreads = jl_atomic_load_acquire(&jl_n_threads); + if (tid < 0 || tid >= nthreads) + return 1; + + // TODO: use correct lock. system_id is only legal if the thread is alive. + jl_ptls_t ptls2 = jl_atomic_load_relaxed(&jl_all_tls_states)[tid]; + if (ptls2 == NULL) + return 2; + uv_thread_t uvtid = ptls2->system_id; + + int ret_uv = uv_thread_setaffinity(&uvtid, mask, NULL, cpumasksize); + if (ret_uv != 0) + return ret_uv; + + return 0; // success +} + #ifdef __cplusplus } #endif diff --git a/src/toplevel.c b/src/toplevel.c index caa01015c362a..a641a635562bb 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -155,7 +155,7 @@ static jl_value_t *jl_eval_module_expr(jl_module_t *parent_module, jl_expr_t *ex } } else { - jl_binding_t *b = jl_get_binding_wr(parent_module, name); + jl_binding_t *b = jl_get_binding_wr(parent_module, name, 1); jl_declare_constant(b, parent_module, name); jl_value_t *old = NULL; if (!jl_atomic_cmpswap(&b->value, &old, (jl_value_t*)newm)) { @@ -325,7 +325,7 @@ void jl_eval_global_expr(jl_module_t *m, jl_expr_t *ex, int set_type) { gs = (jl_sym_t*)arg; } if (!jl_binding_resolved_p(gm, gs)) { - jl_binding_t *b = jl_get_binding_wr(gm, gs); + jl_binding_t *b = jl_get_binding_wr(gm, gs, 1); if (set_type) { jl_value_t *old_ty = NULL; // maybe set the type too, perhaps @@ -622,7 +622,7 @@ static void import_module(jl_module_t *JL_NONNULL m, jl_module_t *import, jl_sym jl_symbol_name(name), jl_symbol_name(m->name)); } else { - b = jl_get_binding_wr(m, name); + b = jl_get_binding_wr(m, name, 1); } jl_declare_constant(b, m, name); jl_checked_assignment(b, m, name, (jl_value_t*)import); @@ -874,7 +874,7 @@ jl_value_t *jl_toplevel_eval_flex(jl_module_t *JL_NONNULL m, jl_value_t *e, int gm = m; gs = (jl_sym_t*)arg; } - jl_binding_t *b = jl_get_binding_wr(gm, gs); + jl_binding_t *b = jl_get_binding_wr(gm, gs, 1); jl_declare_constant(b, gm, gs); JL_GC_POP(); return jl_nothing; diff --git a/stdlib/Artifacts/src/Artifacts.jl b/stdlib/Artifacts/src/Artifacts.jl index e8a40c76b9e49..bd44369655ae4 100644 --- a/stdlib/Artifacts/src/Artifacts.jl +++ b/stdlib/Artifacts/src/Artifacts.jl @@ -9,7 +9,9 @@ that would be convenient to place within an immutable, life-cycled datastore. module Artifacts import Base: get, SHA1 -using Base.BinaryPlatforms, Base.TOML +using Base.BinaryPlatforms: AbstractPlatform, Platform, HostPlatform +using Base.BinaryPlatforms: tags, triplet, select_platform +using Base.TOML: TOML export artifact_exists, artifact_path, artifact_meta, artifact_hash, select_downloadable_artifacts, find_artifacts_toml, @artifact_str @@ -400,7 +402,7 @@ function artifact_meta(name::String, artifact_dict::Dict, artifacts_toml::String # If it's an array, find the entry that best matches our current platform if isa(meta, Vector) - dl_dict = Dict{AbstractPlatform,Dict{String,Any}}() + dl_dict = Dict{Platform,Dict{String,Any}}() for x in meta x = x::Dict{String, Any} dl_dict[unpack_platform(x, name, artifacts_toml)] = x diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index 7391c277b0718..e1f7f900bff51 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -492,3 +492,8 @@ end Base.OrderStyle(::Type{<:AbstractTime}) = Base.Ordered() Base.ArithmeticStyle(::Type{<:AbstractTime}) = Base.ArithmeticWraps() + +# minimal Base.TOML support +Date(d::Base.TOML.Date) = Date(d.year, d.month, d.day) +Time(t::Base.TOML.Time) = Time(t.hour, t.minute, t.second, t.ms) +DateTime(dt::Base.TOML.DateTime) = DateTime(Date(dt.date), Time(dt.time)) diff --git a/stdlib/Distributed.version b/stdlib/Distributed.version index ca528066e9403..02eac7eadf0ad 100644 --- a/stdlib/Distributed.version +++ b/stdlib/Distributed.version @@ -1,4 +1,4 @@ DISTRIBUTED_BRANCH = master -DISTRIBUTED_SHA1 = 41c01069533e22a6ce6b794746e4b3aa9f5a25cd +DISTRIBUTED_SHA1 = 6c7cdb5860fa5cb9ca191ce9c52a3d25a9ab3781 DISTRIBUTED_GIT_URL := https://github.com/JuliaLang/Distributed.jl DISTRIBUTED_TAR_URL = https://api.github.com/repos/JuliaLang/Distributed.jl/tarball/$1 diff --git a/stdlib/InteractiveUtils/src/editless.jl b/stdlib/InteractiveUtils/src/editless.jl index 5b87dc0c57d40..6d1d75f1072ea 100644 --- a/stdlib/InteractiveUtils/src/editless.jl +++ b/stdlib/InteractiveUtils/src/editless.jl @@ -223,6 +223,9 @@ Edit a file or directory optionally providing a line number to edit the file at. Return to the `julia` prompt when you quit the editor. The editor can be changed by setting `JULIA_EDITOR`, `VISUAL` or `EDITOR` as an environment variable. +!!! compat "Julia 1.9" + The `column` argument requires at least Julia 1.9. + See also [`InteractiveUtils.define_editor`](@ref). """ function edit(path::AbstractString, line::Integer=0, column::Integer=0) diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index f3d39824ad72c..c2144bf85d024 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -1934,18 +1934,24 @@ function copytrito!(B::AbstractMatrix, A::AbstractMatrix, uplo::AbstractChar) BLAS.chkuplo(uplo) m,n = size(A) m1,n1 = size(B) - (m1 < m || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least the same number of rows and columns than A of size ($m,$n)")) + A = Base.unalias(B, A) if uplo == 'U' - for j=1:n - for i=1:min(j,m) - @inbounds B[i,j] = A[i,j] - end + if n < m + (m1 < n || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($n,$n)")) + else + (m1 < m || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($m,$n)")) end - else # uplo == 'L' - for j=1:n - for i=j:m - @inbounds B[i,j] = A[i,j] - end + for j in 1:n, i in 1:min(j,m) + @inbounds B[i,j] = A[i,j] + end + else # uplo == 'L' + if m < n + (m1 < m || n1 < m) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($m,$m)")) + else + (m1 < m || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($m,$n)")) + end + for j in 1:n, i in j:m + @inbounds B[i,j] = A[i,j] end end return B diff --git a/stdlib/LinearAlgebra/src/lapack.jl b/stdlib/LinearAlgebra/src/lapack.jl index fc8d8d67e07b4..4b7bffcd50e89 100644 --- a/stdlib/LinearAlgebra/src/lapack.jl +++ b/stdlib/LinearAlgebra/src/lapack.jl @@ -3701,7 +3701,7 @@ for (trcon, trevc, trrfs, elty) in # LOGICAL SELECT( * ) # DOUBLE PRECISION T( LDT, * ), VL( LDVL, * ), VR( LDVR, * ), #$ WORK( * ) - function trevc!(side::AbstractChar, howmny::AbstractChar, select::AbstractVector{BlasInt}, T::AbstractMatrix{$elty}, + Base.@constprop :aggressive function trevc!(side::AbstractChar, howmny::AbstractChar, select::AbstractVector{BlasInt}, T::AbstractMatrix{$elty}, VL::AbstractMatrix{$elty} = similar(T), VR::AbstractMatrix{$elty} = similar(T)) require_one_based_indexing(select, T, VL, VR) @@ -7163,9 +7163,23 @@ for (fn, elty) in ((:dlacpy_, :Float64), function lacpy!(B::AbstractMatrix{$elty}, A::AbstractMatrix{$elty}, uplo::AbstractChar) require_one_based_indexing(A, B) chkstride1(A, B) - m,n = size(A) - m1,n1 = size(B) - (m1 < m || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least the same number of rows and columns than A of size ($m,$n)")) + m, n = size(A) + m1, n1 = size(B) + if uplo == 'U' + if n < m + (m1 < n || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($n,$n)")) + else + (m1 < m || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($m,$n)")) + end + elseif uplo == 'L' + if m < n + (m1 < m || n1 < m) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($m,$m)")) + else + (m1 < m || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($m,$n)")) + end + else + (m1 < m || n1 < n) && throw(DimensionMismatch(lazy"B of size ($m1,$n1) should have at least size ($m,$n)")) + end lda = max(1, stride(A, 2)) ldb = max(1, stride(B, 2)) ccall((@blasfunc($fn), libblastrampoline), Cvoid, diff --git a/stdlib/LinearAlgebra/src/lbt.jl b/stdlib/LinearAlgebra/src/lbt.jl index aadcb45d606a3..02b4411566290 100644 --- a/stdlib/LinearAlgebra/src/lbt.jl +++ b/stdlib/LinearAlgebra/src/lbt.jl @@ -13,6 +13,19 @@ struct lbt_library_info_t f2c::Int32 cblas::Int32 end + +macro get_warn(map, key) + return quote + if !haskey($(esc(map)), $(esc(key))) + @warn(string("[LBT] Unknown key into ", $(string(map)), ": ", $(esc(key)), ", defaulting to :unknown")) + # All the unknown values share a common value: `-1` + $(esc(map))[$(esc(LBT_INTERFACE_UNKNOWN))] + else + $(esc(map))[$(esc(key))] + end + end +end + const LBT_INTERFACE_LP64 = 32 const LBT_INTERFACE_ILP64 = 64 const LBT_INTERFACE_UNKNOWN = -1 @@ -35,10 +48,12 @@ const LBT_INV_F2C_MAP = Dict(v => k for (k, v) in LBT_F2C_MAP) const LBT_COMPLEX_RETSTYLE_NORMAL = 0 const LBT_COMPLEX_RETSTYLE_ARGUMENT = 1 +const LBT_COMPLEX_RETSTYLE_FNDA = 2 const LBT_COMPLEX_RETSTYLE_UNKNOWN = -1 const LBT_COMPLEX_RETSTYLE_MAP = Dict( LBT_COMPLEX_RETSTYLE_NORMAL => :normal, LBT_COMPLEX_RETSTYLE_ARGUMENT => :argument, + LBT_COMPLEX_RETSTYLE_FNDA => :float_normal_double_argument, LBT_COMPLEX_RETSTYLE_UNKNOWN => :unknown, ) const LBT_INV_COMPLEX_RETSTYLE_MAP = Dict(v => k for (k, v) in LBT_COMPLEX_RETSTYLE_MAP) @@ -69,10 +84,10 @@ struct LBTLibraryInfo lib_info.handle, unsafe_string(lib_info.suffix), unsafe_wrap(Vector{UInt8}, lib_info.active_forwards, div(num_exported_symbols,8)+1), - LBT_INTERFACE_MAP[lib_info.interface], - LBT_COMPLEX_RETSTYLE_MAP[lib_info.complex_retstyle], - LBT_F2C_MAP[lib_info.f2c], - LBT_CBLAS_MAP[lib_info.cblas], + @get_warn(LBT_INTERFACE_MAP, lib_info.interface), + @get_warn(LBT_COMPLEX_RETSTYLE_MAP, lib_info.complex_retstyle), + @get_warn(LBT_F2C_MAP, lib_info.f2c), + @get_warn(LBT_CBLAS_MAP, lib_info.cblas), ) end end diff --git a/stdlib/LinearAlgebra/src/transpose.jl b/stdlib/LinearAlgebra/src/transpose.jl index 8aa04f7d34b48..a36919b2e557a 100644 --- a/stdlib/LinearAlgebra/src/transpose.jl +++ b/stdlib/LinearAlgebra/src/transpose.jl @@ -74,27 +74,32 @@ julia> A ``` """ adjoint!(B::AbstractMatrix, A::AbstractMatrix) = transpose_f!(adjoint, B, A) + +@noinline function check_transpose_axes(axesA, axesB) + axesB == reverse(axesA) || throw(DimensionMismatch("axes of the destination are incompatible with that of the source")) +end + function transpose!(B::AbstractVector, A::AbstractMatrix) - axes(B,1) == axes(A,2) && axes(A,1) == 1:1 || throw(DimensionMismatch("transpose")) + check_transpose_axes((axes(B,1), axes(B,2)), axes(A)) copyto!(B, A) end function transpose!(B::AbstractMatrix, A::AbstractVector) - axes(B,2) == axes(A,1) && axes(B,1) == 1:1 || throw(DimensionMismatch("transpose")) + check_transpose_axes(axes(B), (axes(A,1), axes(A,2))) copyto!(B, A) end function adjoint!(B::AbstractVector, A::AbstractMatrix) - axes(B,1) == axes(A,2) && axes(A,1) == 1:1 || throw(DimensionMismatch("transpose")) + check_transpose_axes((axes(B,1), axes(B,2)), axes(A)) ccopy!(B, A) end function adjoint!(B::AbstractMatrix, A::AbstractVector) - axes(B,2) == axes(A,1) && axes(B,1) == 1:1 || throw(DimensionMismatch("transpose")) + check_transpose_axes(axes(B), (axes(A,1), axes(A,2))) ccopy!(B, A) end const transposebaselength=64 function transpose_f!(f, B::AbstractMatrix, A::AbstractMatrix) inds = axes(A) - axes(B,1) == inds[2] && axes(B,2) == inds[1] || throw(DimensionMismatch(string(f))) + check_transpose_axes(axes(B), inds) m, n = length(inds[1]), length(inds[2]) if m*n<=4*transposebaselength diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index e782c0929f09f..c3def1318db86 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -269,10 +269,24 @@ Base.isstored(A::UpperTriangular, i::Int, j::Int) = @propagate_inbounds getindex(A::UpperTriangular, i::Integer, j::Integer) = i <= j ? A.data[i,j] : _zero(A.data,j,i) +_zero_triangular_half_str(::Type{<:UpperOrUnitUpperTriangular}) = "lower" +_zero_triangular_half_str(::Type{<:LowerOrUnitLowerTriangular}) = "upper" + +@noinline function throw_nonzeroerror(T, @nospecialize(x), i, j) + Ts = _zero_triangular_half_str(T) + Tn = nameof(T) + throw(ArgumentError( + lazy"cannot set index in the $Ts triangular part ($i, $j) of an $Tn matrix to a nonzero value ($x)")) +end +@noinline function throw_nononeerror(T, @nospecialize(x), i, j) + Tn = nameof(T) + throw(ArgumentError( + lazy"cannot set index on the diagonal ($i, $j) of an $Tn matrix to a non-unit value ($x)")) +end + @propagate_inbounds function setindex!(A::UpperTriangular, x, i::Integer, j::Integer) if i > j - iszero(x) || throw(ArgumentError("cannot set index in the lower triangular part " * - lazy"($i, $j) of an UpperTriangular matrix to a nonzero value ($x)")) + iszero(x) || throw_nonzeroerror(typeof(A), x, i, j) else A.data[i,j] = x end @@ -281,11 +295,9 @@ end @propagate_inbounds function setindex!(A::UnitUpperTriangular, x, i::Integer, j::Integer) if i > j - iszero(x) || throw(ArgumentError("cannot set index in the lower triangular part " * - lazy"($i, $j) of a UnitUpperTriangular matrix to a nonzero value ($x)")) + iszero(x) || throw_nonzeroerror(typeof(A), x, i, j) elseif i == j - x == oneunit(x) || throw(ArgumentError(lazy"cannot set index on the diagonal ($i, $j) " * - lazy"of a UnitUpperTriangular matrix to a non-unit value ($x)")) + x == oneunit(x) || throw_nononeerror(typeof(A), x, i, j) else A.data[i,j] = x end @@ -294,8 +306,7 @@ end @propagate_inbounds function setindex!(A::LowerTriangular, x, i::Integer, j::Integer) if i < j - iszero(x) || throw(ArgumentError("cannot set index in the upper triangular part " * - lazy"($i, $j) of a LowerTriangular matrix to a nonzero value ($x)")) + iszero(x) || throw_nonzeroerror(typeof(A), x, i, j) else A.data[i,j] = x end @@ -304,28 +315,31 @@ end @propagate_inbounds function setindex!(A::UnitLowerTriangular, x, i::Integer, j::Integer) if i < j - iszero(x) || throw(ArgumentError("cannot set index in the upper triangular part " * - lazy"($i, $j) of a UnitLowerTriangular matrix to a nonzero value ($x)")) + iszero(x) || throw_nonzeroerror(typeof(A), x, i, j) elseif i == j - x == oneunit(x) || throw(ArgumentError(lazy"cannot set index on the diagonal ($i, $j) " * - lazy"of a UnitLowerTriangular matrix to a non-unit value ($x)")) + x == oneunit(x) || throw_nononeerror(typeof(A), x, i, j) else A.data[i,j] = x end return A end +@noinline function throw_setindex_structuralzero_error(T, @nospecialize(x)) + Ts = _zero_triangular_half_str(T) + Tn = nameof(T) + throw(ArgumentError( + lazy"cannot set indices in the $Ts triangular part of an $Tn matrix to a nonzero value ($x)")) +end + @inline function fill!(A::UpperTriangular, x) - iszero(x) || throw(ArgumentError("cannot set indices in the lower triangular part " * - lazy"of an UpperTriangular matrix to a nonzero value ($x)")) + iszero(x) || throw_setindex_structuralzero_error(typeof(A), x) for col in axes(A,2), row in firstindex(A,1):col @inbounds A.data[row, col] = x end A end @inline function fill!(A::LowerTriangular, x) - iszero(x) || throw(ArgumentError("cannot set indices in the upper triangular part " * - lazy"of a LowerTriangular matrix to a nonzero value ($x)")) + iszero(x) || throw_setindex_structuralzero_error(typeof(A), x) for col in axes(A,2), row in col:lastindex(A,1) @inbounds A.data[row, col] = x end diff --git a/stdlib/LinearAlgebra/src/tridiag.jl b/stdlib/LinearAlgebra/src/tridiag.jl index 447d87f1c5efc..497e265b280bc 100644 --- a/stdlib/LinearAlgebra/src/tridiag.jl +++ b/stdlib/LinearAlgebra/src/tridiag.jl @@ -176,6 +176,12 @@ issymmetric(S::SymTridiagonal) = true tr(S::SymTridiagonal) = sum(S.dv) +@noinline function throw_diag_outofboundserror(n, sz) + sz1, sz2 = sz + throw(ArgumentError(LazyString(lazy"requested diagonal, $n, must be at least $(-sz1) ", + lazy"and at most $sz2 for an $(sz1)-by-$(sz2) matrix"))) +end + function diag(M::SymTridiagonal{T}, n::Integer=0) where T<:Number # every branch call similar(..., ::Int) to make sure the # same vector type is returned independent of n @@ -187,8 +193,7 @@ function diag(M::SymTridiagonal{T}, n::Integer=0) where T<:Number elseif absn <= size(M,1) return fill!(similar(M.dv, size(M,1)-absn), zero(T)) else - throw(ArgumentError(string(lazy"requested diagonal, $n, must be at least $(-size(M, 1)) ", - lazy"and at most $(size(M, 2)) for an $(size(M, 1))-by-$(size(M, 2)) matrix"))) + throw_diag_outofboundserror(n, size(M)) end end function diag(M::SymTridiagonal, n::Integer=0) @@ -203,8 +208,7 @@ function diag(M::SymTridiagonal, n::Integer=0) elseif n <= size(M,1) throw(ArgumentError("requested diagonal contains undefined zeros of an array type")) else - throw(ArgumentError(string(lazy"requested diagonal, $n, must be at least $(-size(M, 1)) ", - lazy"and at most $(size(M, 2)) for an $(size(M, 1))-by-$(size(M, 2)) matrix"))) + throw_diag_outofboundserror(n, size(M)) end end @@ -346,7 +350,7 @@ isdiag(M::SymTridiagonal) = iszero(_evview(M)) function tril!(M::SymTridiagonal{T}, k::Integer=0) where T n = length(M.dv) if !(-n - 1 <= k <= n - 1) - throw(ArgumentError(string(lazy"the requested diagonal, $k, must be at least ", + throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ", lazy"$(-n - 1) and at most $(n - 1) in an $n-by-$n matrix"))) elseif k < -1 fill!(M.ev, zero(T)) @@ -365,7 +369,7 @@ end function triu!(M::SymTridiagonal{T}, k::Integer=0) where T n = length(M.dv) if !(-n + 1 <= k <= n + 1) - throw(ArgumentError(string(lazy"the requested diagonal, $k, must be at least ", + throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ", lazy"$(-n + 1) and at most $(n + 1) in an $n-by-$n matrix"))) elseif k > 1 fill!(M.ev, zero(T)) @@ -474,7 +478,7 @@ struct Tridiagonal{T,V<:AbstractVector{T}} <: AbstractMatrix{T} require_one_based_indexing(dl, d, du) n = length(d) if (length(dl) != n-1 || length(du) != n-1) && !(length(d) == 0 && length(dl) == 0 && length(du) == 0) - throw(ArgumentError(string("cannot construct Tridiagonal from incompatible ", + throw(ArgumentError(LazyString("cannot construct Tridiagonal from incompatible ", "lengths of subdiagonal, diagonal and superdiagonal: ", lazy"($(length(dl)), $(length(d)), $(length(du)))"))) end @@ -642,7 +646,7 @@ function diag(M::Tridiagonal{T}, n::Integer=0) where T elseif abs(n) <= size(M,1) return fill!(similar(M.d, size(M,1)-abs(n)), zero(T)) else - throw(ArgumentError(string(lazy"requested diagonal, $n, must be at least $(-size(M, 1)) ", + throw(ArgumentError(LazyString(lazy"requested diagonal, $n, must be at least $(-size(M, 1)) ", lazy"and at most $(size(M, 2)) for an $(size(M, 1))-by-$(size(M, 2)) matrix"))) end end @@ -695,7 +699,7 @@ end elseif j - i == 1 @inbounds A.du[i] = x elseif !iszero(x) - throw(ArgumentError(string(lazy"cannot set entry ($i, $j) off ", + throw(ArgumentError(LazyString(lazy"cannot set entry ($i, $j) off ", lazy"the tridiagonal band to a nonzero value ($x)"))) end return x @@ -738,7 +742,7 @@ isdiag(M::Tridiagonal) = iszero(M.dl) && iszero(M.du) function tril!(M::Tridiagonal{T}, k::Integer=0) where T n = length(M.d) if !(-n - 1 <= k <= n - 1) - throw(ArgumentError(string(lazy"the requested diagonal, $k, must be at least ", + throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ", lazy"$(-n - 1) and at most $(n - 1) in an $n-by-$n matrix"))) elseif k < -1 fill!(M.dl, zero(T)) @@ -756,7 +760,7 @@ end function triu!(M::Tridiagonal{T}, k::Integer=0) where T n = length(M.d) if !(-n + 1 <= k <= n + 1) - throw(ArgumentError(string(lazy"the requested diagonal, $k, must be at least ", + throw(ArgumentError(LazyString(lazy"the requested diagonal, $k, must be at least ", lazy"$(-n + 1) and at most $(n + 1) in an $n-by-$n matrix"))) elseif k > 1 fill!(M.dl, zero(T)) diff --git a/stdlib/LinearAlgebra/test/adjtrans.jl b/stdlib/LinearAlgebra/test/adjtrans.jl index 2c533af37f912..1a66c7430723e 100644 --- a/stdlib/LinearAlgebra/test/adjtrans.jl +++ b/stdlib/LinearAlgebra/test/adjtrans.jl @@ -703,4 +703,14 @@ end @test B == At end +@testset "error message in transpose" begin + v = zeros(2) + A = zeros(1,1) + B = zeros(2,3) + for (t1, t2) in Any[(A, v), (v, A), (A, B)] + @test_throws "axes of the destination are incompatible with that of the source" transpose!(t1, t2) + @test_throws "axes of the destination are incompatible with that of the source" adjoint!(t1, t2) + end +end + end # module TestAdjointTranspose diff --git a/stdlib/LinearAlgebra/test/generic.jl b/stdlib/LinearAlgebra/test/generic.jl index 6318b8e405ede..fd464d6c0762c 100644 --- a/stdlib/LinearAlgebra/test/generic.jl +++ b/stdlib/LinearAlgebra/test/generic.jl @@ -647,12 +647,64 @@ end @testset "copytrito!" begin n = 10 - A = rand(n, n) - for uplo in ('L', 'U') - B = zeros(n, n) - copytrito!(B, A, uplo) - C = uplo == 'L' ? tril(A) : triu(A) - @test B ≈ C + @testset "square" begin + for A in (rand(n, n), rand(Int8, n, n)), uplo in ('L', 'U') + for AA in (A, view(A, reverse.(axes(A))...)) + C = uplo == 'L' ? tril(AA) : triu(AA) + for B in (zeros(n, n), zeros(n+1, n+2)) + copytrito!(B, AA, uplo) + @test view(B, 1:n, 1:n) == C + end + end + end + end + @testset "wide" begin + for A in (rand(n, 2n), rand(Int8, n, 2n)) + for AA in (A, view(A, reverse.(axes(A))...)) + C = tril(AA) + for (M, N) in ((n, n), (n+1, n), (n, n+1), (n+1, n+1)) + B = zeros(M, N) + copytrito!(B, AA, 'L') + @test view(B, 1:n, 1:n) == view(C, 1:n, 1:n) + end + @test_throws DimensionMismatch copytrito!(zeros(n-1, 2n), AA, 'L') + C = triu(AA) + for (M, N) in ((n, 2n), (n+1, 2n), (n, 2n+1), (n+1, 2n+1)) + B = zeros(M, N) + copytrito!(B, AA, 'U') + @test view(B, 1:n, 1:2n) == view(C, 1:n, 1:2n) + end + @test_throws DimensionMismatch copytrito!(zeros(n+1, 2n-1), AA, 'U') + end + end + end + @testset "tall" begin + for A in (rand(2n, n), rand(Int8, 2n, n)) + for AA in (A, view(A, reverse.(axes(A))...)) + C = triu(AA) + for (M, N) in ((n, n), (n+1, n), (n, n+1), (n+1, n+1)) + B = zeros(M, N) + copytrito!(B, AA, 'U') + @test view(B, 1:n, 1:n) == view(C, 1:n, 1:n) + end + @test_throws DimensionMismatch copytrito!(zeros(n-1, n+1), AA, 'U') + C = tril(AA) + for (M, N) in ((2n, n), (2n, n+1), (2n+1, n), (2n+1, n+1)) + B = zeros(M, N) + copytrito!(B, AA, 'L') + @test view(B, 1:2n, 1:n) == view(C, 1:2n, 1:n) + end + @test_throws DimensionMismatch copytrito!(zeros(n-1, n+1), AA, 'L') + end + end + end + @testset "aliasing" begin + M = Matrix(reshape(1:36, 6, 6)) + A = view(M, 1:5, 1:5) + A2 = Matrix(A) + B = view(M, 2:6, 2:6) + copytrito!(B, A, 'U') + @test UpperTriangular(B) == UpperTriangular(A2) end end diff --git a/stdlib/LinearAlgebra/test/lapack.jl b/stdlib/LinearAlgebra/test/lapack.jl index 652c6c2e27e6c..000438a004b23 100644 --- a/stdlib/LinearAlgebra/test/lapack.jl +++ b/stdlib/LinearAlgebra/test/lapack.jl @@ -805,8 +805,26 @@ end B = zeros(elty, n, n) LinearAlgebra.LAPACK.lacpy!(B, A, uplo) C = uplo == 'L' ? tril(A) : (uplo == 'U' ? triu(A) : A) - @test B ≈ C + @test B == C + B = zeros(elty, n+1, n+1) + LinearAlgebra.LAPACK.lacpy!(B, A, uplo) + C = uplo == 'L' ? tril(A) : (uplo == 'U' ? triu(A) : A) + @test view(B, 1:n, 1:n) == C end + A = rand(elty, n, n+1) + B = zeros(elty, n, n) + LinearAlgebra.LAPACK.lacpy!(B, A, 'L') + @test B == view(tril(A), 1:n, 1:n) + B = zeros(elty, n, n+1) + LinearAlgebra.LAPACK.lacpy!(B, A, 'U') + @test B == triu(A) + A = rand(elty, n+1, n) + B = zeros(elty, n, n) + LinearAlgebra.LAPACK.lacpy!(B, A, 'U') + @test B == view(triu(A), 1:n, 1:n) + B = zeros(elty, n+1, n) + LinearAlgebra.LAPACK.lacpy!(B, A, 'L') + @test B == tril(A) end end diff --git a/stdlib/LinearAlgebra/test/triangular.jl b/stdlib/LinearAlgebra/test/triangular.jl index 97bf756044c66..d4dff286c7997 100644 --- a/stdlib/LinearAlgebra/test/triangular.jl +++ b/stdlib/LinearAlgebra/test/triangular.jl @@ -1050,4 +1050,10 @@ end end end +@testset "type-stable eigvecs" begin + D = Float64[1 0; 0 2] + V = @inferred eigvecs(UpperTriangular(D)) + @test V == Diagonal([1, 1]) +end + end # module TestTriangular diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 0d9ae50e19a8f..81fcdb434fc92 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,4 +1,4 @@ PKG_BRANCH = release-1.11 -PKG_SHA1 = f112626414a4f666306adb84936ea85aec77c89d +PKG_SHA1 = 280f702c1ace34b51cd4551ed448b19516c36849 PKG_GIT_URL := https://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 9888654dc21c5..431e78551ed2f 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -76,9 +76,10 @@ mutable struct MIState key_repeats::Int last_action::Symbol current_action::Symbol + async_channel::Channel{Function} end -MIState(i, mod, c, a, m) = MIState(i, mod, c, a, m, String[], 0, Char[], 0, :none, :none) +MIState(i, mod, c, a, m) = MIState(i, mod, c, a, m, String[], 0, Char[], 0, :none, :none, Channel{Function}()) const BufferLike = Union{MIState,ModeState,IOBuffer} const State = Union{MIState,ModeState} @@ -2310,7 +2311,7 @@ keymap_data(state, ::Union{HistoryPrompt, PrefixHistoryPrompt}) = state Base.isempty(s::PromptState) = s.input_buffer.size == 0 -on_enter(s::PromptState) = s.p.on_enter(s) +on_enter(s::MIState) = state(s).p.on_enter(s) move_input_start(s::BufferLike) = (seek(buffer(s), 0); nothing) move_input_end(buf::IOBuffer) = (seekend(buf); nothing) @@ -2823,44 +2824,61 @@ keymap_data(ms::MIState, m::ModalInterface) = keymap_data(state(ms), mode(ms)) function prompt!(term::TextTerminal, prompt::ModalInterface, s::MIState = init_state(term, prompt)) Base.reseteof(term) + l = Base.ReentrantLock() + t1 = Threads.@spawn :interactive while true + wait(s.async_channel) + status = @lock l begin + fcn = take!(s.async_channel) + fcn(s) + end + status ∈ (:ok, :ignore) || break + end raw!(term, true) enable_bracketed_paste(term) try activate(prompt, s, term, term) old_state = mode(s) - while true - kmap = keymap(s, prompt) - fcn = match_input(kmap, s) - kdata = keymap_data(s, prompt) - s.current_action = :unknown # if the to-be-run action doesn't update this field, - # :unknown will be recorded in the last_action field - local status - # errors in keymaps shouldn't cause the REPL to fail, so wrap in a - # try/catch block - try - status = fcn(s, kdata) - catch e - @error "Error in the keymap" exception=e,catch_backtrace() - # try to cleanup and get `s` back to its original state before returning - transition(s, :reset) - transition(s, old_state) - status = :done - end - status !== :ignore && (s.last_action = s.current_action) - if status === :abort - s.aborted = true - return buffer(s), false, false - elseif status === :done - return buffer(s), true, false - elseif status === :suspend - if Sys.isunix() - return buffer(s), true, true + # spawn this because the main repl task is sticky (due to use of @async and _wait2) + # and we want to not block typing when the repl task thread is busy + t2 = Threads.@spawn :interactive while true + eof(term) || peek(term) # wait before locking but don't consume + @lock l begin + kmap = keymap(s, prompt) + fcn = match_input(kmap, s) + kdata = keymap_data(s, prompt) + s.current_action = :unknown # if the to-be-run action doesn't update this field, + # :unknown will be recorded in the last_action field + local status + # errors in keymaps shouldn't cause the REPL to fail, so wrap in a + # try/catch block + try + status = fcn(s, kdata) + catch e + @error "Error in the keymap" exception=e,catch_backtrace() + # try to cleanup and get `s` back to its original state before returning + transition(s, :reset) + transition(s, old_state) + status = :done + end + status !== :ignore && (s.last_action = s.current_action) + if status === :abort + s.aborted = true + return buffer(s), false, false + elseif status === :done + return buffer(s), true, false + elseif status === :suspend + if Sys.isunix() + return buffer(s), true, true + end + else + @assert status ∈ (:ok, :ignore) end - else - @assert status ∈ (:ok, :ignore) end end + return fetch(t2) finally + put!(s.async_channel, Returns(:done)) + wait(t1) raw!(term, false) && disable_bracketed_paste(term) end # unreachable diff --git a/stdlib/REPL/src/Pkg_beforeload.jl b/stdlib/REPL/src/Pkg_beforeload.jl new file mode 100644 index 0000000000000..7d80cfee3d54f --- /dev/null +++ b/stdlib/REPL/src/Pkg_beforeload.jl @@ -0,0 +1,114 @@ +## Pkg stuff needed before Pkg has loaded + +const Pkg_pkgid = Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg") +const Pkg_REPLExt_pkgid = Base.PkgId(Base.UUID("ceef7b17-42e7-5b1c-81d4-4cc4a2494ccf"), "REPLExt") + +function load_pkg() + @lock Base.require_lock begin + REPLExt = Base.require_stdlib(Pkg_pkgid, "REPLExt") + # require_stdlib does not guarantee that the `__init__` of the package is done when loading is done async + # but we need to wait for the repl mode to be set up + lock = get(Base.package_locks, Pkg_REPLExt_pkgid.uuid, nothing) + lock !== nothing && wait(lock[2]) + return REPLExt + end +end + +## Below here copied/tweaked from Pkg Types.jl so that the dummy Pkg prompt +# can populate the env correctly before Pkg loads + +function safe_realpath(path) + isempty(path) && return path + if ispath(path) + try + return realpath(path) + catch + return path + end + end + a, b = splitdir(path) + return joinpath(safe_realpath(a), b) +end + +function find_project_file(env::Union{Nothing,String}=nothing) + project_file = nothing + if env isa Nothing + project_file = Base.active_project() + project_file === nothing && return nothing # in the Pkg version these are pkgerrors + elseif startswith(env, '@') + project_file = Base.load_path_expand(env) + project_file === nothing && return nothing + elseif env isa String + if isdir(env) + isempty(readdir(env)) || return nothing + project_file = joinpath(env, Base.project_names[end]) + else + project_file = endswith(env, ".toml") ? abspath(env) : + abspath(env, Base.project_names[end]) + end + end + @assert project_file isa String && + (isfile(project_file) || !ispath(project_file) || + isdir(project_file) && isempty(readdir(project_file))) + return safe_realpath(project_file) +end + +function relative_project_path(project_file::String, path::String) + # compute path relative the project + # realpath needed to expand symlinks before taking the relative path + return relpath(safe_realpath(abspath(path)), safe_realpath(dirname(project_file))) +end + +function projname(project_file::String) + if isfile(project_file) + name = try + p = Base.TOML.Parser() + Base.TOML.reinit!(p, read(project_file, String); filepath=project_file) + proj = Base.TOML.parse(p) + get(proj, "name", nothing) + catch + nothing + end + else + name = nothing + end + if name === nothing + name = basename(dirname(project_file)) + end + for depot in Base.DEPOT_PATH + envdir = joinpath(depot, "environments") + if startswith(abspath(project_file), abspath(envdir)) + return "@" * name + end + end + return name +end + +prev_project_file = nothing +prev_project_timestamp = nothing +prev_prefix = "" + +function Pkg_promptf() + global prev_project_timestamp, prev_prefix, prev_project_file + project_file = find_project_file() + prefix = "" + if project_file !== nothing + if prev_project_file == project_file && prev_project_timestamp == mtime(project_file) + prefix = prev_prefix + else + project_name = projname(project_file) + if project_name !== nothing + pname = projname(project_file) + if textwidth(pname) > 30 + pname = first(pname, 27) * "..." + end + prefix = "($(pname)) " + prev_prefix = prefix + prev_project_timestamp = mtime(project_file) + prev_project_file = project_file + end + end + end + # Note no handling of Pkg.offline, as the Pkg version does here + return "$(prefix)pkg> " +end diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 8b540f0220a30..a210c2fe2cf95 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -144,6 +144,8 @@ using .REPLCompletions include("TerminalMenus/TerminalMenus.jl") include("docview.jl") +include("Pkg_beforeload.jl") + @nospecialize # use only declared type signatures answer_color(::AbstractREPL) = "" @@ -242,7 +244,7 @@ function check_for_missing_packages_and_run_hooks(ast) mods = modules_to_be_loaded(ast) filter!(mod -> isnothing(Base.identify_package(String(mod))), mods) # keep missing modules if !isempty(mods) - isempty(install_packages_hooks) && Base.require_stdlib(Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg")) + isempty(install_packages_hooks) && load_pkg() for f in install_packages_hooks Base.invokelatest(f, mods) && return end @@ -536,6 +538,7 @@ mutable struct LineEditREPL <: AbstractREPL answer_color::String shell_color::String help_color::String + pkg_color::String history_file::Bool in_shell::Bool in_help::Bool @@ -548,13 +551,13 @@ mutable struct LineEditREPL <: AbstractREPL interface::ModalInterface backendref::REPLBackendRef frontend_task::Task - function LineEditREPL(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell,in_help,envcolors) + function LineEditREPL(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,pkg_color,history_file,in_shell,in_help,envcolors) opts = Options() opts.hascolor = hascolor if !hascolor opts.beep_colors = [""] end - new(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,history_file,in_shell, + new(t,hascolor,prompt_color,input_color,answer_color,shell_color,help_color,pkg_color,history_file,in_shell, in_help,envcolors,false,nothing, opts, nothing, Tuple{String,Int}[]) end end @@ -571,6 +574,7 @@ LineEditREPL(t::TextTerminal, hascolor::Bool, envcolors::Bool=false) = hascolor ? Base.answer_color() : "", hascolor ? Base.text_colors[:red] : "", hascolor ? Base.text_colors[:yellow] : "", + hascolor ? Base.text_colors[:blue] : "", false, false, false, envcolors ) @@ -1041,8 +1045,6 @@ setup_interface( extra_repl_keymap::Any = repl.options.extra_keymap ) = setup_interface(repl, hascolor, extra_repl_keymap) -# we have to grab this after Pkg is loaded so cache it -pkg_mode::Union{Nothing,LineEdit.Prompt} = nothing # This non keyword method can be precompiled which is important function setup_interface( @@ -1119,6 +1121,34 @@ function setup_interface( end, sticky = true) + # Set up dummy Pkg mode that will be replaced once Pkg is loaded + # use 6 dots to occupy the same space as the most likely "@v1.xx" env name + dummy_pkg_mode = Prompt(Pkg_promptf, + prompt_prefix = hascolor ? repl.pkg_color : "", + prompt_suffix = hascolor ? + (repl.envcolors ? Base.input_color : repl.input_color) : "", + repl = repl, + complete = LineEdit.EmptyCompletionProvider(), + on_done = respond(line->nothing, repl, julia_prompt), + on_enter = function (s::MIState) + # This is hit when the user tries to execute a command before the real Pkg mode has been + # switched to. Ok to do this even if Pkg is loading on the other task because of the loading lock. + REPLExt = load_pkg() + if REPLExt isa Module && isdefined(REPLExt, :PkgCompletionProvider) + for mode in repl.interface.modes + if mode isa LineEdit.Prompt && mode.complete isa REPLExt.PkgCompletionProvider + # pkg mode + buf = copy(LineEdit.buffer(s)) + transition(s, mode) do + LineEdit.state(s, mode).input_buffer = buf + end + end + end + end + return true + end, + sticky = true) + ################################# Stage II ############################# @@ -1126,7 +1156,8 @@ function setup_interface( # We will have a unified history for all REPL modes hp = REPLHistoryProvider(Dict{Symbol,Prompt}(:julia => julia_prompt, :shell => shell_mode, - :help => help_mode)) + :help => help_mode, + :pkg => dummy_pkg_mode)) if repl.history_file try hist_path = find_hist_file() @@ -1149,6 +1180,7 @@ function setup_interface( julia_prompt.hist = hp shell_mode.hist = hp help_mode.hist = hp + dummy_pkg_mode.hist = hp julia_prompt.on_done = respond(x->Base.parse_input_line(x,filename=repl_filename(repl,hp)), repl, julia_prompt) @@ -1189,29 +1221,39 @@ function setup_interface( end, ']' => function (s::MIState,o...) if isempty(s) || position(LineEdit.buffer(s)) == 0 - global pkg_mode - if pkg_mode === nothing - pkgid = Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg") - REPLExt = Base.require_stdlib(pkgid, "REPLExt") - pkg_mode = nothing + buf = copy(LineEdit.buffer(s)) + transition(s, dummy_pkg_mode) do + LineEdit.state(s, dummy_pkg_mode).input_buffer = buf + end + # load Pkg on another thread if available so that typing in the dummy Pkg prompt + # isn't blocked, but instruct the main REPL task to do the transition via s.async_channel + t_replswitch = Threads.@spawn begin + REPLExt = load_pkg() if REPLExt isa Module && isdefined(REPLExt, :PkgCompletionProvider) - for mode in repl.interface.modes - if mode isa LineEdit.Prompt && mode.complete isa REPLExt.PkgCompletionProvider - pkg_mode = mode - break + put!(s.async_channel, + function (s::MIState) + LineEdit.mode(s) === dummy_pkg_mode || return :ok + for mode in repl.interface.modes + if mode isa LineEdit.Prompt && mode.complete isa REPLExt.PkgCompletionProvider + buf = copy(LineEdit.buffer(s)) + transition(s, mode) do + LineEdit.state(s, mode).input_buffer = buf + end + if !isempty(s) && @invokelatest(LineEdit.check_for_hint(s)) + @invokelatest(LineEdit.refresh_line(s)) + end + break + end + end + return :ok end - end - end - end - if pkg_mode !== nothing - buf = copy(LineEdit.buffer(s)) - transition(s, pkg_mode) do - LineEdit.state(s, pkg_mode).input_buffer = buf + ) end - return end + Base.errormonitor(t_replswitch) + else + edit_insert(s, ']') end - edit_insert(s, ']') end, # Bracketed Paste Mode @@ -1391,9 +1433,9 @@ function setup_interface( b = Dict{Any,Any}[skeymap, mk, prefix_keymap, LineEdit.history_keymap, LineEdit.default_keymap, LineEdit.escape_defaults] prepend!(b, extra_repl_keymap) - shell_mode.keymap_dict = help_mode.keymap_dict = LineEdit.keymap(b) + shell_mode.keymap_dict = help_mode.keymap_dict = dummy_pkg_mode.keymap_dict = LineEdit.keymap(b) - allprompts = LineEdit.TextInterface[julia_prompt, shell_mode, help_mode, search_prompt, prefix_prompt] + allprompts = LineEdit.TextInterface[julia_prompt, shell_mode, help_mode, dummy_pkg_mode, search_prompt, prefix_prompt] return ModalInterface(allprompts) end diff --git a/stdlib/REPL/src/precompile.jl b/stdlib/REPL/src/precompile.jl index 7299742eaef1c..7c45bc8a0be5b 100644 --- a/stdlib/REPL/src/precompile.jl +++ b/stdlib/REPL/src/precompile.jl @@ -220,4 +220,8 @@ end generate_precompile_statements() precompile(Tuple{typeof(getproperty), REPL.REPLBackend, Symbol}) +precompile(Tuple{typeof(Base.take!), Base.Channel{Function}}) +precompile(Tuple{typeof(Base.put!), Base.Channel{Function}, Function}) +precompile(Tuple{typeof(Core.kwcall), NamedTuple{names, T} where T<:Tuple where names, typeof(REPL.LineEdit.complete_line), REPL.LineEdit.EmptyCompletionProvider, Any}) + end # Precompile diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index 83df34a056578..671c45a6bf2b1 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -500,8 +500,9 @@ for prompt = ["TestΠ", () -> randstring(rand(1:10))] repl_mode = repl.interface.modes[1] shell_mode = repl.interface.modes[2] help_mode = repl.interface.modes[3] - histp = repl.interface.modes[4] - prefix_mode = repl.interface.modes[5] + pkg_mode = repl.interface.modes[4] + histp = repl.interface.modes[5] + prefix_mode = repl.interface.modes[6] hp = REPL.REPLHistoryProvider(Dict{Symbol,Any}(:julia => repl_mode, :shell => shell_mode, @@ -1559,8 +1560,9 @@ for prompt = ["TestΠ", () -> randstring(rand(1:10))] repl_mode = repl.interface.modes[1] shell_mode = repl.interface.modes[2] help_mode = repl.interface.modes[3] - histp = repl.interface.modes[4] - prefix_mode = repl.interface.modes[5] + pkg_mode = repl.interface.modes[4] + histp = repl.interface.modes[5] + prefix_mode = repl.interface.modes[6] hp = REPL.REPLHistoryProvider(Dict{Symbol,Any}(:julia => repl_mode, :shell => shell_mode, @@ -1776,3 +1778,25 @@ end @test_broken isempty(undoc) @test undoc == [:AbstractREPL, :BasicREPL, :LineEditREPL, :StreamREPL] end + +@testset "Dummy Pkg prompt" begin + # do this in an empty depot to test default for new users + withenv("JULIA_DEPOT_PATH" => mktempdir(), "JULIA_LOAD_PATH" => nothing) do + prompt = readchomp(`$(Base.julia_cmd()[1]) --startup-file=no -e "using REPL; print(REPL.Pkg_promptf())"`) + @test prompt == "(@v$(VERSION.major).$(VERSION.minor)) pkg> " + end + + get_prompt(proj::String) = readchomp(`$(Base.julia_cmd()[1]) --startup-file=no $(proj) -e "using REPL; print(REPL.Pkg_promptf())"`) + + @test get_prompt("--project=$(pkgdir(REPL))") == "(REPL) pkg> " + + tdir = mkpath(joinpath(mktempdir(), "foo")) + @test get_prompt("--project=$tdir") == "(foo) pkg> " + + proj_file = joinpath(tdir, "Project.toml") + touch(proj_file) # make a bad Project.toml + @test get_prompt("--project=$proj_file") == "(foo) pkg> " + + write(proj_file, "name = \"Bar\"\n") + @test get_prompt("--project=$proj_file") == "(Bar) pkg> " +end diff --git a/stdlib/TOML/Project.toml b/stdlib/TOML/Project.toml index 17fc8be19ec8e..ceb4acf8bbc65 100644 --- a/stdlib/TOML/Project.toml +++ b/stdlib/TOML/Project.toml @@ -6,12 +6,13 @@ version = "1.0.3" Dates = "ade2ca70-3891-5945-98fb-dc099432e06a" [compat] +Dates = "1.11.0" julia = "1.6" [extras] Downloads = "f43a241f-c20a-4ad4-852c-f6b1247861c6" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" Tar = "a4e569a6-e804-4fa4-b0f3-eef7a1d5b13e" +Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" p7zip_jll = "3f19e933-33d8-53b3-aaab-bd5110c3b7a0" [targets] diff --git a/stdlib/TOML/src/TOML.jl b/stdlib/TOML/src/TOML.jl index 858b75a2e0eff..7414b5dc686f4 100644 --- a/stdlib/TOML/src/TOML.jl +++ b/stdlib/TOML/src/TOML.jl @@ -7,6 +7,8 @@ and to serialize Julia data structures to TOML format. """ module TOML +using Dates + module Internals # The parser is defined in Base using Base.TOML: Parser, parse, tryparse, ParserError, isvalid_barekey_char, reinit! @@ -36,6 +38,17 @@ performance if a larger number of small files are parsed. """ const Parser = Internals.Parser +""" + DTParser() + +Constructor for a TOML `Parser` which returns date and time objects from Dates. +""" +function DTParser(args...; kwargs...) + parser = Parser(args...; kwargs...) + parser.Dates = Dates + return parser +end + """ parsefile(f::AbstractString) parsefile(p::Parser, f::AbstractString) @@ -46,7 +59,7 @@ Parse file `f` and return the resulting table (dictionary). Throw a See also [`TOML.tryparsefile`](@ref). """ parsefile(f::AbstractString) = - Internals.parse(Parser(readstring(f); filepath=abspath(f))) + Internals.parse(DTParser(readstring(f); filepath=abspath(f))) parsefile(p::Parser, f::AbstractString) = Internals.parse(Internals.reinit!(p, readstring(f); filepath=abspath(f))) @@ -60,7 +73,7 @@ Parse file `f` and return the resulting table (dictionary). Return a See also [`TOML.parsefile`](@ref). """ tryparsefile(f::AbstractString) = - Internals.tryparse(Parser(readstring(f); filepath=abspath(f))) + Internals.tryparse(DTParser(readstring(f); filepath=abspath(f))) tryparsefile(p::Parser, f::AbstractString) = Internals.tryparse(Internals.reinit!(p, readstring(f); filepath=abspath(f))) @@ -74,7 +87,7 @@ Throw a [`ParserError`](@ref) upon failure. See also [`TOML.tryparse`](@ref). """ parse(str::AbstractString) = - Internals.parse(Parser(String(str))) + Internals.parse(DTParser(String(str))) parse(p::Parser, str::AbstractString) = Internals.parse(Internals.reinit!(p, String(str))) parse(io::IO) = parse(read(io, String)) @@ -90,7 +103,7 @@ Return a [`ParserError`](@ref) upon failure. See also [`TOML.parse`](@ref). """ tryparse(str::AbstractString) = - Internals.tryparse(Parser(String(str))) + Internals.tryparse(DTParser(String(str))) tryparse(p::Parser, str::AbstractString) = Internals.tryparse(Internals.reinit!(p, String(str))) tryparse(io::IO) = tryparse(read(io, String)) diff --git a/stdlib/TOML/src/print.jl b/stdlib/TOML/src/print.jl index 91ea4fd392e4b..168a7f63c6b5b 100644 --- a/stdlib/TOML/src/print.jl +++ b/stdlib/TOML/src/print.jl @@ -34,7 +34,8 @@ function print_toml_escaped(io::IO, s::AbstractString) end const MbyFunc = Union{Function, Nothing} -const TOMLValue = Union{AbstractVector, AbstractDict, Dates.DateTime, Dates.Time, Dates.Date, Bool, Integer, AbstractFloat, AbstractString} +const TOMLValue = Union{AbstractVector, AbstractDict, Bool, Integer, AbstractFloat, AbstractString, + Dates.DateTime, Dates.Time, Dates.Date, Base.TOML.DateTime, Base.TOML.Time, Base.TOML.Date} ######## @@ -89,6 +90,9 @@ function printvalue(f::MbyFunc, io::IO, value::AbstractVector, sorted::Bool) end function printvalue(f::MbyFunc, io::IO, value::TOMLValue, sorted::Bool) + value isa Base.TOML.DateTime && (value = Dates.DateTime(value)) + value isa Base.TOML.Time && (value = Dates.Time(value)) + value isa Base.TOML.Date && (value = Dates.Date(value)) value isa Dates.DateTime ? Base.print(io, Dates.format(value, Dates.dateformat"YYYY-mm-dd\THH:MM:SS.sss\Z")) : value isa Dates.Time ? Base.print(io, Dates.format(value, Dates.dateformat"HH:MM:SS.sss")) : value isa Dates.Date ? Base.print(io, Dates.format(value, Dates.dateformat"YYYY-mm-dd")) : diff --git a/sysimage.mk b/sysimage.mk index b5f47be0160a4..96f26b071a997 100644 --- a/sysimage.mk +++ b/sysimage.mk @@ -17,7 +17,7 @@ $(build_private_libdir)/%.$(SHLIB_EXT): $(build_private_libdir)/%-o.a @$(call PRINT_LINK, $(CXX) $(LDFLAGS) -shared $(fPIC) -L$(build_private_libdir) -L$(build_libdir) -L$(build_shlibdir) -o $@ \ $(WHOLE_ARCHIVE) $< $(NO_WHOLE_ARCHIVE) \ $(if $(findstring -debug,$(notdir $@)),-ljulia-internal-debug -ljulia-debug,-ljulia-internal -ljulia) \ - $$([ $(OS) = WINNT ] && echo '' -lssp)) + $$([ $(OS) = WINNT ] && echo '' $(LIBM) -lssp --disable-auto-import --disable-runtime-pseudo-reloc)) @$(INSTALL_NAME_CMD)$(notdir $@) $@ @$(DSYMUTIL) $@ diff --git a/test/arrayops.jl b/test/arrayops.jl index 1c36453a6adae..0659cebca1e86 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -3187,42 +3187,23 @@ end end end -@testset "Wrapping Memory into Arrays with view and reshape" begin - mem::Memory{Int} = Memory{Int}(undef, 10) .= 11:20 - - @test_throws DimensionMismatch reshape(mem, 10, 10) - @test_throws DimensionMismatch reshape(mem, 5) - @test_throws BoundsError view(mem, 1:10, 1:10) - @test_throws BoundsError view(mem, 1:11) - @test_throws BoundsError view(mem, 3:11) - @test_throws BoundsError view(mem, 0:4) - - @test @inferred(view(mem, 1:5))::Vector{Int} == 11:15 - @test @inferred(view(mem, 1:2))::Vector{Int} == 11:12 - @test @inferred(view(mem, 1:10))::Vector{Int} == 11:20 - @test @inferred(view(mem, 3:8))::Vector{Int} == 13:18 - @test @inferred(view(mem, 20:19))::Vector{Int} == [] - @test @inferred(view(mem, -5:-7))::Vector{Int} == [] - @test @inferred(view(mem, :))::Vector{Int} == mem - @test @inferred(reshape(mem, 5, 2))::Matrix{Int} == reshape(11:20, 5, 2) - - # 53990 - @test @inferred(view(mem, unsigned(1):10))::Vector{Int} == 11:20 - - empty_mem = Memory{Module}(undef, 0) - @test_throws BoundsError view(empty_mem, 0:1) - @test_throws BoundsError view(empty_mem, 1:2) - @test_throws DimensionMismatch reshape(empty_mem, 1) - @test_throws DimensionMismatch reshape(empty_mem, 1, 2, 3) - @test_throws ArgumentError reshape(empty_mem, 2^16, 2^16, 2^16, 2^16) - - @test @inferred(view(empty_mem, 1:0))::Vector{Module} == [] - @test @inferred(view(empty_mem, 10:3))::Vector{Module} == [] - @test @inferred(view(empty_mem, :))::Vector{Module} == empty_mem - @test isempty(@inferred(reshape(empty_mem, 0, 7, 1))::Array{Module, 3}) - - offset_inds = OffsetArrays.IdOffsetRange(values=3:6, indices=53:56) - @test @inferred(view(collect(mem), offset_inds)) == view(mem, offset_inds) +@testset "Wrapping Memory into Arrays" begin + mem = Memory{Int}(undef, 10) .= 1 + memref = MemoryRef(mem) + @test_throws DimensionMismatch Base.wrap(Array, mem, (10, 10)) + @test Base.wrap(Array, mem, (5,)) == ones(Int, 5) + @test Base.wrap(Array, mem, 2) == ones(Int, 2) + @test Base.wrap(Array, memref, 10) == ones(Int, 10) + @test Base.wrap(Array, memref, (2,2,2)) == ones(Int,2,2,2) + @test Base.wrap(Array, mem, (5, 2)) == ones(Int, 5, 2) + + memref2 = MemoryRef(mem, 3) + @test Base.wrap(Array, memref2, (5,)) == ones(Int, 5) + @test Base.wrap(Array, memref2, 2) == ones(Int, 2) + @test Base.wrap(Array, memref2, (2,2,2)) == ones(Int,2,2,2) + @test Base.wrap(Array, memref2, (3, 2)) == ones(Int, 3, 2) + @test_throws DimensionMismatch Base.wrap(Array, memref2, 9) + @test_throws DimensionMismatch Base.wrap(Array, memref2, 10) end @testset "Memory size" begin diff --git a/test/compiler/AbstractInterpreter.jl b/test/compiler/AbstractInterpreter.jl index 2068997c77c82..507773dabb35f 100644 --- a/test/compiler/AbstractInterpreter.jl +++ b/test/compiler/AbstractInterpreter.jl @@ -10,7 +10,7 @@ include("newinterp.jl") # OverlayMethodTable # ================== -using Base.Experimental: @MethodTable, @overlay +using Base.Experimental: @MethodTable, @overlay, @consistent_overlay # @overlay method with return type annotation @MethodTable RT_METHOD_DEF @@ -20,8 +20,8 @@ using Base.Experimental: @MethodTable, @overlay end @newinterp MTOverlayInterp -@MethodTable OverlayedMT -CC.method_table(interp::MTOverlayInterp) = CC.OverlayMethodTable(CC.get_inference_world(interp), OverlayedMT) +@MethodTable OVERLAY_MT +CC.method_table(interp::MTOverlayInterp) = CC.OverlayMethodTable(CC.get_inference_world(interp), OVERLAY_MT) function CC.add_remark!(interp::MTOverlayInterp, ::CC.InferenceState, remark) if interp.meta !== nothing @@ -32,7 +32,8 @@ function CC.add_remark!(interp::MTOverlayInterp, ::CC.InferenceState, remark) end strangesin(x) = sin(x) -@overlay OverlayedMT strangesin(x::Float64) = iszero(x) ? nothing : cos(x) +@overlay OVERLAY_MT strangesin(x::Float64) = + iszero(x) ? throw(StrangeSinError()) : x < 0 ? nothing : cos(x) # inference should use the overlayed method table @test Base.return_types((Float64,); interp=MTOverlayInterp()) do x @@ -84,7 +85,7 @@ end |> only === Float64 # not fully covered overlay method match overlay_match(::Any) = nothing -@overlay OverlayedMT overlay_match(::Int) = missing +@overlay OVERLAY_MT overlay_match(::Int) = missing @test Base.return_types((Any,); interp=MTOverlayInterp()) do x overlay_match(x) end |> only === Union{Nothing,Missing} @@ -116,11 +117,48 @@ Base.@assume_effects :total totalcall(f, args...) = f(args...) end end |> only === Nothing +# override `:native_executable` to allow concrete-eval for overlay-ed methods +function myfactorial(x::Int, raise) + res = 1 + 0 ≤ x < 20 || raise("x is too big") + Base.@assume_effects :terminates_locally while x > 1 + res *= x + x -= 1 + end + return res +end +raise_on_gpu1(x) = error(x) +@overlay OVERLAY_MT @noinline raise_on_gpu1(x) = #=do something with GPU=# error(x) +raise_on_gpu2(x) = error(x) +@consistent_overlay OVERLAY_MT @noinline raise_on_gpu2(x) = #=do something with GPU=# error(x) +raise_on_gpu3(x) = error(x) +@consistent_overlay OVERLAY_MT @noinline Base.@assume_effects :foldable raise_on_gpu3(x) = #=do something with GPU=# error_on_gpu(x) +cpu_factorial(x::Int) = myfactorial(x, error) +gpu_factorial1(x::Int) = myfactorial(x, raise_on_gpu1) +gpu_factorial2(x::Int) = myfactorial(x, raise_on_gpu2) +gpu_factorial3(x::Int) = myfactorial(x, raise_on_gpu3) + +@test Base.infer_effects(cpu_factorial, (Int,); interp=MTOverlayInterp()) |> Core.Compiler.is_nonoverlayed +@test Base.infer_effects(gpu_factorial1, (Int,); interp=MTOverlayInterp()) |> !Core.Compiler.is_nonoverlayed +@test Base.infer_effects(gpu_factorial2, (Int,); interp=MTOverlayInterp()) |> Core.Compiler.is_consistent_overlay +let effects = Base.infer_effects(gpu_factorial3, (Int,); interp=MTOverlayInterp()) + # check if `@consistent_overlay` together works with `@assume_effects` + # N.B. the overlaid `raise_on_gpu3` is not :foldable otherwise since `error_on_gpu` is (intetionally) undefined. + @test Core.Compiler.is_consistent_overlay(effects) + @test Core.Compiler.is_foldable(effects) +end +@test Base.infer_return_type(; interp=MTOverlayInterp()) do + Val(gpu_factorial2(3)) +end == Val{6} +@test Base.infer_return_type(; interp=MTOverlayInterp()) do + Val(gpu_factorial3(3)) +end == Val{6} + # GPUCompiler needs accurate inference through kwfunc with the overlay of `Core.throw_inexacterror` # https://github.com/JuliaLang/julia/issues/48097 @newinterp Issue48097Interp -@MethodTable Issue48097MT -CC.method_table(interp::Issue48097Interp) = CC.OverlayMethodTable(CC.get_inference_world(interp), Issue48097MT) +@MethodTable ISSUE_48097_MT +CC.method_table(interp::Issue48097Interp) = CC.OverlayMethodTable(CC.get_inference_world(interp), ISSUE_48097_MT) CC.InferenceParams(::Issue48097Interp) = CC.InferenceParams(; unoptimize_throw_blocks=false) function CC.concrete_eval_eligible(interp::Issue48097Interp, @nospecialize(f), result::CC.MethodCallResult, arginfo::CC.ArgInfo, sv::CC.AbsIntState) @@ -132,34 +170,59 @@ function CC.concrete_eval_eligible(interp::Issue48097Interp, end return ret end -@overlay Issue48097MT @noinline Core.throw_inexacterror(f::Symbol, ::Type{T}, val) where {T} = return +@overlay ISSUE_48097_MT @noinline Core.throw_inexacterror(f::Symbol, ::Type{T}, val) where {T} = return issue48097(; kwargs...) = return 42 -@test_broken fully_eliminated(; interp=Issue48097Interp(), retval=42) do +@test fully_eliminated(; interp=Issue48097Interp(), retval=42) do issue48097(; a=1f0, b=1.0) end +# https://github.com/JuliaLang/julia/issues/52938 +@newinterp Issue52938Interp +@MethodTable ISSUE_52938_MT +CC.method_table(interp::Issue52938Interp) = CC.OverlayMethodTable(CC.get_inference_world(interp), ISSUE_52938_MT) +inner52938(x, types::Type, args...; kwargs...) = x +outer52938(x) = @inline inner52938(x, Tuple{}; foo=Ref(42), bar=1) +@test fully_eliminated(outer52938, (Any,); interp=Issue52938Interp(), retval=Argument(2)) + +# https://github.com/JuliaGPU/CUDA.jl/issues/2241 +@newinterp Cuda2241Interp +@MethodTable CUDA_2241_MT +CC.method_table(interp::Cuda2241Interp) = CC.OverlayMethodTable(CC.get_inference_world(interp), CUDA_2241_MT) +inner2241(f, types::Type, args...; kwargs...) = nothing +function outer2241(f) + @inline inner2241(f, Tuple{}; foo=Ref(42), bar=1) + return nothing +end +# NOTE CUDA.jl overlays `throw_boundserror` in a way that causes effects, but these effects +# are ignored for this call graph at the `@assume_effects` annotation on `typejoin`. +# Here it's important to use `@consistent_overlay` to avoid tainting the `:nonoverlayed` bit. +const cuda_kernel_state = Ref{Any}() +@consistent_overlay CUDA_2241_MT @inline Base.throw_boundserror(A, I) = + (cuda_kernel_state[] = (A, I); error()) +@test fully_eliminated(outer2241, (Nothing,); interp=Cuda2241Interp(), retval=nothing) + # Should not concrete-eval overlayed methods in semi-concrete interpretation @newinterp OverlaySinInterp -@MethodTable OverlaySinMT -CC.method_table(interp::OverlaySinInterp) = CC.OverlayMethodTable(CC.get_inference_world(interp), OverlaySinMT) +@MethodTable OVERLAY_SIN_MT +CC.method_table(interp::OverlaySinInterp) = CC.OverlayMethodTable(CC.get_inference_world(interp), OVERLAY_SIN_MT) overlay_sin1(x) = error("Not supposed to be called.") -@overlay OverlaySinMT overlay_sin1(x) = cos(x) -@overlay OverlaySinMT Base.sin(x::Union{Float32,Float64}) = overlay_sin1(x) +@overlay OVERLAY_SIN_MT overlay_sin1(x) = cos(x) +@overlay OVERLAY_SIN_MT Base.sin(x::Union{Float32,Float64}) = overlay_sin1(x) let oc = Base.code_ircode(; interp=OverlaySinInterp()) do sin(0.) end |> only |> first |> Core.OpaqueClosure @test oc() == cos(0.) end -@overlay OverlaySinMT Base.sin(x::Union{Float32,Float64}) = @noinline overlay_sin1(x) +@overlay OVERLAY_SIN_MT Base.sin(x::Union{Float32,Float64}) = @noinline overlay_sin1(x) let oc = Base.code_ircode(; interp=OverlaySinInterp()) do sin(0.) end |> only |> first |> Core.OpaqueClosure @test oc() == cos(0.) end _overlay_sin2(x) = error("Not supposed to be called.") -@overlay OverlaySinMT _overlay_sin2(x) = cos(x) +@overlay OVERLAY_SIN_MT _overlay_sin2(x) = cos(x) overlay_sin2(x) = _overlay_sin2(x) -@overlay OverlaySinMT Base.sin(x::Union{Float32,Float64}) = @noinline overlay_sin2(x) +@overlay OVERLAY_SIN_MT Base.sin(x::Union{Float32,Float64}) = @noinline overlay_sin2(x) let oc = Base.code_ircode(; interp=OverlaySinInterp()) do sin(0.) end |> only |> first |> Core.OpaqueClosure diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index a95ef3225c3f3..e8f43236c1bd1 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -887,3 +887,19 @@ end ex54166 = Union{Missing, Int64}[missing -2; missing -2]; dims54166 = (1,2) @test (minimum(ex54166; dims=dims54166)[1] === missing) + +function foo54599() + pkginfo = @noinline bar54599() + pkgid = pkginfo !== nothing ? pkginfo[1] : nothing + @noinline println(devnull, pkgid) + pkgid.uuid !== nothing ? pkgid.uuid : false +end + +#this function used to crash allocopt due to a no predecessors bug +barnopreds() = Base.inferencebarrier(true) ? (Base.PkgId(Test),1) : nothing +function foonopreds() + pkginfo = @noinline barnopreds() + pkgid = pkginfo !== nothing ? pkginfo[1] : nothing + pkgid.uuid !== nothing ? pkgid.uuid : false +end +@test foonopreds() !== nothing diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index 36c28b6c844bf..0b458905e4217 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -89,13 +89,13 @@ Base.@assume_effects :terminates_globally function recur_termination1(x) 0 ≤ x < 20 || error("bad fact") return x * recur_termination1(x-1) end -@test_broken Core.Compiler.is_foldable(Base.infer_effects(recur_termination1, (Int,))) +@test Core.Compiler.is_foldable(Base.infer_effects(recur_termination1, (Int,))) @test Core.Compiler.is_terminates(Base.infer_effects(recur_termination1, (Int,))) function recur_termination2() Base.@assume_effects :total !:terminates_globally recur_termination1(12) end -@test_broken fully_eliminated(recur_termination2) +@test fully_eliminated(recur_termination2) @test fully_eliminated() do; recur_termination2(); end Base.@assume_effects :terminates_globally function recur_termination21(x) @@ -104,15 +104,15 @@ Base.@assume_effects :terminates_globally function recur_termination21(x) return recur_termination22(x) end recur_termination22(x) = x * recur_termination21(x-1) -@test_broken Core.Compiler.is_foldable(Base.infer_effects(recur_termination21, (Int,))) -@test_broken Core.Compiler.is_foldable(Base.infer_effects(recur_termination22, (Int,))) +@test Core.Compiler.is_foldable(Base.infer_effects(recur_termination21, (Int,))) +@test Core.Compiler.is_foldable(Base.infer_effects(recur_termination22, (Int,))) @test Core.Compiler.is_terminates(Base.infer_effects(recur_termination21, (Int,))) @test Core.Compiler.is_terminates(Base.infer_effects(recur_termination22, (Int,))) function recur_termination2x() Base.@assume_effects :total !:terminates_globally recur_termination21(12) + recur_termination22(12) end -@test_broken fully_eliminated(recur_termination2x) +@test fully_eliminated(recur_termination2x) @test fully_eliminated() do; recur_termination2x(); end # anonymous function support for `@assume_effects` @@ -970,7 +970,7 @@ unknown_sparam_nothrow2(x::Ref{Ref{T}}) where T = (T; nothing) abstractly_recursive1() = abstractly_recursive2() abstractly_recursive2() = (Core.Compiler._return_type(abstractly_recursive1, Tuple{}); 1) abstractly_recursive3() = abstractly_recursive2() -@test Core.Compiler.is_terminates(Base.infer_effects(abstractly_recursive3, ())) +@test_broken Core.Compiler.is_terminates(Base.infer_effects(abstractly_recursive3, ())) actually_recursive1(x) = actually_recursive2(x) actually_recursive2(x) = (x <= 0) ? 1 : actually_recursive1(x - 1) actually_recursive3(x) = actually_recursive2(x) diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index d546e70d7d749..b661fcc384268 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -5292,6 +5292,15 @@ end foo51090(b) = return bar51090(b) @test !fully_eliminated(foo51090, (Int,)) +Base.@assume_effects :terminates_globally @noinline function bar51090_terminates(b) + b == 0 && return + r = foo51090_terminates(b - 1) + Base.donotdelete(b) + return r +end +foo51090_terminates(b) = return bar51090_terminates(b) +@test !fully_eliminated(foo51090_terminates, (Int,)) + # exploit throwness from concrete eval for intrinsics @test Base.return_types() do Base.or_int(true, 1) diff --git a/test/compiler/irpasses.jl b/test/compiler/irpasses.jl index c124832030cbd..6d6cd74028594 100644 --- a/test/compiler/irpasses.jl +++ b/test/compiler/irpasses.jl @@ -1820,3 +1820,24 @@ function f53521() end end @test code_typed(f53521)[1][2] === Nothing + +# https://github.com/JuliaLang/julia/issues/54596 +# finalized object's uses have no postdominator +let f = (x)->nothing, mi = Base.method_instance(f, (Base.RefValue{Nothing},)), code = Any[ + # Basic Block 1 + Expr(:new, Base.RefValue{Nothing}, nothing) + Expr(:call, Core.finalizer, f, SSAValue(1), true, mi) + GotoIfNot(false, 6) + # Basic Block 2 + Expr(:call, Base.getfield, SSAValue(1), :x) + ReturnNode(SSAValue(4)) + # Basic Block 3 + Expr(:call, Base.getfield, SSAValue(1), :x) + ReturnNode(SSAValue(6)) +] + ir = make_ircode(code; ssavaluetypes=Any[Base.RefValue{Nothing}, Nothing, Any, Nothing, Any, Nothing, Any]) + inlining = Core.Compiler.InliningState(Core.Compiler.NativeInterpreter()) + Core.Compiler.verify_ir(ir) + ir = Core.Compiler.sroa_pass!(ir, inlining) + Core.Compiler.verify_ir(ir) +end diff --git a/test/core.jl b/test/core.jl index 9950056aa8b32..1d8e260fe4a0a 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5524,9 +5524,6 @@ let a = Base.StringVector(2^17) @test sizeof(c) == 0 end -# issue #53990 / https://github.com/JuliaLang/julia/pull/53896#discussion_r1555087951 -@test Base.StringVector(UInt64(2)) isa Vector{UInt8} - @test_throws ArgumentError eltype(Bottom) # issue #16424, re-evaluating type definitions @@ -8135,6 +8132,7 @@ end @test_broken Int isa Union{Union, Type{Union{Int,T1}} where {T1}} let M = @__MODULE__ + Core.eval(M, :(global a_typed_global)) @test Core.set_binding_type!(M, :a_typed_global, Tuple{Union{Integer,Nothing}}) === nothing @test Core.get_binding_type(M, :a_typed_global) === Tuple{Union{Integer,Nothing}} @test Core.set_binding_type!(M, :a_typed_global, Tuple{Union{Integer,Nothing}}) === nothing diff --git a/test/gc.jl b/test/gc.jl index d5990582cc00a..578fe799fb935 100644 --- a/test/gc.jl +++ b/test/gc.jl @@ -5,11 +5,13 @@ using Test function run_gctest(file) let cmd = `$(Base.julia_cmd()) --depwarn=error --rr-detach --startup-file=no $file` @testset for test_nthreads in (1, 2, 4) - @testset for concurrent_sweep in (0, 1) - new_env = copy(ENV) - new_env["JULIA_NUM_THREADS"] = string(test_nthreads) - new_env["JULIA_NUM_GC_THREADS"] = "$(test_nthreads),$(concurrent_sweep)" - @test success(run(pipeline(setenv(cmd, new_env), stdout = stdout, stderr = stderr))) + @testset for test_nithreads in (0, 1) + @testset for concurrent_sweep in (0, 1) + new_env = copy(ENV) + new_env["JULIA_NUM_THREADS"] = "$test_nthreads,$test_nithreads" + new_env["JULIA_NUM_GC_THREADS"] = "$(test_nthreads),$(concurrent_sweep)" + @test success(run(pipeline(setenv(cmd, new_env), stdout = stdout, stderr = stderr))) + end end end end diff --git a/test/intrinsics.jl b/test/intrinsics.jl index 8e4ab932f5eb6..dc9ce1ecd0039 100644 --- a/test/intrinsics.jl +++ b/test/intrinsics.jl @@ -345,3 +345,16 @@ Base.show(io::IO, a::IntWrap) = print(io, "IntWrap(", a.x, ")") @test r2 isa IntWrap && r2.x === 103 === r[].x && r2 !== r[] end end)() + +@testset "issue #54548" begin + @inline passthrough(ptr::Core.LLVMPtr{T,A}) where {T,A} = Base.llvmcall((""" + define ptr addrspace(1) @entry(ptr addrspace(1) %0) #0 { + entry: + ret ptr addrspace(1) %0 + } + + attributes #0 = { alwaysinline }""", "entry"), + Core.LLVMPtr{T,A}, Tuple{Core.LLVMPtr{T,A}}, ptr) + f(gws) = passthrough(Core.bitcast(Core.LLVMPtr{UInt32,1}, gws)) + f(C_NULL) +end diff --git a/test/iobuffer.jl b/test/iobuffer.jl index d82a68c61f780..0e74595d29d20 100644 --- a/test/iobuffer.jl +++ b/test/iobuffer.jl @@ -277,6 +277,7 @@ end c = zeros(UInt8,8) @test bytesavailable(bstream) == 8 @test !eof(bstream) + @test Base.reseteof(bstream) === nothing # TODO: Actually test intended effect read!(bstream,c) @test c == a[3:10] @test closewrite(bstream) === nothing diff --git a/test/llvmpasses/alloc-opt-pass.ll b/test/llvmpasses/alloc-opt-pass.ll index 6bee0fd325105..5d9030a3866b2 100644 --- a/test/llvmpasses/alloc-opt-pass.ll +++ b/test/llvmpasses/alloc-opt-pass.ll @@ -1,7 +1,5 @@ ; This file is a part of Julia. License is MIT: https://julialang.org/license -; RUN: opt -enable-new-pm=1 --opaque-pointers=0 --load-pass-plugin=libjulia-codegen%shlibext -passes='function(AllocOpt)' -S %s | FileCheck %s --check-prefixes=CHECK,TYPED - ; RUN: opt -enable-new-pm=1 --opaque-pointers=1 --load-pass-plugin=libjulia-codegen%shlibext -passes='function(AllocOpt)' -S %s | FileCheck %s --check-prefixes=CHECK,OPAQUE @tag = external addrspace(10) global {} @@ -21,23 +19,23 @@ ; CHECK-NEXT: br label %L3 ; CHECK: L3: -define void @preserve_branches(i8* %fptr, i1 %b, i1 %b2) { - %pgcstack = call {}*** @julia.get_pgcstack() - %ptls = call {}*** @julia.ptls_states() - %ptls_i8 = bitcast {}*** %ptls to i8* +define void @preserve_branches(ptr %fptr, i1 %b, i1 %b2) { + %pgcstack = call ptr @julia.get_pgcstack() + %ptls = call ptr @julia.ptls_states() + %ptls_i8 = bitcast ptr %ptls to ptr br i1 %b, label %L1, label %L3 -L1: - %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, {} addrspace(10)* @tag) - %tok = call token (...) @llvm.julia.gc_preserve_begin({} addrspace(10)* nonnull %v) +L1: ; preds = %0 + %v = call noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 8, ptr addrspace(10) @tag) + %tok = call token (...) @llvm.julia.gc_preserve_begin(ptr addrspace(10) nonnull %v) call void @external_function() br i1 %b2, label %L2, label %L3 -L2: +L2: ; preds = %L1 call void @external_function() br label %L3 -L3: +L3: ; preds = %L2, %L1, %0 ret void } ; CHECK-LABEL: }{{$}} @@ -56,24 +54,24 @@ L3: ; CHECK-NEXT: br label %L3 ; CHECK: L3: -define void @preserve_branches2(i8* %fptr, i1 %b, i1 %b2) { - %pgcstack = call {}*** @julia.get_pgcstack() - %ptls = call {}*** @julia.ptls_states() - %ptls_i8 = bitcast {}*** %ptls to i8* - %v2 = call {} addrspace(10)* @external_function2() +define void @preserve_branches2(ptr %fptr, i1 %b, i1 %b2) { + %pgcstack = call ptr @julia.get_pgcstack() + %ptls = call ptr @julia.ptls_states() + %ptls_i8 = bitcast ptr %ptls to ptr + %v2 = call ptr addrspace(10) @external_function2() br i1 %b, label %L1, label %L3 -L1: - %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, {} addrspace(10)* @tag) - %tok = call token (...) @llvm.julia.gc_preserve_begin({} addrspace(10)* %v, {} addrspace(10)* nonnull %v2) +L1: ; preds = %0 + %v = call noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 8, ptr addrspace(10) @tag) + %tok = call token (...) @llvm.julia.gc_preserve_begin(ptr addrspace(10) %v, ptr addrspace(10) nonnull %v2) call void @external_function() br i1 %b2, label %L2, label %L3 -L2: +L2: ; preds = %L1 call void @external_function() br label %L3 -L3: +L3: ; preds = %L2, %L1, %0 ret void } ; CHECK-LABEL: }{{$}} @@ -84,26 +82,30 @@ L3: ; CHECK: store [12 x i8] zeroinitializer, ; CHECK: ret void define void @legal_int_types() { - %pgcstack = call {}*** @julia.get_pgcstack() - %ptls = call {}*** @julia.ptls_states() - %ptls_i8 = bitcast {}*** %ptls to i8* - %var1 = call {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 12, {} addrspace(10)* @tag) - %var2 = addrspacecast {} addrspace(10)* %var1 to {} addrspace(11)* - %var3 = call {}* @julia.pointer_from_objref({} addrspace(11)* %var2) + %pgcstack = call ptr @julia.get_pgcstack() + %ptls = call ptr @julia.ptls_states() + %ptls_i8 = bitcast ptr %ptls to ptr + %var1 = call ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 12, ptr addrspace(10) @tag) + %var2 = addrspacecast ptr addrspace(10) %var1 to ptr addrspace(11) + %var3 = call ptr @julia.pointer_from_objref(ptr addrspace(11) %var2) ret void } ; CHECK-LABEL: }{{$}} - declare void @external_function() -declare {} addrspace(10)* @external_function2() -declare {}*** @julia.ptls_states() -declare {}*** @julia.get_pgcstack() -declare noalias {} addrspace(10)* @julia.gc_alloc_obj(i8*, i64, {} addrspace(10)*) -declare {}* @julia.pointer_from_objref({} addrspace(11)*) -declare void @llvm.memcpy.p11i8.p0i8.i64(i8 addrspace(11)* nocapture writeonly, i8* nocapture readonly, i64, i32, i1) -declare void @llvm.memcpy.p0i8.p0i8.i64(i8* nocapture writeonly, i8* nocapture readonly, i64, i1) + +declare ptr addrspace(10) @external_function2() + +declare ptr @julia.ptls_states() + +declare ptr @julia.get_pgcstack() + +declare noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr, i64, ptr addrspace(10)) + +declare ptr @julia.pointer_from_objref(ptr addrspace(11)) + declare token @llvm.julia.gc_preserve_begin(...) + declare void @llvm.julia.gc_preserve_end(token) ; CHECK-LABEL: @memref_collision @@ -120,24 +122,25 @@ declare void @llvm.julia.gc_preserve_end(token) ; CHECK: L2: ; CHECK: load i define void @memref_collision(i64 %x) { - %pgcstack = call {}*** @julia.get_pgcstack() - %ptls = call {}*** @julia.ptls_states() - %ptls_i8 = bitcast {}*** %ptls to i8* - %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, {} addrspace(10)* @tag) - %v_p = bitcast {} addrspace(10)* %v to i64 addrspace(10)* - store i64 %x, i64 addrspace(10)* %v_p - br i1 0, label %L1, label %L2 - -L1: - %v1 = bitcast {} addrspace(10)* %v to {} addrspace(10)* addrspace(10)* - %v1_x = load {} addrspace(10)*, {} addrspace(10)* addrspace(10)* %v1 + %pgcstack = call ptr @julia.get_pgcstack() + %ptls = call ptr @julia.ptls_states() + %ptls_i8 = bitcast ptr %ptls to ptr + %v = call noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 8, ptr addrspace(10) @tag) + %v_p = bitcast ptr addrspace(10) %v to ptr addrspace(10) + store i64 %x, ptr addrspace(10) %v_p, align 4 + br i1 false, label %L1, label %L2 + +L1: ; preds = %0 + %v1 = bitcast ptr addrspace(10) %v to ptr addrspace(10) + %v1_x = load ptr addrspace(10), ptr addrspace(10) %v1, align 8 ret void -L2: - %v2 = bitcast {} addrspace(10)* %v to i64 addrspace(10)* - %v2_x = load i64, i64 addrspace(10)* %v2 +L2: ; preds = %0 + %v2 = bitcast ptr addrspace(10) %v to ptr addrspace(10) + %v2_x = load i64, ptr addrspace(10) %v2, align 4 ret void } + ; CHECK-LABEL: }{{$}} ; CHECK-LABEL: @lifetime_no_preserve_end @@ -146,19 +149,19 @@ L2: ; CHECK: call void @llvm.lifetime.start ; CHECK: store [8 x i8] zeroinitializer, ; CHECK-NOT: call void @llvm.lifetime.end -define void @lifetime_no_preserve_end({}* noalias nocapture noundef nonnull sret({}) %0) { - %pgcstack = call {}*** @julia.get_pgcstack() - %ptls = call {}*** @julia.ptls_states() - %ptls_i8 = bitcast {}*** %ptls to i8* - %v = call noalias {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, {} addrspace(10)* @tag) - %token = call token (...) @llvm.julia.gc_preserve_begin({} addrspace(10)* %v) - %v_derived = addrspacecast {} addrspace(10)* %v to {} addrspace(11)* - %ptr = call nonnull {}* @julia.pointer_from_objref({} addrspace(11)* %v_derived) - %ptr_raw = bitcast {}* %ptr to i8* - call void @external_function() ; safepoint - %ret_raw = bitcast {}* %0 to i8* - call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %ret_raw, i8 * align 8 %ptr_raw, i64 0, i1 false) - %ret_raw2 = bitcast {}* %0 to i8* +define void @lifetime_no_preserve_end(ptr noalias nocapture noundef nonnull sret({}) %0) { + %pgcstack = call ptr @julia.get_pgcstack() + %ptls = call ptr @julia.ptls_states() + %ptls_i8 = bitcast ptr %ptls to ptr + %v = call noalias ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 8, ptr addrspace(10) @tag) + %token = call token (...) @llvm.julia.gc_preserve_begin(ptr addrspace(10) %v) + %v_derived = addrspacecast ptr addrspace(10) %v to ptr addrspace(11) + %ptr = call nonnull ptr @julia.pointer_from_objref(ptr addrspace(11) %v_derived) + %ptr_raw = bitcast ptr %ptr to ptr + call void @external_function() + %ret_raw = bitcast ptr %0 to ptr + call void @llvm.memcpy.p0.p0.i64(ptr align 8 %ret_raw, ptr align 8 %ptr_raw, i64 0, i1 false) + %ret_raw2 = bitcast ptr %0 to ptr ret void } ; CHECK-LABEL: }{{$}} @@ -175,26 +178,49 @@ define void @lifetime_no_preserve_end({}* noalias nocapture noundef nonnull sret ; CHECK-NOT: zeroinitializer ; CHECK: ret void define void @initializers() { - %pgcstack = call {}*** @julia.get_pgcstack() - %ptls = call {}*** @julia.ptls_states() - %ptls_i8 = bitcast {}*** %ptls to i8* - - %var1 = call {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 1, {} addrspace(10)* @tag) #0 - %var2 = addrspacecast {} addrspace(10)* %var1 to {} addrspace(11)* - %var3 = call {}* @julia.pointer_from_objref({} addrspace(11)* %var2) - - %var4 = call {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 2, {} addrspace(10)* @tag) #1 - %var5 = addrspacecast {} addrspace(10)* %var4 to {} addrspace(11)* - %var6 = call {}* @julia.pointer_from_objref({} addrspace(11)* %var5) - - %var7 = call {} addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 3, {} addrspace(10)* @tag) #2 - %var8 = addrspacecast {} addrspace(10)* %var7 to {} addrspace(11)* - %var9 = call {}* @julia.pointer_from_objref({} addrspace(11)* %var8) - + %pgcstack = call ptr @julia.get_pgcstack() + %ptls = call ptr @julia.ptls_states() + %ptls_i8 = bitcast ptr %ptls to ptr + %var1 = call ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 1, ptr addrspace(10) @tag) #1 + %var2 = addrspacecast ptr addrspace(10) %var1 to ptr addrspace(11) + %var3 = call ptr @julia.pointer_from_objref(ptr addrspace(11) %var2) + %var4 = call ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 2, ptr addrspace(10) @tag) #2 + %var5 = addrspacecast ptr addrspace(10) %var4 to ptr addrspace(11) + %var6 = call ptr @julia.pointer_from_objref(ptr addrspace(11) %var5) + %var7 = call ptr addrspace(10) @julia.gc_alloc_obj(ptr %ptls_i8, i64 3, ptr addrspace(10) @tag) #3 + %var8 = addrspacecast ptr addrspace(10) %var7 to ptr addrspace(11) + %var9 = call ptr @julia.pointer_from_objref(ptr addrspace(11) %var8) ret void } ; CHECK-LABEL: }{{$}} -attributes #0 = { allockind("alloc") } -attributes #1 = { allockind("alloc,uninitialized") } -attributes #2 = { allockind("alloc,zeroed") } +; Test that the pass handles dead basic blocks with references to the allocation +; CHECK-LABEL: @nopreds +; CHECK: alloca i8, i64 0, align 1 +; CHECK: call void @llvm.lifetime.start +define swiftcc { ptr addrspace(10), i8 } @nopreds() { +top: + %0 = call ptr addrspace(10) @julia.gc_alloc_obj(ptr null, i64 0, ptr addrspace(10) null) + %1 = addrspacecast ptr addrspace(10) %0 to ptr addrspace(11) + br label %common.ret + +common.ret: ; preds = %union_move9, %top + ret { ptr addrspace(10), i8 } zeroinitializer + +union_move9: ; No predecessors! + call void @llvm.memcpy.p0.p11.i64(ptr null, ptr addrspace(11) %1, i64 0, i1 false) + br label %common.ret +} +; CHECK-LABEL: }{{$}} + +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memcpy.p11.p0.i64(ptr addrspace(11) noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0 +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memcpy.p0.p11.i64(ptr noalias nocapture writeonly, ptr addrspace(11) noalias nocapture readonly, i64, i1 immarg) #0 +; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite) +declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) #0 + +attributes #0 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) } +attributes #1 = { allockind("alloc") } +attributes #2 = { allockind("alloc,uninitialized") } +attributes #3 = { allockind("alloc,zeroed") } diff --git a/test/llvmpasses/final-lower-gc.ll b/test/llvmpasses/final-lower-gc.ll index eb3b68662c2b4..3975edf0bc492 100644 --- a/test/llvmpasses/final-lower-gc.ll +++ b/test/llvmpasses/final-lower-gc.ll @@ -93,9 +93,8 @@ top: %pgcstack = call {}*** @julia.get_pgcstack() %ptls = call {}*** @julia.ptls_states() %ptls_i8 = bitcast {}*** %ptls to i8* -; CHECK: %0 = add i64 %size, 8 -; TYPED: %v = call noalias nonnull align {{[0-9]+}} dereferenceable(8) {} addrspace(10)* @ijl_gc_alloc_typed(i8* %ptls_i8, i64 %0, i64 12341234) -; OPAQUE: %v = call noalias nonnull align {{[0-9]+}} dereferenceable(8) ptr addrspace(10) @ijl_gc_alloc_typed(ptr %ptls_i8, i64 %0, i64 12341234) +; TYPED: %v = call noalias nonnull align {{[0-9]+}} dereferenceable(8) {} addrspace(10)* @ijl_gc_alloc_typed(i8* %ptls_i8, i64 %size, i64 12341234) +; OPAQUE: %v = call noalias nonnull align {{[0-9]+}} dereferenceable(8) ptr addrspace(10) @ijl_gc_alloc_typed(ptr %ptls_i8, i64 %size, i64 12341234) %v = call {} addrspace(10)* @julia.gc_alloc_bytes(i8* %ptls_i8, i64 %size, i64 12341234) %0 = bitcast {} addrspace(10)* %v to {} addrspace(10)* addrspace(10)* %1 = getelementptr {} addrspace(10)*, {} addrspace(10)* addrspace(10)* %0, i64 -1 diff --git a/test/loading.jl b/test/loading.jl index 53e05a4a76bbc..cda7139e2d875 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -1544,3 +1544,16 @@ end @test_throws SystemError("opening file $(repr(file))") include(file) end end + +@testset "extension path computation name collision" begin + old_load_path = copy(LOAD_PATH) + try + empty!(LOAD_PATH) + push!(LOAD_PATH, joinpath(@__DIR__, "project", "Extensions", "ExtNameCollision_A")) + push!(LOAD_PATH, joinpath(@__DIR__, "project", "Extensions", "ExtNameCollision_B")) + ext_B = Base.PkgId(Base.uuid5(Base.identify_package("ExtNameCollision_B").uuid, "REPLExt"), "REPLExt") + @test Base.locate_package(ext_B) == joinpath(@__DIR__, "project", "Extensions", "ExtNameCollision_B", "ext", "REPLExt.jl") + finally + copy!(LOAD_PATH, old_load_path) + end +end diff --git a/test/math.jl b/test/math.jl index c88905018fb3a..be134adb15d34 100644 --- a/test/math.jl +++ b/test/math.jl @@ -1557,13 +1557,21 @@ end @testset let T = T for f = Any[sin, cos, tan, log, log2, log10, log1p, exponent, sqrt, cbrt, fourthroot, asin, atan, acos, sinh, cosh, tanh, asinh, acosh, atanh, exp, exp2, exp10, expm1] - @testset let f = f - @test Base.infer_return_type(f, (T,)) != Union{} - @test Core.Compiler.is_foldable(Base.infer_effects(f, (T,))) + @testset let f = f, + rt = Base.infer_return_type(f, (T,)), + effects = Base.infer_effects(f, (T,)) + @test rt != Union{} + @test Core.Compiler.is_foldable(effects) end end - @test Core.Compiler.is_foldable(Base.infer_effects(^, (T,Int))) - @test Core.Compiler.is_foldable(Base.infer_effects(^, (T,T))) + @static if !(Sys.iswindows()&&Int==Int32) # COMBAK debug this + @testset let effects = Base.infer_effects(^, (T,Int)) + @test Core.Compiler.is_foldable(effects) + end + end # @static + @testset let effects = Base.infer_effects(^, (T,T)) + @test Core.Compiler.is_foldable(effects) + end end end end; diff --git a/test/misc.jl b/test/misc.jl index e870c7f491c13..d3c583faf8520 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -571,6 +571,24 @@ end # issue #44780 @test summarysize(BigInt(2)^1000) > summarysize(BigInt(2)) +# issue #53061 +mutable struct S53061 + x::Union{Float64, Tuple{Float64, Float64}} + y::Union{Float64, Tuple{Float64, Float64}} +end +let s = S53061[S53061(rand(), (rand(),rand())) for _ in 1:10^4] + @test allequal(summarysize(s) for i in 1:10) +end +struct Z53061 + x::S53061 + y::Int64 +end +let z = Z53061[Z53061(S53061(rand(), (rand(),rand())), 0) for _ in 1:10^4] + @test allequal(summarysize(z) for i in 1:10) + # broken on i868 linux. issue #54895 + @test abs(summarysize(z) - 640000)/640000 <= 0.01 broken = Sys.WORD_SIZE == 32 && Sys.islinux() +end + ## test conversion from UTF-8 to UTF-16 (for Windows APIs) # empty arrays diff --git a/test/namedtuple.jl b/test/namedtuple.jl index 48aa8ea4a2591..558ba94bda8d0 100644 --- a/test/namedtuple.jl +++ b/test/namedtuple.jl @@ -398,14 +398,14 @@ for f in (Base.merge, Base.structdiff) fallback_func(a::NamedTuple, b::NamedTuple) = @invoke f(a::NamedTuple, b::NamedTuple) @testset let eff = Base.infer_effects(fallback_func) @test Core.Compiler.is_foldable(eff) - @test eff.nonoverlayed + @test Core.Compiler.is_nonoverlayed(eff) end @test only(Base.return_types(fallback_func)) == NamedTuple # test if `max_methods = 4` setting works as expected general_func(a::NamedTuple, b::NamedTuple) = f(a, b) @testset let eff = Base.infer_effects(general_func) @test Core.Compiler.is_foldable(eff) - @test eff.nonoverlayed + @test Core.Compiler.is_nonoverlayed(eff) end @test only(Base.return_types(general_func)) == NamedTuple end diff --git a/test/precompile.jl b/test/precompile.jl index d8471120051db..bc6a791627616 100644 --- a/test/precompile.jl +++ b/test/precompile.jl @@ -806,7 +806,7 @@ precompile_test_harness("code caching") do dir mi = minternal.specializations::Core.MethodInstance @test mi.specTypes == Tuple{typeof(M.getelsize),Vector{Int32}} ci = mi.cache - @test ci.relocatability == 1 + @test ci.relocatability == 0 @test ci.inferred !== nothing # ...and that we can add "untracked" roots & non-relocatable CodeInstances to them too Base.invokelatest() do @@ -1045,7 +1045,6 @@ precompile_test_harness("code caching") do dir @test mi.specTypes.parameters[end] === Integer ? !hv : hv end - setglobal!(Main, :inval, invalidations) idxs = findall(==("verify_methods"), invalidations) idxsbits = filter(idxs) do i mi = invalidations[i-1] diff --git a/test/project/Extensions/ExtNameCollision_A/Project.toml b/test/project/Extensions/ExtNameCollision_A/Project.toml new file mode 100644 index 0000000000000..f4cc37786f508 --- /dev/null +++ b/test/project/Extensions/ExtNameCollision_A/Project.toml @@ -0,0 +1,9 @@ +name = "ExtNameCollision_A" +uuid = "9f48de98-8f56-4937-aa32-2a5530882eaa" +version = "0.1.0" + +[weakdeps] +REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[extensions] +REPLExt = "REPL" diff --git a/test/project/Extensions/ExtNameCollision_A/ext/REPLExt.jl b/test/project/Extensions/ExtNameCollision_A/ext/REPLExt.jl new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/project/Extensions/ExtNameCollision_A/src/ExtNameCollision_A.jl b/test/project/Extensions/ExtNameCollision_A/src/ExtNameCollision_A.jl new file mode 100644 index 0000000000000..2f47a862dd9c5 --- /dev/null +++ b/test/project/Extensions/ExtNameCollision_A/src/ExtNameCollision_A.jl @@ -0,0 +1,5 @@ +module ExtNameCollision_A + +greet() = print("Hello World!") + +end # module ExtNameCollision_A diff --git a/test/project/Extensions/ExtNameCollision_B/Project.toml b/test/project/Extensions/ExtNameCollision_B/Project.toml new file mode 100644 index 0000000000000..ac52d64a82a7c --- /dev/null +++ b/test/project/Extensions/ExtNameCollision_B/Project.toml @@ -0,0 +1,9 @@ +name = "ExtNameCollision_B" +uuid = "597d654f-44d8-4443-9b1e-1f2f4b45906f" +version = "0.1.0" + +[weakdeps] +REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb" + +[extensions] +REPLExt = "REPL" diff --git a/test/project/Extensions/ExtNameCollision_B/ext/REPLExt.jl b/test/project/Extensions/ExtNameCollision_B/ext/REPLExt.jl new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/test/project/Extensions/ExtNameCollision_B/src/ExtNameCollision_B.jl b/test/project/Extensions/ExtNameCollision_B/src/ExtNameCollision_B.jl new file mode 100644 index 0000000000000..e7665982a79b3 --- /dev/null +++ b/test/project/Extensions/ExtNameCollision_B/src/ExtNameCollision_B.jl @@ -0,0 +1,5 @@ +module ExtNameCollision_B + +greet() = print("Hello World!") + +end # module ExtNameCollision_B diff --git a/test/reinterpretarray.jl b/test/reinterpretarray.jl index 46ecbf6d06723..e6381329e4ec6 100644 --- a/test/reinterpretarray.jl +++ b/test/reinterpretarray.jl @@ -40,9 +40,8 @@ end @test_throws ArgumentError("cannot reinterpret `Vector{Int32}` as `Int32`, type `Vector{Int32}` is not a bits type") reinterpret(Int32, Av) @test_throws ArgumentError("cannot reinterpret a zero-dimensional `Int64` array to `Int32` which is of a different size") reinterpret(Int32, reshape([Int64(0)])) @test_throws ArgumentError("cannot reinterpret a zero-dimensional `Int32` array to `Int64` which is of a different size") reinterpret(Int64, reshape([Int32(0)])) -@test_throws ArgumentError("""cannot reinterpret an `$Int` array to `Tuple{$Int, $Int}` whose first dimension has size `5`. - The resulting array would have non-integral first dimension. - """) reinterpret(Tuple{Int,Int}, [1,2,3,4,5]) +@test_throws ArgumentError("cannot reinterpret an `$Int` array to `Tuple{$Int, $Int}` whose first dimension has size `5`."* + " The resulting array would have a non-integral first dimension.") reinterpret(Tuple{Int,Int}, [1,2,3,4,5]) @test_throws ArgumentError("`reinterpret(reshape, Complex{Int64}, a)` where `eltype(a)` is Int64 requires that `axes(a, 1)` (got Base.OneTo(4)) be equal to 1:2 (from the ratio of element sizes)") reinterpret(reshape, Complex{Int64}, A) @test_throws ArgumentError("`reinterpret(reshape, T, a)` requires that one of `sizeof(T)` (got 24) and `sizeof(eltype(a))` (got 16) be an integer multiple of the other") reinterpret(reshape, NTuple{3, Int64}, B) diff --git a/test/subarray.jl b/test/subarray.jl index 9fd59ef5a4156..d02da15dc3158 100644 --- a/test/subarray.jl +++ b/test/subarray.jl @@ -1075,3 +1075,34 @@ end @test !isassigned(v, 3, 3) # out-of-bounds end end + +@testset "aliasing checks with shared indices" begin + indices = [1,3] + a = rand(3) + av = @view a[indices] + b = rand(3) + bv = @view b[indices] + @test !Base.mightalias(av, bv) + @test Base.mightalias(a, av) + @test Base.mightalias(b, bv) + @test Base.mightalias(indices, av) + @test Base.mightalias(indices, bv) + @test Base.mightalias(view(indices, :), av) + @test Base.mightalias(view(indices, :), bv) +end + +@testset "aliasing checks with disjoint arrays" begin + A = rand(3,4,5) + @test Base.mightalias(view(A, :, :, 1), view(A, :, :, 1)) + @test !Base.mightalias(view(A, :, :, 1), view(A, :, :, 2)) + + B = reinterpret(UInt64, A) + @test Base.mightalias(view(B, :, :, 1), view(A, :, :, 1)) + @test !Base.mightalias(view(B, :, :, 1), view(A, :, :, 2)) + + C = reinterpret(UInt32, A) + @test Base.mightalias(view(C, :, :, 1), view(A, :, :, 1)) + @test Base.mightalias(view(C, :, :, 1), view(A, :, :, 2)) # This is overly conservative + @test Base.mightalias(@view(C[begin:2:end, :, 1]), view(A, :, :, 1)) + @test Base.mightalias(@view(C[begin:2:end, :, 1]), view(A, :, :, 2)) # This is overly conservative +end diff --git a/test/subtype.jl b/test/subtype.jl index c65521d44ac5a..c26f4fc9d30e2 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -2650,3 +2650,10 @@ let S = Tuple{Val, Val{T}} where {T}, R = Tuple{Val{Val{T}}, Val{T}} where {T}, @testintersect(Tuple{Val{A}, A} where {B, A<:Union{Val{B}, GenericMemory{B}}}, S{1}, R{1}) @testintersect(Tuple{Val{A}, A} where {B, A<:Union{Val{B}, GenericMemory{:not_atomic,Int,B}}}, S{1}, R{1}) end + +#issue 54516 +let S = Tuple{Val{<:T}, Union{Int,T}} where {T}, + T = Tuple{Union{Int,T}, Val{<:T}} where {T} + @testintersect(S, T, !Union{}) + @test !Base.has_free_typevars(typeintersect(S, T)) +end diff --git a/test/syntax.jl b/test/syntax.jl index 25d3e7282818a..2933ae1db1dfa 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -3637,3 +3637,56 @@ end @test array == [7] @test execs == 4 end + +# Issue #54701 - Macro hygiene of argument destructuring +macro makef54701() + quote + call(f) = f((1, 2)) + function $(esc(:f54701))() + call() do (a54701, b54701) + return a54701+b54701 + end + end + end +end +@makef54701 +@test f54701() == 3 +@test !@isdefined(a54701) +@test !@isdefined(b54701) + +# Issue #54607 - binding creation in foreign modules should not be permitted +module Foreign54607 + # Syntactic, not dynamic + try_to_create_binding1() = (Foreign54607.foo = 2) + @eval try_to_create_binding2() = ($(GlobalRef(Foreign54607, :foo)) = 2) + function global_create_binding() + global bar + bar = 3 + end + baz = 4 + begin; + @Base.Experimental.force_compile + compiled_assign = 5 + end + @eval $(GlobalRef(Foreign54607, :gr_assign)) = 6 +end +@test_throws ErrorException (Foreign54607.foo = 1) +@test_throws ErrorException Foreign54607.try_to_create_binding1() +@test_throws ErrorException Foreign54607.try_to_create_binding2() +@test_throws ErrorException begin + @Base.Experimental.force_compile + (Foreign54607.foo = 1) +end +@test_throws ErrorException @eval (GlobalRef(Foreign54607, :gr_assign2)) = 7 +Foreign54607.global_create_binding() +@test isdefined(Foreign54607, :bar) +@test isdefined(Foreign54607, :baz) +@test isdefined(Foreign54607, :compiled_assign) +@test isdefined(Foreign54607, :gr_assign) +Foreign54607.bar = 8 +@test Foreign54607.bar == 8 +begin + @Base.Experimental.force_compile + Foreign54607.bar = 9 +end +@test Foreign54607.bar == 9 diff --git a/test/threads.jl b/test/threads.jl index 307742a4c292b..ab35c327196ef 100644 --- a/test/threads.jl +++ b/test/threads.jl @@ -341,3 +341,15 @@ end @testset "Base.Threads docstrings" begin @test isempty(Docs.undocumented_names(Threads)) end + +@testset "jl_*affinity" begin + cpumasksize = @ccall uv_cpumask_size()::Cint + if !Sys.iswindows() && cpumasksize > 0 # otherwise affinities are not supported on the platform (UV_ENOTSUP) + mask = zeros(Cchar, cpumasksize); + jl_getaffinity = (tid, mask, cpumasksize) -> ccall(:jl_getaffinity, Int32, (Int16, Ptr{Cchar}, Int32), tid, mask, cpumasksize) + jl_setaffinity = (tid, mask, cpumasksize) -> ccall(:jl_setaffinity, Int32, (Int16, Ptr{Cchar}, Int32), tid, mask, cpumasksize) + @test jl_getaffinity(1, mask, cpumasksize) == 0 + fill!(mask, 1) + @test jl_setaffinity(1, mask, cpumasksize) == 0 + end +end