diff --git a/.gitattributes b/.gitattributes index c84e6206024f5..a97c543432108 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,2 @@ -# treat patches as files that should not be modified -*.patch -text +# treat all files as files that should not be modified +* -text diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e97afc54549a1..f8e777ff4f3cb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -264,6 +264,12 @@ If you need to restart your Julia session, just start at step 2 above. built and incorporate them automatically. You only need to rebuild Julia if you made code-changes that Revise cannot handle. +For convenience, there are also `test-revise-*` targets for every `test-*` +target that use Revise to load any modifications to Base into the current +process before running the corresponding test. This can be useful as a shortcut +on the command line (since tests aren't always designed to be run outside the +runtest harness). + ### Code Formatting Guidelines #### General Formatting Guidelines for Julia code contributions diff --git a/HISTORY.md b/HISTORY.md index 309cb031aefc8..881b0e6797241 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -46,6 +46,7 @@ New library functions Standard library changes ------------------------ +* `Pkg` won't clobber pre-compilation files as often when switching environments ([#32651]) * `Pkg` can now download and install binary artifacts through the `Pkg.Artifacts` submodule and supporting functions. ([#32918]) * When `wait` (or `@sync`, or `fetch`) is called on a failing `Task`, the exception is propagated as a diff --git a/LICENSE.md b/LICENSE.md index 3d315105d038e..0d5d98311766c 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -35,7 +35,7 @@ Julia includes code from the following projects, which have their own licenses: - [MUSL](https://git.musl-libc.org/cgit/musl/tree/COPYRIGHT) (for getopt implementation on Windows) [MIT] - [MINGW](https://sourceforge.net/p/mingw/mingw-org-wsl/ci/legacy/tree/mingwrt/mingwex/dirname.c) (for dirname implementation on Windows) [MIT] - [NetBSD](https://www.netbsd.org/about/redistribution.html) (for setjmp, longjmp, and strptime implementations on Windows) [BSD-3] -- [Python](https://docs.python.org/2/license.html) (for strtod implementation on Windows) [BSD-3, effectively] +- [Python](https://docs.python.org/2/license.html) (for strtod and joinpath implementation on Windows) [BSD-3, effectively] The following components included in Julia `Base` have their own separate licenses: diff --git a/Make.inc b/Make.inc index cac4fb1a8c08d..3a9afc68d7190 100644 --- a/Make.inc +++ b/Make.inc @@ -283,6 +283,7 @@ libdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(libdir)) build_private_libdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(build_bindir) $(build_private_libdir)) private_libdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(private_libdir)) datarootdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(datarootdir)) +libexecdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(libexecdir)) docdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(docdir)) sysconfdir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(sysconfdir)) includedir_rel := $(shell $(JULIAHOME)/contrib/relative_path.sh $(bindir) $(includedir)) @@ -928,6 +929,20 @@ else PCRE_CONFIG := $(build_depsbindir)/pcre2-config endif +ifeq ($(USE_SYSTEM_PATCHELF), 1) +PATCHELF := patchelf +else +PATCHELF := $(build_depsbindir)/patchelf +endif + +# On aarch64 and powerpc64le, we assume the page size is 64K. Our binutils linkers +# and such already assume this, but `patchelf` seems to be behind the times. We +# explicitly tell it to use this large page size so that when we rewrite rpaths and +# such, we don't accidentally create incorrectly-aligned sections in our ELF files. +ifneq (,$(filter $(ARCH),aarch64 powerpc64le)) +PATCHELF += --page-size=65536 +endif + # Use ILP64 BLAS interface when building openblas from source on 64-bit architectures ifeq ($(BINARY), 64) ifeq ($(USE_SYSTEM_BLAS), 1) @@ -1246,7 +1261,7 @@ ifeq ($(USECLANG),1) CXXLDFLAGS += -stdlib=libc++ else ifeq ($(USEGCC),1) -$(error BUILD_CUSTOM_LIBCXX is currently only supported with Clang. Try setting BUILD_CUSTOM_LIBCXX=0) +$(error BUILD_CUSTOM_LIBCXX is currently only supported with Clang. Try setting BUILD_CUSTOM_LIBCXX=0 or USECLANG=1) endif endif # Clang CUSTOM_LD_LIBRARY_PATH := LD_LIBRARY_PATH="$(build_libdir)" diff --git a/Makefile b/Makefile index dea4934127b6d..e1ec09ed4dc4f 100644 --- a/Makefile +++ b/Makefile @@ -176,7 +176,7 @@ else JL_PRIVATE_LIBS-$(USE_SYSTEM_ZLIB) += libz endif ifeq ($(USE_LLVM_SHLIB),1) -JL_PRIVATE_LIBS-$(USE_SYSTEM_LLVM) += libLLVM libLLVM-6 +JL_PRIVATE_LIBS-$(USE_SYSTEM_LLVM) += libLLVM libLLVM-8 endif ifeq ($(USE_SYSTEM_LIBM),0) @@ -269,7 +269,7 @@ endef ifeq (,$(findstring $(OS),FreeBSD WINNT)) julia-base: $(build_libdir)/libgfortran*.$(SHLIB_EXT)* $(build_libdir)/libgfortran*.$(SHLIB_EXT)*: | $(build_libdir) julia-deps - -$(CUSTOM_LD_LIBRARY_PATH) PATH="$(PATH):$(build_depsbindir)" $(JULIAHOME)/contrib/fixup-libgfortran.sh --verbose $(build_libdir) + -$(CUSTOM_LD_LIBRARY_PATH) PATH="$(PATH):$(build_depsbindir)" PATCHELF="$(PATCHELF)" $(JULIAHOME)/contrib/fixup-libgfortran.sh --verbose $(build_libdir) JL_PRIVATE_LIBS-0 += libgfortran libgcc_s libquadmath endif @@ -291,6 +291,9 @@ endif ifeq ($(OS),WINNT) -$(INSTALL_M) $(filter-out $(build_bindir)/libjulia-debug.dll,$(wildcard $(build_bindir)/*.dll)) $(DESTDIR)$(bindir)/ -$(INSTALL_M) $(build_libdir)/libjulia.dll.a $(DESTDIR)$(libdir)/ + + # We have a single exception; we want 7z.dll to live in libexec, not bin, so that 7z.exe can find it. + -mv $(DESTDIR)$(bindir)/7z.dll $(DESTDIR)$(libexecdir)/ ifeq ($(BUNDLE_DEBUG_LIBS),1) -$(INSTALL_M) $(build_bindir)/libjulia-debug.dll $(DESTDIR)$(bindir)/ -$(INSTALL_M) $(build_libdir)/libjulia-debug.dll.a $(DESTDIR)$(libdir)/ @@ -298,9 +301,6 @@ endif -$(INSTALL_M) $(build_bindir)/libopenlibm.dll.a $(DESTDIR)$(libdir)/ else - # Install `7z` into libexec/ - $(INSTALL_M) $(build_bindir)/7z$(EXE) $(DESTDIR)$(libexecdir)/ - # Copy over .dSYM directories directly for Darwin ifneq ($(DARWIN_FRAMEWORK),1) ifeq ($(OS),Darwin) @@ -330,6 +330,7 @@ ifeq ($(BUNDLE_DEBUG_LIBS),1) @$(DSYMUTIL) -o $(DESTDIR)$(prefix)/$(framework_resources)/sys-debug.dylib.dSYM $(build_private_libdir)/sys-debug.dylib endif endif + for suffix in $(JL_PRIVATE_LIBS-0) ; do \ for lib in $(build_libdir)/$${suffix}.*$(SHLIB_EXT)*; do \ if [ "$${lib##*.}" != "dSYM" ]; then \ @@ -342,6 +343,8 @@ endif $(INSTALL_M) $$lib $(DESTDIR)$(private_libdir) ; \ done endif + # Install `7z` into libexec/ + $(INSTALL_M) $(build_bindir)/7z$(EXE) $(DESTDIR)$(libexecdir)/ # Copy public headers cp -R -L $(build_includedir)/julia/* $(DESTDIR)$(includedir)/julia @@ -389,12 +392,12 @@ ifneq ($(DARWIN_FRAMEWORK),1) endif else ifneq (,$(findstring $(OS),Linux FreeBSD)) for j in $(JL_TARGETS) ; do \ - patchelf --set-rpath '$$ORIGIN/$(private_libdir_rel):$$ORIGIN/$(libdir_rel)' $(DESTDIR)$(bindir)/$$j; \ + $(PATCHELF) --set-rpath '$$ORIGIN/$(private_libdir_rel):$$ORIGIN/$(libdir_rel)' $(DESTDIR)$(bindir)/$$j; \ done endif # Overwrite JL_SYSTEM_IMAGE_PATH in julia library - if [ $(DARWIN_FRAMEWORK) == 0 ]; then \ + if [ $(DARWIN_FRAMEWORK) = 0 ]; then \ RELEASE_TARGET=$(DESTDIR)$(libdir)/libjulia.$(SHLIB_EXT); \ DEBUG_TARGET=$(DESTDIR)$(libdir)/libjulia-debug.$(SHLIB_EXT); \ else \ @@ -402,22 +405,22 @@ endif DEBUG_TARGET=$(DESTDIR)$(prefix)/$(framework_dylib)_debug; \ fi; \ $(call stringreplace,$${RELEASE_TARGET},sys.$(SHLIB_EXT)$$,$(private_libdir_rel)/sys.$(SHLIB_EXT)); \ - if [ $(BUNDLE_DEBUG_LIBS) == 1 ]; then \ + if [ $(BUNDLE_DEBUG_LIBS) = 1 ]; then \ $(call stringreplace,$${DEBUG_TARGET},sys-debug.$(SHLIB_EXT)$$,$(private_libdir_rel)/sys-debug.$(SHLIB_EXT)); \ fi; endif # On FreeBSD, remove the build's libdir from each library's RPATH ifeq ($(OS),FreeBSD) - $(JULIAHOME)/contrib/fixup-rpath.sh $(build_depsbindir)/patchelf $(DESTDIR)$(libdir) $(build_libdir) - $(JULIAHOME)/contrib/fixup-rpath.sh $(build_depsbindir)/patchelf $(DESTDIR)$(private_libdir) $(build_libdir) - $(JULIAHOME)/contrib/fixup-rpath.sh $(build_depsbindir)/patchelf $(DESTDIR)$(bindir) $(build_libdir) + $(JULIAHOME)/contrib/fixup-rpath.sh "$(PATCHELF)" $(DESTDIR)$(libdir) $(build_libdir) + $(JULIAHOME)/contrib/fixup-rpath.sh "$(PATCHELF)" $(DESTDIR)$(private_libdir) $(build_libdir) + $(JULIAHOME)/contrib/fixup-rpath.sh "$(PATCHELF)" $(DESTDIR)$(bindir) $(build_libdir) # Set libgfortran's RPATH to ORIGIN instead of GCCPATH. It's only libgfortran that # needs to be fixed here, as libgcc_s and libquadmath don't have RPATHs set. If we # don't set libgfortran's RPATH, it won't be able to find its friends on systems # that don't have the exact GCC port installed used for the build. for lib in $(DESTDIR)$(private_libdir)/libgfortran*$(SHLIB_EXT)*; do \ - $(build_depsbindir)/patchelf --set-rpath '$$ORIGIN' $$lib; \ + $(PATCHELF) --set-rpath '$$ORIGIN' $$lib; \ done endif @@ -557,7 +560,8 @@ distcleanall: cleanall julia-debug julia-release julia-stdlib julia-deps julia-deps-libs \ julia-ui-release julia-ui-debug julia-src-release julia-src-debug \ julia-symlink julia-base julia-sysimg julia-sysimg-ji julia-sysimg-release julia-sysimg-debug \ - test testall testall1 test clean distcleanall cleanall clean-* \ + test testall testall1 test test-* test-revise-* \ + clean distcleanall cleanall clean-* \ run-julia run-julia-debug run-julia-release run \ install binary-dist light-source-dist.tmp light-source-dist \ dist full-source-dist source-dist @@ -577,8 +581,13 @@ testall1: check-whitespace $(JULIA_BUILD_MODE) @env JULIA_CPU_THREADS=1 $(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/test all JULIA_BUILD_MODE=$(JULIA_BUILD_MODE) test-%: check-whitespace $(JULIA_BUILD_MODE) + @([ $$(( $$(date +%s) - $$(date +%s -r $(build_private_libdir)/sys.$(SHLIB_EXT)) )) -le 100 ] && \ + printf '\033[93m HINT The system image was recently rebuilt. Are you aware of the test-revise-* targets? See CONTRIBUTING.md. \033[0m\n') || true @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/test $* JULIA_BUILD_MODE=$(JULIA_BUILD_MODE) +test-revise-%: + @$(MAKE) $(QUIET_MAKE) -C $(BUILDROOT)/test revise-$* JULIA_BUILD_MODE=$(JULIA_BUILD_MODE) + # download target for some hardcoded windows dependencies .PHONY: win-extras wine_path win-extras: @@ -596,11 +605,11 @@ else LLVM_SIZE := $(build_depsbindir)/llvm-size$(EXE) endif build-stats: - @echo $(JULCOLOR)' ==> ./julia binary sizes'$(ENDCOLOR) + @printf $(JULCOLOR)' ==> ./julia binary sizes\n'$(ENDCOLOR) $(call spawn,$(LLVM_SIZE) -A $(call cygpath_w,$(build_private_libdir)/sys.$(SHLIB_EXT)) \ $(call cygpath_w,$(build_shlibdir)/libjulia.$(SHLIB_EXT)) \ $(call cygpath_w,$(build_bindir)/julia$(EXE))) - @echo $(JULCOLOR)' ==> ./julia launch speedtest'$(ENDCOLOR) + @printf $(JULCOLOR)' ==> ./julia launch speedtest\n'$(ENDCOLOR) @time $(call spawn,$(build_bindir)/julia$(EXE) -e '') @time $(call spawn,$(build_bindir)/julia$(EXE) -e '') @time $(call spawn,$(build_bindir)/julia$(EXE) -e '') diff --git a/NEWS.md b/NEWS.md index 1df8753ba0a7b..0d440d9c8e893 100644 --- a/NEWS.md +++ b/NEWS.md @@ -8,6 +8,9 @@ New language features * `import` now allows quoted symbols, e.g. `import Base.:+` ([#33158]). +* Function composition now supports multiple functions: `∘(f, g, h) = f ∘ g ∘ h` +and splatting `∘(fs...)` for composing an iterable collection of functions ([#33568]). + Language changes ---------------- @@ -30,9 +33,10 @@ New library functions * The `tempname` function now takes an optional `parent::AbstractString` argument to give it a directory in which to attempt to produce a temporary path name ([#33090]). * The `tempname` function now takes a `cleanup::Bool` keyword argument defaulting to `true`, which causes the process to try to ensure that any file or directory at the path returned by `tempname` is deleted upon process exit ([#33090]). * The `readdir` function now takes a `join::Bool` keyword argument defaulting to `false`, which when set causes `readdir` to join its directory argument with each listed name ([#33113]). +* `readdir` output is now guaranteed to be sorted. The `sort` keyword allows opting out of sorting to get names in OS-native order ([#33542]). * The new `only(x)` function returns the one-and-only element of a collection `x`, and throws an `ArgumentError` if `x` contains zero or multiple elements. ([#33129]) * `takewhile` and `dropwhile` have been added to the Iterators submodule ([#33437]). - +* There is a now an `evalpoly` (generated) function meant to take the role of the `@evalpoly` macro. The function is just as efficient as the macro while giving added flexibility, so it should be preferred over `@evalpoly`. `evalpoly` takes a list of coefficients as a tuple, so where one might write `@evalpoly(x, p1, p2, p3)` one would instead write `evalpoly(x, (p1, p2, p3))`. Standard library changes ------------------------ @@ -43,6 +47,10 @@ Standard library changes * Verbose `display` of `Char` (`text/plain` output) now shows the codepoint value in standard-conforming `"U+XXXX"` format ([#33291]). +* `Iterators.partition` now uses views (or smartly re-computed ranges) for partitions of all `AbstractArray`s ([#33533]). + +* Sets are now displayed less compactly in the REPL, as a column of elements, like vectors + and dictionaries ([#33300]). #### Libdl @@ -54,6 +62,8 @@ Standard library changes * `ldlt` and non-pivoted `lu` now throw a new `ZeroPivotException` type ([#33372]). +* `cond(A, p)` with `p=1` or `p=Inf` now computes the exact condition number instead of an estimate ([#33547]). + #### Random * `AbstractRNG`s now behave like scalars when used in broadcasting ([#33213]). @@ -63,6 +73,11 @@ Standard library changes * The performance of `rand(::Tuple)` is improved in some cases ([#32208]). As a consequence, the stream of generated values produced for a given seed has changed. +#### REPL + +* The attributes of the implicit `IOContext` used by the REPL to display objects can be + modified by the user (experimental feature) ([#29249]). + #### SparseArrays #### Dates diff --git a/README.md b/README.md index a9d3ab697ecbf..a2e5520d3a5a9 100644 --- a/README.md +++ b/README.md @@ -84,7 +84,7 @@ Julia. However, most users should use the most recent stable version of Julia. You can get this version by changing to the Julia directory and running: - git checkout v1.2.0 + git checkout v1.3.0 Now run `make` to build the `julia` executable. diff --git a/base/Base.jl b/base/Base.jl index c39b08b13539b..0595cd4674da6 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -30,7 +30,7 @@ setproperty!(x::Type, f::Symbol, v) = setfield!(x, f, v) getproperty(x::Tuple, f::Int) = getfield(x, f) setproperty!(x::Tuple, f::Int, v) = setfield!(x, f, v) # to get a decent error -getproperty(Core.@nospecialize(x), f::Symbol) = getfield(x, f) +getproperty(x, f::Symbol) = getfield(x, f) setproperty!(x, f::Symbol, v) = setfield!(x, f, convert(fieldtype(typeof(x), f), v)) include("coreio.jl") diff --git a/base/Enums.jl b/base/Enums.jl index 54473a716380c..b4c02327dd5e2 100644 --- a/base/Enums.jl +++ b/base/Enums.jl @@ -121,7 +121,7 @@ macro enum(T, syms...) end basetype = Int32 typename = T - if isa(T, Expr) && T.head == :(::) && length(T.args) == 2 && isa(T.args[1], Symbol) + if isa(T, Expr) && T.head === :(::) && length(T.args) == 2 && isa(T.args[1], Symbol) typename = T.args[1] basetype = Core.eval(__module__, T.args[2]) if !isa(basetype, DataType) || !(basetype <: Integer) || !isbitstype(basetype) @@ -137,7 +137,7 @@ macro enum(T, syms...) i = zero(basetype) hasexpr = false - if length(syms) == 1 && syms[1] isa Expr && syms[1].head == :block + if length(syms) == 1 && syms[1] isa Expr && syms[1].head === :block syms = syms[1].args end for s in syms @@ -147,7 +147,7 @@ macro enum(T, syms...) throw(ArgumentError("overflow in value \"$s\" of Enum $typename")) end elseif isa(s, Expr) && - (s.head == :(=) || s.head == :kw) && + (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) diff --git a/base/Makefile b/base/Makefile index 70be6fd6a7f75..cd8c952f8c46a 100644 --- a/base/Makefile +++ b/base/Makefile @@ -61,6 +61,7 @@ ifeq ($(OS),WINNT) @printf 'const DATAROOTDIR = "%s"\n' '$(subst /,\\,$(datarootdir_rel))' >> $@ @printf 'const DOCDIR = "%s"\n' '$(subst /,\\,$(docdir_rel))' >> $@ @printf 'const LIBDIR = "%s"\n' '$(subst /,\\,$(libdir_rel))' >> $@ + @printf 'const LIBEXECDIR = "%s"\n' '$(subst /,\\,$(libexecdir_rel))' >> $@ @printf 'const PRIVATE_LIBDIR = "%s"\n' '$(subst /,\\,$(private_libdir_rel))' >> $@ @printf 'const INCLUDEDIR = "%s"\n' '$(subst /,\\,$(includedir_rel))' >> $@ else @@ -68,6 +69,7 @@ else @echo "const DATAROOTDIR = \"$(datarootdir_rel)\"" >> $@ @echo "const DOCDIR = \"$(docdir_rel)\"" >> $@ @echo "const LIBDIR = \"$(libdir_rel)\"" >> $@ + @echo "const LIBEXECDIR = \"$(libexecdir_rel)\"" >> $@ @echo "const PRIVATE_LIBDIR = \"$(private_libdir_rel)\"" >> $@ @echo "const INCLUDEDIR = \"$(includedir_rel)\"" >> $@ endif diff --git a/base/abstractarraymath.jl b/base/abstractarraymath.jl index 40d83f61e2b73..d7d9550f82f5f 100644 --- a/base/abstractarraymath.jl +++ b/base/abstractarraymath.jl @@ -69,7 +69,7 @@ julia> dropdims(a; dims=3) """ dropdims(A; dims) = _dropdims(A, dims) function _dropdims(A::AbstractArray, dims::Dims) - for i in 1:length(dims) + for i in eachindex(dims) 1 <= dims[i] <= ndims(A) || throw(ArgumentError("dropped dims must be in range 1:ndims(A)")) length(axes(A, dims[i])) == 1 || throw(ArgumentError("dropped dims must all be size 1")) for j = 1:i-1 @@ -375,7 +375,7 @@ _reperr(s, n, N) = throw(ArgumentError("number of " * s * " repetitions " * end # fill the first inner block - if all(x -> x == 1, inner) + if all(isequal(1), inner) idxs = (axes(A)..., ntuple(n->OneTo(1), ndims(R)-ndims(A))...) # keep dimension consistent R[idxs...] = A else @@ -390,12 +390,12 @@ _reperr(s, n, N) = throw(ArgumentError("number of " * s * " repetitions " * end # fill the outer blocks along each dimension - if all(x -> x == 1, outer) + if all(isequal(1), outer) return R end src_indices = [1:n for n in inner_shape] dest_indices = copy(src_indices) - for i in 1:length(outer) + for i in eachindex(outer) B = view(R, src_indices...) for j in 2:outer[i] dest_indices[i] = dest_indices[i] .+ inner_shape[i] @@ -452,6 +452,7 @@ See also [`eachrow`](@ref), [`eachcol`](@ref), and [`selectdim`](@ref). length(dims) == 1 || throw(ArgumentError("only single dimensions are supported")) dim = first(dims) dim <= ndims(A) || throw(DimensionMismatch("A doesn't have $dim dimensions")) - idx1, idx2 = ntuple(d->(:), dim-1), ntuple(d->(:), ndims(A)-dim) - return (view(A, idx1..., i, idx2...) for i in axes(A, dim)) + inds_before = ntuple(d->(:), dim-1) + inds_after = ntuple(d->(:), ndims(A)-dim) + return (view(A, inds_before..., i, inds_after...) for i in axes(A, dim)) end diff --git a/base/abstractdict.jl b/base/abstractdict.jl index 37b0d6f4e418e..6bcae02d539dc 100644 --- a/base/abstractdict.jl +++ b/base/abstractdict.jl @@ -77,7 +77,8 @@ function keys end Return an iterator over all keys in a dictionary. `collect(keys(a))` returns an array of keys. -Since the keys are stored internally in a hash table, +When the keys are stored internally in a hash table, +as is the case for `Dict`, the order in which they are returned may vary. But `keys(a)` and `values(a)` both iterate `a` and return the elements in the same order. @@ -102,7 +103,8 @@ keys(a::AbstractDict) = KeySet(a) Return an iterator over all values in a collection. `collect(values(a))` returns an array of values. -Since the values are stored internally in a hash table, +When the values are stored internally in a hash table, +as is the case for `Dict`, the order in which they are returned may vary. But `keys(a)` and `values(a)` both iterate `a` and return the elements in the same order. diff --git a/base/abstractset.jl b/base/abstractset.jl index fc226e849cd10..f7399fdcd27df 100644 --- a/base/abstractset.jl +++ b/base/abstractset.jl @@ -35,7 +35,10 @@ julia> union([4, 2], 1:2) 1 julia> union(Set([1, 2]), 2:3) -Set([2, 3, 1]) +Set{Int64} with 3 elements: + 2 + 3 + 1 ``` """ function union end @@ -60,7 +63,12 @@ julia> a = Set([1, 3, 4, 5]); julia> union!(a, 1:2:8); julia> a -Set([7, 4, 3, 5, 1]) +Set{Int64} with 5 elements: + 7 + 4 + 3 + 5 + 1 ``` """ function union!(s::AbstractSet, sets...) @@ -105,7 +113,8 @@ julia> intersect([1, 4, 4, 5, 6], [4, 6, 6, 7, 8]) 6 julia> intersect(Set([1, 2]), BitSet([2, 3])) -Set([2]) +Set{Int64} with 1 element: + 2 ``` """ intersect(s::AbstractSet, itr, itrs...) = intersect!(intersect(s, itr), itrs...) @@ -160,7 +169,8 @@ julia> a = Set([1, 3, 4, 5]); julia> setdiff!(a, 1:2:6); julia> a -Set([4]) +Set{Int64} with 1 element: + 4 ``` """ function setdiff!(s::AbstractSet, itrs...) diff --git a/base/array.jl b/base/array.jl index 3449d7bc49a2d..34e0c052546b9 100644 --- a/base/array.jl +++ b/base/array.jl @@ -1076,13 +1076,16 @@ julia> A 2 julia> S = Set([1, 2]) -Set([2, 1]) +Set{Int64} with 2 elements: + 2 + 1 julia> pop!(S) 2 julia> S -Set([1]) +Set{Int64} with 1 element: + 1 julia> pop!(Dict(1=>2)) 1 => 2 @@ -1770,7 +1773,8 @@ findfirst(testf::Function, A::Union{AbstractArray, AbstractString}) = findnext(testf, A, first(keys(A))) function findfirst(p::Union{Fix2{typeof(isequal),T},Fix2{typeof(==),T}}, r::StepRange{T,S}) where {T,S} - first(r) <= p.x <= last(r) || return nothing + isempty(r) && return nothing + minimum(r) <= p.x <= maximum(r) || return nothing d = convert(S, p.x - first(r)) iszero(d % step(r)) || return nothing return d ÷ step(r) + 1 diff --git a/base/arraymath.jl b/base/arraymath.jl index 233490804446d..dfea81dd9d35f 100644 --- a/base/arraymath.jl +++ b/base/arraymath.jl @@ -48,10 +48,10 @@ function +(A::Array, Bs::Array...) end for f in (:/, :\, :*) - if f != :/ + if f !== :/ @eval ($f)(A::Number, B::AbstractArray) = broadcast_preserving_zero_d($f, A, B) end - if f != :\ + if f !== :\ @eval ($f)(A::AbstractArray, B::Number) = broadcast_preserving_zero_d($f, A, B) end end diff --git a/base/atomics.jl b/base/atomics.jl index 241d7a134d8d1..b5446be5e0d99 100644 --- a/base/atomics.jl +++ b/base/atomics.jl @@ -398,7 +398,7 @@ for typ in atomictypes ret $lt %rv """, $typ, Tuple{Ptr{$typ}, $typ}, unsafe_convert(Ptr{$typ}, x), v) else - rmwop == :xchg || continue + rmwop === :xchg || continue @eval $fn(x::Atomic{$typ}, v::$typ) = llvmcall($""" %iptr = inttoptr i$WORD_SIZE %0 to $ilt* diff --git a/base/bitset.jl b/base/bitset.jl index 17003837a2633..4afd55755aa3b 100644 --- a/base/bitset.jl +++ b/base/bitset.jl @@ -340,10 +340,13 @@ filter!(f, s::BitSet) = unsafe_filter!(f, s) @inline in(n::Int, s::BitSet) = _bits_getindex(s.bits, n, s.offset) @inline in(n::Integer, s::BitSet) = _is_convertible_Int(n) ? in(Int(n), s) : false -function iterate(s::BitSet, idx=0) - idx = _bits_findnext(s.bits, idx) - idx == -1 && return nothing - (idx + intoffset(s), idx+1) +function iterate(s::BitSet, (word, idx) = (CHK0, 0)) + while word == 0 + idx == length(s.bits) && return nothing + idx += 1 + word = @inbounds s.bits[idx] + end + trailing_zeros(word) + (idx - 1 + s.offset) << 6, (_blsr(word), idx) end @noinline _throw_bitset_notempty_error() = diff --git a/base/boot.jl b/base/boot.jl index fd610eda65e6a..2fdfc2e8cacb9 100644 --- a/base/boot.jl +++ b/base/boot.jl @@ -210,6 +210,7 @@ else const UInt = UInt32 end +function iterate end function Typeof end ccall(:jl_toplevel_eval_in, Any, (Any, Any), Core, quote @@ -431,11 +432,12 @@ Array{T}(A::AbstractArray{S,N}) where {T,N,S} = Array{T,N}(A) AbstractArray{T}(A::AbstractArray{S,N}) where {T,S,N} = AbstractArray{T,N}(A) # primitive Symbol constructors -function Symbol(s::String) +eval(Core, :(function Symbol(s::String) + $(Expr(:meta, :pure)) return ccall(:jl_symbol_n, Ref{Symbol}, (Ptr{UInt8}, Int), ccall(:jl_string_ptr, Ptr{UInt8}, (Any,), s), sizeof(s)) -end +end)) function Symbol(a::Array{UInt8,1}) return ccall(:jl_symbol_n, Ref{Symbol}, (Ptr{UInt8}, Int), ccall(:jl_array_ptr, Ptr{UInt8}, (Any,), a), diff --git a/base/broadcast.jl b/base/broadcast.jl index 16c0653be515d..a27cb7ba74180 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -919,20 +919,19 @@ end length(dest) < 256 && return invoke(copyto!, Tuple{AbstractArray, Broadcasted{Nothing}}, dest, bc) tmp = Vector{Bool}(undef, bitcache_size) destc = dest.chunks - ind = cind = 1 + cind = 1 bc′ = preprocess(dest, bc) - @simd for I in eachindex(bc′) - @inbounds tmp[ind] = bc′[I] - ind += 1 - if ind > bitcache_size - dumpbitcache(destc, cind, tmp) - cind += bitcache_chunks - ind = 1 + for P in Iterators.partition(eachindex(bc′), bitcache_size) + ind = 1 + @simd for I in P + @inbounds tmp[ind] = bc′[I] + ind += 1 + end + @simd for i in ind:bitcache_size + @inbounds tmp[i] = false end - end - if ind > 1 - @inbounds tmp[ind:bitcache_size] .= false dumpbitcache(destc, cind, tmp) + cind += bitcache_chunks end return dest end @@ -1145,12 +1144,12 @@ Base.@propagate_inbounds dotview(args...) = Base.maybeview(args...) dottable(x) = false # avoid dotting spliced objects (e.g. view calls inserted by @view) # don't add dots to dot operators dottable(x::Symbol) = (!isoperator(x) || first(string(x)) != '.' || x === :..) && x !== :(:) -dottable(x::Expr) = x.head != :$ +dottable(x::Expr) = x.head !== :$ undot(x) = x function undot(x::Expr) - if x.head == :.= + if x.head === :.= Expr(:(=), x.args...) - elseif x.head == :block # occurs in for x=..., y=... + elseif x.head === :block # occurs in for x=..., y=... Expr(:block, map(undot, x.args)...) else x @@ -1159,22 +1158,22 @@ end __dot__(x) = x function __dot__(x::Expr) dotargs = map(__dot__, x.args) - if x.head == :call && dottable(x.args[1]) + if x.head === :call && dottable(x.args[1]) Expr(:., dotargs[1], Expr(:tuple, dotargs[2:end]...)) - elseif x.head == :comparison + elseif x.head === :comparison Expr(:comparison, (iseven(i) && dottable(arg) && arg isa Symbol && isoperator(arg) ? Symbol('.', arg) : arg for (i, arg) in pairs(dotargs))...) - elseif x.head == :$ + elseif x.head === :$ x.args[1] - elseif x.head == :let # don't add dots to `let x=...` assignments + elseif x.head === :let # don't add dots to `let x=...` assignments Expr(:let, undot(dotargs[1]), dotargs[2]) - elseif x.head == :for # don't add dots to for x=... assignments + elseif x.head === :for # don't add dots to for x=... assignments Expr(:for, undot(dotargs[1]), dotargs[2]) - elseif (x.head == :(=) || x.head == :function || x.head == :macro) && + elseif (x.head === :(=) || x.head === :function || x.head === :macro) && Meta.isexpr(x.args[1], :call) # function or macro definition Expr(x.head, x.args[1], dotargs[2]) else - if x.head == :&& || x.head == :|| + if x.head === :&& || x.head === :|| error(""" Using `&&` and `||` is disallowed in `@.` expressions. Use `&` or `|` for elementwise logical operations. diff --git a/base/cartesian.jl b/base/cartesian.jl index 865b766664708..45276e918b17c 100644 --- a/base/cartesian.jl +++ b/base/cartesian.jl @@ -45,7 +45,7 @@ function _nloops(N::Int, itersym::Symbol, arraysym::Symbol, args::Expr...) end function _nloops(N::Int, itersym::Symbol, rangeexpr::Expr, args::Expr...) - if rangeexpr.head != :-> + if rangeexpr.head !== :-> throw(ArgumentError("second argument must be an anonymous function expression to compute the range")) end if !(1 <= length(args) <= 3) @@ -167,7 +167,7 @@ evaluate to `true`. can be convenient for bounds-checking. """ macro nall(N::Int, criterion::Expr) - if criterion.head != :-> + if criterion.head !== :-> throw(ArgumentError("second argument must be an anonymous function expression yielding the criterion")) end conds = Any[ Expr(:escape, inlineanonymous(criterion, i)) for i = 1:N ] @@ -183,7 +183,7 @@ evaluate to `true`. `@nany 3 d->(i_d > 1)` would generate the expression `(i_1 > 1 || i_2 > 1 || i_3 > 1)`. """ macro nany(N::Int, criterion::Expr) - if criterion.head != :-> + if criterion.head !== :-> error("Second argument must be an anonymous function expression yielding the criterion") end conds = Any[ Expr(:escape, inlineanonymous(criterion, i)) for i = 1:N ] @@ -233,7 +233,7 @@ end # Simplify expressions like :(d->3:size(A,d)-3) given an explicit value for d function inlineanonymous(ex::Expr, val) - if ex.head != :-> + if ex.head !== :-> throw(ArgumentError("not an anonymous function")) end if !isa(ex.args[1], Symbol) @@ -313,7 +313,7 @@ end function lreplace!(ex::Expr, r::LReplace) # Curly-brace notation, which acts like parentheses - if ex.head == :curly && length(ex.args) == 2 && isa(ex.args[1], Symbol) && endswith(string(ex.args[1]), "_") + if ex.head === :curly && length(ex.args) == 2 && isa(ex.args[1], Symbol) && endswith(string(ex.args[1]), "_") excurly = exprresolve(lreplace!(ex.args[2], r)) if isa(excurly, Number) return Symbol(ex.args[1],excurly) @@ -333,12 +333,12 @@ lreplace!(arg, r::LReplace) = arg poplinenum(arg) = arg function poplinenum(ex::Expr) - if ex.head == :block + if ex.head === :block if length(ex.args) == 1 return ex.args[1] elseif length(ex.args) == 2 && isa(ex.args[1], LineNumberNode) return ex.args[2] - elseif (length(ex.args) == 2 && isa(ex.args[1], Expr) && ex.args[1].head == :line) + elseif (length(ex.args) == 2 && isa(ex.args[1], Expr) && ex.args[1].head === :line) return ex.args[2] end end @@ -353,7 +353,7 @@ const exprresolve_cond_dict = Dict{Symbol,Function}(:(==) => ==, :(<) => <, :(>) => >, :(<=) => <=, :(>=) => >=) function exprresolve_arith(ex::Expr) - if ex.head == :call && haskey(exprresolve_arith_dict, ex.args[1]) && all([isa(ex.args[i], Number) for i = 2:length(ex.args)]) + if ex.head === :call && haskey(exprresolve_arith_dict, ex.args[1]) && all([isa(ex.args[i], Number) for i = 2:length(ex.args)]) return true, exprresolve_arith_dict[ex.args[1]](ex.args[2:end]...) end false, 0 @@ -362,7 +362,7 @@ exprresolve_arith(arg) = false, 0 exprresolve_conditional(b::Bool) = true, b function exprresolve_conditional(ex::Expr) - if ex.head == :call && ex.args[1] ∈ keys(exprresolve_cond_dict) && isa(ex.args[2], Number) && isa(ex.args[3], Number) + if ex.head === :call && ex.args[1] ∈ keys(exprresolve_cond_dict) && isa(ex.args[2], Number) && isa(ex.args[3], Number) return true, exprresolve_cond_dict[ex.args[1]](ex.args[2], ex.args[3]) end false, false @@ -378,12 +378,12 @@ function exprresolve(ex::Expr) can_eval, result = exprresolve_arith(ex) if can_eval return result - elseif ex.head == :call && (ex.args[1] == :+ || ex.args[1] == :-) && length(ex.args) == 3 && ex.args[3] == 0 + elseif ex.head === :call && (ex.args[1] === :+ || ex.args[1] === :-) && length(ex.args) == 3 && ex.args[3] == 0 # simplify x+0 and x-0 return ex.args[2] end # Resolve array references - if ex.head == :ref && isa(ex.args[1], Array) + if ex.head === :ref && isa(ex.args[1], Array) for i = 2:length(ex.args) if !isa(ex.args[i], Real) return ex @@ -392,7 +392,7 @@ function exprresolve(ex::Expr) return ex.args[1][ex.args[2:end]...] end # Resolve conditionals - if ex.head == :if + if ex.head === :if can_eval, tf = exprresolve_conditional(ex.args[1]) if can_eval ex = tf ? ex.args[2] : ex.args[3] diff --git a/base/channels.jl b/base/channels.jl index 606a3b2dec7d9..f3b53188a791f 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -31,7 +31,7 @@ mutable struct Channel{T} <: AbstractChannel{T} cond_wait::Threads.Condition # waiting for data to become maybe available cond_put::Threads.Condition # waiting for a writeable slot state::Symbol - excp::Union{Exception, Nothing} # exception to be thrown when state != :open + excp::Union{Exception, Nothing} # exception to be thrown when state !== :open data::Vector{T} sz_max::Int # maximum size of channel @@ -189,7 +189,7 @@ function close(c::Channel, excp::Exception=closed_exception()) end nothing end -isopen(c::Channel) = (c.state == :open) +isopen(c::Channel) = (c.state === :open) """ bind(chnl::Channel, task::Task) @@ -242,10 +242,8 @@ Stacktrace: ``` """ function bind(c::Channel, task::Task) - # TODO: implement "schedulewait" and deprecate taskdone_hook - #T = Task(() -> close_chnl_on_taskdone(task, c)) - #schedulewait(task, T) - register_taskdone_hook(task, tsk -> close_chnl_on_taskdone(tsk, c)) + T = Task(() -> close_chnl_on_taskdone(task, c)) + _wait2(task, T) return c end @@ -277,28 +275,19 @@ end function close_chnl_on_taskdone(t::Task, c::Channel) isopen(c) || return - cleanup = () -> try - isopen(c) || return - if istaskfailed(t) - excp = task_result(t) - if excp isa Exception - close(c, excp) - return - end + lock(c) + try + isopen(c) || return + if istaskfailed(t) + excp = task_result(t) + if excp isa Exception + close(c, excp) + return end - close(c) - return - finally - unlock(c) end - if trylock(c) - # can't use `lock`, since attempts to task-switch to wait for it - # will just silently fail and leave us with broken state - cleanup() - else - # so schedule this to happen once we are finished destroying our task - # (on a new Task) - @async (lock(c); cleanup()) + close(c) + finally + unlock(c) end nothing end @@ -459,7 +448,7 @@ function iterate(c::Channel, state=nothing) try return (take!(c), nothing) catch e - if isa(e, InvalidStateException) && e.state == :closed + if isa(e, InvalidStateException) && e.state === :closed return nothing else rethrow() diff --git a/base/client.jl b/base/client.jl index 8f5a89fbb8666..a6b07aadde5a7 100644 --- a/base/client.jl +++ b/base/client.jl @@ -49,17 +49,9 @@ function repl_cmd(cmd, out) if !haskey(ENV, "OLDPWD") error("cd: OLDPWD not set") end - cd(ENV["OLDPWD"]) - else - @static if !Sys.iswindows() - # TODO: this is a rather expensive way to copy a string, remove? - # If it's intended to simulate `cd`, it should instead be doing - # more nearly `cd $dir && printf %s \$PWD` (with appropriate quoting), - # since shell `cd` does more than just `echo` the result. - dir = read(`$shell -c "printf '%s' $(shell_escape_posixly(dir))"`, String) - end - cd(dir) + dir = ENV["OLDPWD"] end + cd(dir) else cd() end @@ -94,7 +86,7 @@ function scrub_repl_backtrace(bt) if bt !== nothing && !(bt isa Vector{Any}) # ignore our sentinel value types bt = stacktrace(bt) # remove REPL-related frames from interactive printing - eval_ind = findlast(frame -> !frame.from_c && frame.func == :eval, bt) + eval_ind = findlast(frame -> !frame.from_c && frame.func === :eval, bt) eval_ind === nothing || deleteat!(bt, eval_ind:length(bt)) end return bt diff --git a/base/cmd.jl b/base/cmd.jl index 3da3313d17776..4890af1c4c7cd 100644 --- a/base/cmd.jl +++ b/base/cmd.jl @@ -102,6 +102,8 @@ shell_escape(cmd::Cmd; special::AbstractString="") = shell_escape(cmd.exec..., special=special) shell_escape_posixly(cmd::Cmd) = shell_escape_posixly(cmd.exec...) +shell_escape_winsomely(cmd::Cmd) = + shell_escape_winsomely(cmd.exec...) function show(io::IO, cmd::Cmd) print_env = cmd.env !== nothing diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 0645f41879b51..421cdb15edfdd 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -427,7 +427,7 @@ end # refine its type to an array of element types. # Union of Tuples of the same length is converted to Tuple of Unions. # returns an array of types -function precise_container_type(@nospecialize(typ), vtypes::VarTable, sv::InferenceState) +function precise_container_type(@nospecialize(itft), @nospecialize(typ), vtypes::VarTable, sv::InferenceState) if isa(typ, PartialStruct) && typ.typ.name === Tuple.name return typ.fields end @@ -489,17 +489,24 @@ function precise_container_type(@nospecialize(typ), vtypes::VarTable, sv::Infere elseif tti0 <: Array return Any[Vararg{eltype(tti0)}] else - return abstract_iteration(typ, vtypes, sv) + return abstract_iteration(itft, typ, vtypes, sv) end end # simulate iteration protocol on container type up to fixpoint -function abstract_iteration(@nospecialize(itertype), vtypes::VarTable, sv::InferenceState) +function abstract_iteration(@nospecialize(itft), @nospecialize(itertype), vtypes::VarTable, sv::InferenceState) if !isdefined(Main, :Base) || !isdefined(Main.Base, :iterate) || !isconst(Main.Base, :iterate) return Any[Vararg{Any}] end - iteratef = getfield(Main.Base, :iterate) - stateordonet = abstract_call(iteratef, nothing, Any[Const(iteratef), itertype], vtypes, sv) + if itft === nothing + iteratef = getfield(Main.Base, :iterate) + itft = Const(iteratef) + elseif isa(itft, Const) + iteratef = itft.val + else + return Any[Vararg{Any}] + end + stateordonet = abstract_call(iteratef, nothing, Any[itft, itertype], vtypes, sv) # Return Bottom if this is not an iterator. # WARNING: Changes to the iteration protocol must be reflected here, # this is not just an optimization. @@ -543,7 +550,7 @@ function abstract_iteration(@nospecialize(itertype), vtypes::VarTable, sv::Infer end # do apply(af, fargs...), where af is a function value -function abstract_apply(@nospecialize(aft), aargtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState, +function abstract_apply(@nospecialize(itft), @nospecialize(aft), aargtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState, max_methods = sv.params.MAX_METHODS) aftw = widenconst(aft) if !isa(aft, Const) && (!isType(aftw) || has_free_typevars(aftw)) @@ -561,7 +568,7 @@ function abstract_apply(@nospecialize(aft), aargtypes::Vector{Any}, vtypes::VarT for i = 1:nargs ctypes´ = [] for ti in (splitunions ? uniontypes(aargtypes[i]) : Any[aargtypes[i]]) - cti = precise_container_type(ti, vtypes, sv) + cti = precise_container_type(itft, ti, vtypes, sv) if _any(t -> t === Bottom, cti) continue end @@ -632,9 +639,33 @@ function pure_eval_call(@nospecialize(f), argtypes::Vector{Any}, @nospecialize(a end end +function argtype_by_index(argtypes::Vector{Any}, i::Int) + n = length(argtypes) + if isvarargtype(argtypes[n]) + return i >= n ? unwrapva(argtypes[n]) : argtypes[i] + else + return i > n ? Bottom : argtypes[i] + end +end + +function argtype_tail(argtypes::Vector{Any}, i::Int) + n = length(argtypes) + if isvarargtype(argtypes[n]) && i > n + i = n + end + return argtypes[i:n] +end + function abstract_call(@nospecialize(f), fargs::Union{Nothing,Vector{Any}}, argtypes::Vector{Any}, vtypes::VarTable, sv::InferenceState, max_methods = sv.params.MAX_METHODS) if f === _apply - return abstract_apply(argtypes[2], argtypes[3:end], vtypes, sv, max_methods) + ft = argtype_by_index(argtypes, 2) + ft === Bottom && return Bottom + return abstract_apply(nothing, ft, argtype_tail(argtypes, 3), vtypes, sv, max_methods) + elseif f === _apply_iterate + itft = argtype_by_index(argtypes, 2) + ft = argtype_by_index(argtypes, 3) + (itft === Bottom || ft === Bottom) && return Bottom + return abstract_apply(itft, ft, argtype_tail(argtypes, 4), vtypes, sv, max_methods) end la = length(argtypes) @@ -662,7 +693,7 @@ function abstract_call(@nospecialize(f), fargs::Union{Nothing,Vector{Any}}, argt end rt = builtin_tfunction(f, argtypes[2:end], sv) if f === getfield && isa(fargs, Vector{Any}) && length(argtypes) == 3 && isa(argtypes[3], Const) && isa(argtypes[3].val, Int) && argtypes[2] ⊑ Tuple - cti = precise_container_type(argtypes[2], vtypes, sv) + cti = precise_container_type(nothing, argtypes[2], vtypes, sv) idx = argtypes[3].val if 1 <= idx <= length(cti) rt = unwrapva(cti[idx]) @@ -788,6 +819,8 @@ function abstract_call(@nospecialize(f), fargs::Union{Nothing,Vector{Any}}, argt return ret end return Any + elseif f === Tuple && la == 2 && !isconcretetype(widenconst(argtypes[2])) + return Tuple elseif is_return_type(f) rt_rt = return_type_tfunc(argtypes, vtypes, sv) if rt_rt !== nothing @@ -833,6 +866,8 @@ function abstract_call(@nospecialize(f), fargs::Union{Nothing,Vector{Any}}, argt end elseif length(argtypes) == 2 && istopfunction(f, :typename) return typename_static(argtypes[2]) + elseif max_methods > 1 && istopfunction(f, :copyto!) + max_methods = 1 end atype = argtypes_to_type(argtypes) diff --git a/base/compiler/compiler.jl b/base/compiler/compiler.jl index c2d97e40cded4..b44a60f23d626 100644 --- a/base/compiler/compiler.jl +++ b/base/compiler/compiler.jl @@ -5,7 +5,7 @@ getfield(getfield(Main, :Core), :eval)(getfield(Main, :Core), :(baremodule Compi using Core.Intrinsics, Core.IR import Core: print, println, show, write, unsafe_write, stdout, stderr, - _apply, svec, apply_type, Builtin, IntrinsicFunction, MethodInstance, CodeInstance + _apply, _apply_iterate, svec, apply_type, Builtin, IntrinsicFunction, MethodInstance, CodeInstance const getproperty = getfield const setproperty! = setfield! diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index a208156274496..38d8a6ea4b377 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -157,7 +157,7 @@ function stmt_affects_purity(@nospecialize(stmt), ir) return !(t ⊑ Bool) end if isa(stmt, Expr) - return stmt.head != :loopinfo && stmt.head != :enter + return stmt.head !== :loopinfo && stmt.head !== :enter end return true end @@ -167,7 +167,7 @@ function optimize(opt::OptimizationState, @nospecialize(result)) def = opt.linfo.def nargs = Int(opt.nargs) - 1 @timeit "optimizer" ir = run_passes(opt.src, nargs, opt) - force_noinline = _any(@nospecialize(x) -> isexpr(x, :meta) && x.args[1] == :noinline, ir.meta) + force_noinline = _any(@nospecialize(x) -> isexpr(x, :meta) && x.args[1] === :noinline, ir.meta) # compute inlining and other related optimizations if (isa(result, Const) || isconstType(result)) @@ -262,6 +262,7 @@ function is_pure_intrinsic_infer(f::IntrinsicFunction) f === Intrinsics.llvmcall || # this one is never effect-free f === Intrinsics.arraylen || # this one is volatile f === Intrinsics.sqrt_llvm || # this one may differ at runtime (by a few ulps) + f === Intrinsics.sqrt_llvm_fast || # this one may differ at runtime (by a few ulps) f === Intrinsics.cglobal) # cglobal lookup answer changes at runtime end diff --git a/base/compiler/ssair/driver.jl b/base/compiler/ssair/driver.jl index 8f00e39cab03b..cb05e7e414311 100644 --- a/base/compiler/ssair/driver.jl +++ b/base/compiler/ssair/driver.jl @@ -34,7 +34,7 @@ end function normalize(@nospecialize(stmt), meta::Vector{Any}) if isa(stmt, Expr) - if stmt.head == :meta + if stmt.head === :meta args = stmt.args if length(args) > 0 push!(meta, stmt) diff --git a/base/compiler/ssair/inlining.jl b/base/compiler/ssair/inlining.jl index 3e07c7e4dbfeb..bd96133892872 100644 --- a/base/compiler/ssair/inlining.jl +++ b/base/compiler/ssair/inlining.jl @@ -383,7 +383,7 @@ function ir_inline_item!(compact::IncrementalCompact, idx::Int, argexprs::Vector end elseif isa(stmt′, GotoNode) stmt′ = GotoNode(stmt′.label + bb_offset) - elseif isa(stmt′, Expr) && stmt′.head == :enter + elseif isa(stmt′, Expr) && stmt′.head === :enter stmt′ = Expr(:enter, stmt′.args[1] + bb_offset) elseif isa(stmt′, GotoIfNot) stmt′ = GotoIfNot(stmt′.cond, stmt′.dest + bb_offset) @@ -560,7 +560,7 @@ function batch_inline!(todo::Vector{Any}, ir::IRCode, linetable::Vector{LineInfo end elseif isa(stmt, GotoNode) compact[idx] = GotoNode(state.bb_rename[stmt.label]) - elseif isa(stmt, Expr) && stmt.head == :enter + elseif isa(stmt, Expr) && stmt.head === :enter compact[idx] = Expr(:enter, state.bb_rename[stmt.args[1]]) elseif isa(stmt, GotoIfNot) compact[idx] = GotoIfNot(stmt.cond, state.bb_rename[stmt.dest]) @@ -592,11 +592,11 @@ function spec_lambda(@nospecialize(atype), sv::OptimizationState, @nospecialize( end # This assumes the caller has verified that all arguments to the _apply call are Tuples. -function rewrite_apply_exprargs!(ir::IRCode, idx::Int, argexprs::Vector{Any}, atypes::Vector{Any}) - new_argexprs = Any[argexprs[2]] - new_atypes = Any[atypes[2]] +function rewrite_apply_exprargs!(ir::IRCode, idx::Int, argexprs::Vector{Any}, atypes::Vector{Any}, arg_start::Int) + new_argexprs = Any[argexprs[arg_start]] + new_atypes = Any[atypes[arg_start]] # loop over original arguments and flatten any known iterators - for i in 3:length(argexprs) + for i in (arg_start+1):length(argexprs) def = argexprs[i] def_type = atypes[i] if def_type isa PartialStruct @@ -882,11 +882,15 @@ end function inline_apply!(ir::IRCode, idx::Int, sig::Signature, params::Params) stmt = ir.stmts[idx] - while sig.f === Core._apply + while sig.f === Core._apply || sig.f === Core._apply_iterate + arg_start = sig.f === Core._apply ? 2 : 3 atypes = sig.atypes + if arg_start > length(atypes) + return nothing + end # Try to figure out the signature of the function being called # and if rewrite_apply_exprargs can deal with this form - for i = 3:length(atypes) + for i = (arg_start + 1):length(atypes) # TODO: We could basically run the iteration protocol here if !is_valid_type_for_apply_rewrite(atypes[i], params) return nothing @@ -894,13 +898,13 @@ function inline_apply!(ir::IRCode, idx::Int, sig::Signature, params::Params) end # Independent of whether we can inline, the above analysis allows us to rewrite # this apply call to a regular call - ft = atypes[2] - if length(atypes) == 3 && ft isa Const && ft.val === Core.tuple && atypes[3] ⊑ Tuple + ft = atypes[arg_start] + if length(atypes) == arg_start+1 && ft isa Const && ft.val === Core.tuple && atypes[arg_start+1] ⊑ Tuple # rewrite `((t::Tuple)...,)` to `t` - ir.stmts[idx] = stmt.args[3] + ir.stmts[idx] = stmt.args[arg_start+1] return nothing end - stmt.args, atypes = rewrite_apply_exprargs!(ir, idx, stmt.args, atypes) + stmt.args, atypes = rewrite_apply_exprargs!(ir, idx, stmt.args, atypes, arg_start) has_free_typevars(ft) && return nothing f = singleton_type(ft) sig = Signature(f, ft, atypes) diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index 665387c1143b6..45184203a7fdb 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -80,7 +80,7 @@ function basic_blocks_starts(stmts::Vector{Any}) if stmt.head === :leave # :leave terminates a BB push!(jump_dests, idx+1) - elseif stmt.head == :enter + elseif stmt.head === :enter # :enter starts/ends a BB push!(jump_dests, idx) push!(jump_dests, idx+1) @@ -151,7 +151,7 @@ function compute_basic_blocks(stmts::Vector{Any}) push!(b.succs, block′) end elseif isa(terminator, Expr) - if terminator.head == :enter + if terminator.head === :enter # :enter gets a virtual edge to the exception handler and # the exception handler gets a virtual edge from outside # the function. @@ -162,7 +162,7 @@ function compute_basic_blocks(stmts::Vector{Any}) push!(blocks[block′].preds, num) push!(blocks[block′].preds, 0) push!(b.succs, block′) - elseif terminator.head == :gotoifnot + elseif terminator.head === :gotoifnot block′ = block_for_inst(basic_block_index, terminator.args[2]::Int) if block′ == num + 1 # This GotoIfNot acts like a noop - treat it as such. diff --git a/base/compiler/ssair/legacy.jl b/base/compiler/ssair/legacy.jl index 038bd75daa011..66e830046eaf3 100644 --- a/base/compiler/ssair/legacy.jl +++ b/base/compiler/ssair/legacy.jl @@ -35,7 +35,7 @@ function inflate_ir(ci::CodeInfo, sptypes::Vector{Any}, argtypes::Vector{Any}) code[i] = GotoIfNot(stmt.cond, block_for_inst(cfg, stmt.dest)) elseif isa(stmt, PhiNode) code[i] = PhiNode(Any[block_for_inst(cfg, edge) for edge in stmt.edges], stmt.values) - elseif isa(stmt, Expr) && stmt.head == :enter + elseif isa(stmt, Expr) && stmt.head === :enter stmt.args[1] = block_for_inst(cfg, stmt.args[1]) code[i] = stmt else @@ -87,7 +87,7 @@ function replace_code_newstyle!(ci::CodeInfo, ir::IRCode, nargs::Int) else ci.code[i] = Expr(:unreachable) end - elseif isa(stmt, Expr) && stmt.head == :enter + elseif isa(stmt, Expr) && stmt.head === :enter stmt.args[1] = first(ir.cfg.blocks[stmt.args[1]].stmts) ci.code[i] = stmt else diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index ae53dd87d41f3..f30860c0324ea 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -571,7 +571,7 @@ function getfield_elim_pass!(ir::IRCode, domtree::DomTree) (isa(c1, Const) && isa(c2, Const)) && continue lift_comparison!(compact, idx, c1, c2, stmt, lifting_cache) continue - elseif isexpr(stmt, :call) && stmt.args[1] == :unchecked_getfield + elseif isexpr(stmt, :call) && stmt.args[1] === :unchecked_getfield is_getfield = true is_unchecked = true elseif isexpr(stmt, :foreigncall) diff --git a/base/compiler/ssair/slot2ssa.jl b/base/compiler/ssair/slot2ssa.jl index a2ee23efaecf9..a15c03758fc8d 100644 --- a/base/compiler/ssair/slot2ssa.jl +++ b/base/compiler/ssair/slot2ssa.jl @@ -475,7 +475,7 @@ function domsort_ssa!(ir::IRCode, domtree::DomTree) end result_stmts[inst_range[end]] = GotoIfNot(terminator.cond, bb_rename[terminator.dest]) elseif !isa(terminator, ReturnNode) - if isa(terminator, Expr) && terminator.head == :enter + if isa(terminator, Expr) && terminator.head === :enter terminator.args[1] = bb_rename[terminator.args[1]] end if bb_rename[bb + 1] != new_bb + 1 diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index a9543bcac4eba..cdbdb5a70c53f 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -164,6 +164,7 @@ add_tfunc(floor_llvm, 1, 1, math_tfunc, 10) add_tfunc(trunc_llvm, 1, 1, math_tfunc, 10) add_tfunc(rint_llvm, 1, 1, math_tfunc, 10) add_tfunc(sqrt_llvm, 1, 1, math_tfunc, 20) +add_tfunc(sqrt_llvm_fast, 1, 1, math_tfunc, 20) ## same-type comparisons ## cmp_tfunc(@nospecialize(x), @nospecialize(y)) = Bool add_tfunc(eq_int, 2, 2, cmp_tfunc, 1) @@ -997,7 +998,7 @@ function apply_type_nothrow(argtypes::Array{Any, 1}, @nospecialize(rt)) for i = 2:length(argtypes) isa(u, UnionAll) || return false ai = widenconditional(argtypes[i]) - if ai === TypeVar + if ai ⊑ TypeVar # We don't know anything about the bounds of this typevar, but as # long as the UnionAll is not constrained, that's ok. if !(u.var.lb === Union{} && u.var.ub === Any) diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl index 931d56bffd66d..7abd0c13b869d 100644 --- a/base/compiler/typelattice.jl +++ b/base/compiler/typelattice.jl @@ -168,6 +168,8 @@ function ⊑(@nospecialize(a), @nospecialize(b)) return a.instance === b.val end return false + elseif isa(a, PartialTypeVar) && b === TypeVar + return true elseif !(isa(a, Type) || isa(a, TypeVar)) || !(isa(b, Type) || isa(b, TypeVar)) return a === b diff --git a/base/compiler/utilities.jl b/base/compiler/utilities.jl index b53030f71614c..dcb91ac1f952f 100644 --- a/base/compiler/utilities.jl +++ b/base/compiler/utilities.jl @@ -73,7 +73,9 @@ function quoted(@nospecialize(x)) end function is_inlineable_constant(@nospecialize(x)) - x isa Type && return true + if x isa Type || x isa Symbol + return true + end return isbits(x) && Core.sizeof(x) <= MAX_INLINE_CONST_SIZE end diff --git a/base/compiler/validation.jl b/base/compiler/validation.jl index 1dafa43644ff3..e02327cbb663b 100644 --- a/base/compiler/validation.jl +++ b/base/compiler/validation.jl @@ -139,9 +139,9 @@ function validate_code!(errors::Vector{>:InvalidCodeError}, c::CodeInfo, is_top_ push!(errors, InvalidCodeError(INVALID_RETURN, x.args[1])) end validate_val!(x.args[1]) - elseif head === :call || head === :invoke || head == :gc_preserve_end || head === :meta || + elseif head === :call || head === :invoke || head === :gc_preserve_end || head === :meta || head === :inbounds || head === :foreigncall || head === :cfunction || - head === :const || head === :enter || head === :leave || head == :pop_exception || + head === :const || head === :enter || head === :leave || head === :pop_exception || head === :method || head === :global || head === :static_parameter || head === :new || head === :splatnew || head === :thunk || head === :loopinfo || head === :throw_undef_if_not || head === :unreachable diff --git a/base/deprecated.jl b/base/deprecated.jl index 21131cf857632..fd8e42c46427b 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -42,16 +42,16 @@ macro deprecate(old, new, ex=true) depwarn($"`$old` is deprecated, use `$new` instead.", Core.Typeof($(esc(old))).name.mt.name) $(esc(new))(args...) end)) - elseif isa(old, Expr) && (old.head == :call || old.head == :where) + elseif isa(old, Expr) && (old.head === :call || old.head === :where) remove_linenums!(new) oldcall = sprint(show_unquoted, old) newcall = sprint(show_unquoted, new) # if old.head is a :where, step down one level to the :call to avoid code duplication below - callexpr = old.head == :call ? old : old.args[1] - if callexpr.head == :call + callexpr = old.head === :call ? old : old.args[1] + if callexpr.head === :call if isa(callexpr.args[1], Symbol) oldsym = callexpr.args[1]::Symbol - elseif isa(callexpr.args[1], Expr) && callexpr.args[1].head == :curly + elseif isa(callexpr.args[1], Expr) && callexpr.args[1].head === :curly oldsym = callexpr.args[1].args[1]::Symbol else error("invalid usage of @deprecate") diff --git a/base/dict.jl b/base/dict.jl index 4cff4ee095fe9..8c1d762527bb8 100644 --- a/base/dict.jl +++ b/base/dict.jl @@ -631,7 +631,7 @@ end """ delete!(collection, key) -Delete the mapping for the given key in a collection, and return the collection. +Delete the mapping for the given key in a collection, if any, and return the collection. # Examples ```jldoctest @@ -641,6 +641,10 @@ Dict{String,Int64} with 2 entries: "a" => 1 julia> delete!(d, "b") +Dict{String,Int64} with 1 entry: + "a" => 1 + +julia> delete!(d, "b") # d is left unchanged Dict{String,Int64} with 1 entry: "a" => 1 ``` diff --git a/base/div.jl b/base/div.jl index acf44ea0bc07e..5d91ba5ec19bb 100644 --- a/base/div.jl +++ b/base/div.jl @@ -1,12 +1,14 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + # Div is truncating by default """ div(x, y, r::RoundingMode=RoundToZero) -Compute the remainder of `x` after integer division by `y`, with the quotient rounded -according to the rounding mode `r`. In other words, the quantity +The quotient from Euclidean division. Computes x/y, rounded to an integer according +to the rounding mode `r`. In other words, the quantity - y*round(x/y,r) + round(x/y,r) without any intermediate rounding. @@ -52,7 +54,7 @@ without any intermediate rounding. - if `r == RoundDown`, then the result is in the interval ``[0, y)`` if `y` is positive, or ``(y, 0]`` otherwise. The result may not be exact if `x` and `y` have different signs, and - `abs(x) < abs(y)`. See also[`RoundDown`](@ref). + `abs(x) < abs(y)`. See also [`RoundDown`](@ref). - if `r == RoundUp`, then the result is in the interval `(-y,0]` if `y` is positive, or `[0,-y)` otherwise. The result may not be exact if `x` and `y` have the same sign, and @@ -231,7 +233,15 @@ fld(x::T, y::T) where {T<:Real} = throw(MethodError(div, (x, y, RoundDown))) cld(x::T, y::T) where {T<:Real} = throw(MethodError(div, (x, y, RoundUp))) # Promotion -div(x::Real, y::Real, r::RoundingMode) = div(promote(x, y)..., r) +function div(x::Real, y::Real, r::RoundingMode) + typeof(x) === typeof(y) && throw(MethodError(div, (x, y, r))) + if r == RoundToZero + # For compat. Remove in 2.0. + div(promote(x, y)...) + else + div(promote(x, y)..., r) + end +end # Integers # fld(x,y) == div(x,y) - ((x>=0) != (y>=0) && rem(x,y) != 0 ? 1 : 0) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 62de1099212ce..125cf50f98539 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -945,7 +945,8 @@ kw"mutable struct" Special function available to inner constructors which created a new object of the type. -See the manual section on [Inner Constructor Methods](@ref) for more information. +See the manual section on [Inner Constructor Methods](@ref man-inner-constructor-methods) +for more information. """ kw"new" diff --git a/base/error.jl b/base/error.jl index c0a37ed66f899..604914318c6b1 100644 --- a/base/error.jl +++ b/base/error.jl @@ -62,25 +62,39 @@ rethrow(e) = ccall(:jl_rethrow_other, Bottom, (Any,), e) struct InterpreterIP code::Union{CodeInfo,Core.MethodInstance,Nothing} stmt::Csize_t + mod::Union{Module,Nothing} end -# convert dual arrays (ips, interpreter_frames) to a single array of locations +# convert dual arrays (raw bt buffer, array of GC managed values) to a single +# array of locations function _reformat_bt(bt, bt2) ret = Vector{Union{InterpreterIP,Ptr{Cvoid}}}() i, j = 1, 1 while i <= length(bt) ip = bt[i]::Ptr{Cvoid} - if ip == Ptr{Cvoid}(-1%UInt) - # The next one is really a CodeInfo - push!(ret, InterpreterIP( - bt2[j], - bt[i+2])) - j += 1 - i += 3 - else - push!(ret, Ptr{Cvoid}(ip)) + if UInt(ip) != (-1 % UInt) # See also jl_bt_is_native + # native frame + push!(ret, ip) i += 1 + continue + end + # Extended backtrace entry + entry_metadata = reinterpret(UInt, bt[i+1]) + njlvalues = entry_metadata & 0x7 + nuintvals = (entry_metadata >> 3) & 0x7 + tag = (entry_metadata >> 6) & 0xf + header = entry_metadata >> 10 + if tag == 1 # JL_BT_INTERP_FRAME_TAG + code = bt2[j] + mod = njlvalues == 2 ? bt2[j+1] : nothing + push!(ret, InterpreterIP(code, header, mod)) + else + # Tags we don't know about are an error + throw(ArgumentError("Unexpected extended backtrace entry tag $tag at bt[$i]")) end + # See jl_bt_entry_size + j += njlvalues + i += Int(2 + njlvalues + nuintvals) end ret end @@ -95,8 +109,8 @@ function backtrace() # skip frame for backtrace(). Note that for this to work properly, # backtrace() itself must not be interpreted nor inlined. skip = 1 - bt1, bt2 = ccall(:jl_backtrace_from_here, Any, (Cint,Cint), false, skip) - _reformat_bt(bt1, bt2) + bt1, bt2 = ccall(:jl_backtrace_from_here, Ref{SimpleVector}, (Cint, Cint), false, skip) + return _reformat_bt(bt1::Vector{Ptr{Cvoid}}, bt2::Vector{Any}) end """ @@ -105,10 +119,8 @@ end Get the backtrace of the current exception, for use within `catch` blocks. """ function catch_backtrace() - bt = Ref{Any}(nothing) - bt2 = Ref{Any}(nothing) - ccall(:jl_get_backtrace, Cvoid, (Ref{Any}, Ref{Any}), bt, bt2) - return _reformat_bt(bt[], bt2[]) + bt, bt2 = ccall(:jl_get_backtrace, Ref{SimpleVector}, ()) + return _reformat_bt(bt::Vector{Ptr{Cvoid}}, bt2::Vector{Any}) end """ @@ -147,12 +159,13 @@ end ## system error handling ## """ - systemerror(sysfunc, iftrue) + systemerror(sysfunc[, errno::Cint=Libc.errno()]) + systemerror(sysfunc, iftrue::Bool) Raises a `SystemError` for `errno` with the descriptive string `sysfunc` if `iftrue` is `true` """ -systemerror(p, b::Bool; extrainfo=nothing) = b ? throw(Main.Base.SystemError(string(p), Libc.errno(), extrainfo)) : nothing - +systemerror(p, b::Bool; extrainfo=nothing) = b ? systemerror(p, extrainfo=extrainfo) : nothing +systemerror(p, errno::Cint=Libc.errno(); extrainfo=nothing) = throw(Main.Base.SystemError(string(p), errno, extrainfo)) ## system errors from Windows API functions struct WindowsErrorInfo @@ -160,12 +173,14 @@ struct WindowsErrorInfo extrainfo end """ - windowserror(sysfunc, iftrue) + windowserror(sysfunc[, code::UInt32=Libc.GetLastError()]) + windowserror(sysfunc, iftrue::Bool) -Like [`systemerror`](@ref), but for Windows API functions that use [`GetLastError`](@ref) instead -of setting [`errno`](@ref). +Like [`systemerror`](@ref), but for Windows API functions that use [`GetLastError`](@ref Base.Libc.GetLastError) to +return an error code instead of setting [`errno`](@ref Base.Libc.errno). """ -windowserror(p, b::Bool; extrainfo=nothing) = b ? throw(Main.Base.SystemError(string(p), Libc.errno(), WindowsErrorInfo(Libc.GetLastError(), extrainfo))) : nothing +windowserror(p, b::Bool; extrainfo=nothing) = b ? windowserror(b, extrainfo=extrainfo) : nothing +windowserror(p, code::UInt32=Libc.GetLastError(); extrainfo=nothing) = throw(Main.Base.SystemError(string(p), 0, WindowsErrorInfo(code, extrainfo))) ## assertion macro ## diff --git a/base/errorshow.jl b/base/errorshow.jl index 3b6495a2a3c9f..4cb0b045a784a 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -320,8 +320,14 @@ function showerror_ambiguous(io::IO, meth, f, args) sigfix = typeintersect(m.sig, sigfix) end if isa(unwrap_unionall(sigfix), DataType) && sigfix <: Tuple - print(io, "\nPossible fix, define\n ") - Base.show_tuple_as_call(io, :function, sigfix) + if all(m->morespecific(sigfix, m.sig), meth) + print(io, "\nPossible fix, define\n ") + Base.show_tuple_as_call(io, :function, sigfix) + else + println(io) + print(io, "To resolve the ambiguity, try making one of the methods more specific, or ") + print(io, "adding a new method more specific than any of the existing applicable methods.") + end end nothing end @@ -705,7 +711,7 @@ end function show(io::IO, ip::InterpreterIP) print(io, typeof(ip)) if ip.code isa Core.CodeInfo - print(io, " in top-level CodeInfo at statement $(Int(ip.stmt))") + print(io, " in top-level CodeInfo for $(ip.mod) at statement $(Int(ip.stmt))") else print(io, " in $(ip.code) at statement $(Int(ip.stmt))") end diff --git a/base/essentials.jl b/base/essentials.jl index 4f36de7baecef..36887ea27c06a 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -113,6 +113,8 @@ macro _propagate_inbounds_meta() return Expr(:meta, :inline, :propagate_inbounds) end +function iterate end + """ convert(T, x) @@ -164,7 +166,7 @@ true function convert end convert(::Type{Union{}}, x) = throw(MethodError(convert, (Union{}, x))) -convert(::Type{Any}, @nospecialize(x)) = x +convert(::Type{Any}, x) = x convert(::Type{T}, x::T) where {T} = x convert(::Type{Type}, x::Type) = x # the ssair optimizer is strongly dependent on this method existing to avoid over-specialization # in the absence of inlining-enabled diff --git a/base/exports.jl b/base/exports.jl index 49063720c14c4..90e177fc8a9f7 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -196,6 +196,7 @@ export # scalar math @evalpoly, + evalpoly, abs, abs2, acos, @@ -744,6 +745,7 @@ export nameof, parentmodule, pathof, + pkgdir, names, which, @isdefined, diff --git a/base/expr.jl b/base/expr.jl index 7e57c2282b549..45726a624d4bc 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -67,8 +67,9 @@ function copy(c::CodeInfo) cnew.slotflags = copy(cnew.slotflags) cnew.codelocs = copy(cnew.codelocs) cnew.linetable = copy(cnew.linetable) - cnew.ssaflags = copy(cnew.ssaflags) - ssavaluetypes = cnew.ssavaluetypes + cnew.ssaflags = copy(cnew.ssaflags) + cnew.edges = cnew.edges === nothing ? nothing : copy(cnew.edges) + ssavaluetypes = cnew.ssavaluetypes ssavaluetypes isa Vector{Any} && (cnew.ssavaluetypes = copy(ssavaluetypes)) return cnew end @@ -196,7 +197,7 @@ end """ @noinline -Prevent the compiler from inlining a function. +Give a hint to the compiler that it should not inline a function. Small functions are typically inlined automatically. By using `@noinline` on small functions, auto-inlining can be @@ -208,6 +209,8 @@ prevented. This is shown in the following example: Function Definition =# end + +If the function is trivial (for example returning a constant) it might get inlined anyway. ``` """ macro noinline(ex) @@ -242,10 +245,8 @@ macro propagate_inbounds(ex) if isa(ex, Expr) pushmeta!(ex, :inline) pushmeta!(ex, :propagate_inbounds) - esc(ex) - else - esc(ex) end + esc(ex) end """ diff --git a/base/fastmath.jl b/base/fastmath.jl index c0475b316122d..741bf20cb6b9e 100644 --- a/base/fastmath.jl +++ b/base/fastmath.jl @@ -24,7 +24,7 @@ module FastMath export @fastmath -import Core.Intrinsics: sqrt_llvm, neg_float_fast, +import Core.Intrinsics: sqrt_llvm_fast, neg_float_fast, add_float_fast, sub_float_fast, mul_float_fast, div_float_fast, rem_float_fast, eq_float_fast, ne_float_fast, lt_float_fast, le_float_fast @@ -91,7 +91,7 @@ const rewrite_op = function make_fastmath(expr::Expr) if expr.head === :quote return expr - elseif expr.head == :call && expr.args[1] == :^ && expr.args[3] isa Integer + elseif expr.head === :call && expr.args[1] === :^ && expr.args[3] isa Integer # mimic Julia's literal_pow lowering of literal integer powers return Expr(:call, :(Base.FastMath.pow_fast), make_fastmath(expr.args[2]), Val{expr.args[3]}()) end @@ -277,7 +277,7 @@ pow_fast(x::Float64, y::Integer) = ccall("llvm.powi.f64", llvmcall, Float64, (Fl pow_fast(x::FloatTypes, ::Val{p}) where {p} = pow_fast(x, p) # inlines already via llvm.powi @inline pow_fast(x, v::Val) = Base.literal_pow(^, x, v) -sqrt_fast(x::FloatTypes) = sqrt_llvm(x) +sqrt_fast(x::FloatTypes) = sqrt_llvm_fast(x) # libm diff --git a/base/file.jl b/base/file.jl index 46ed60f48ac5f..094327922dcc5 100644 --- a/base/file.jl +++ b/base/file.jl @@ -398,6 +398,9 @@ end touch(path::AbstractString) Update the last-modified timestamp on a file to the current time. + +If the file does not exist a new file is created. + Return `path`. # Examples @@ -462,7 +465,10 @@ const TEMP_CLEANUP_LOCK = ReentrantLock() function temp_cleanup_later(path::AbstractString; asap::Bool=false) lock(TEMP_CLEANUP_LOCK) - TEMP_CLEANUP[path] = asap + # each path should only be inserted here once, but if there + # is a collision, let !asap win over asap: if any user might + # still be using the path, don't delete it until process exit + TEMP_CLEANUP[path] = get(TEMP_CLEANUP, path, true) & asap if length(TEMP_CLEANUP) > TEMP_CLEANUP_MAX[] temp_cleanup_purge(false) TEMP_CLEANUP_MAX[] = max(TEMP_CLEANUP_MIN[], 2*length(TEMP_CLEANUP)) @@ -474,12 +480,16 @@ end function temp_cleanup_purge(all::Bool=true) need_gc = Sys.iswindows() for (path, asap) in TEMP_CLEANUP - if (all || asap) && ispath(path) - need_gc && GC.gc(true) - need_gc = false - rm(path, recursive=true, force=true) + try + if (all || asap) && ispath(path) + need_gc && GC.gc(true) + need_gc = false + rm(path, recursive=true, force=true) + end + !ispath(path) && delete!(TEMP_CLEANUP, path) + catch ex + @warn "temp cleanup" _group=:file exception=(ex, catch_backtrace()) end - !ispath(path) && delete!(TEMP_CLEANUP, path) end end @@ -678,7 +688,10 @@ struct uv_dirent_t end """ - readdir(dir::AbstractString=pwd(); join::Bool=false) -> Vector{String} + readdir(dir::AbstractString=pwd(); + join::Bool = false, + sort::Bool = true, + ) -> Vector{String} Return the names in the directory `dir` or the current working directory if not given. When `join` is false, `readdir` returns just the names in the directory @@ -686,8 +699,12 @@ as is; when `join` is true, it returns `joinpath(dir, name)` for each `name` so that the returned strings are full paths. If you want to get absolute paths back, call `readdir` with an absolute directory path and `join` set to true. +By default, `readdir` sorts the list of names it returns. If you want to skip +sorting the names and get them in the order that the file system lists them, +you can use `readir(dir, sort=false)` to opt out of sorting. + !!! compat "Julia 1.4" - The `join` keyword argument requires at least Julia 1.4. + The `join` and `sort` keyword arguments require at least Julia 1.4. # Examples ```julia-repl @@ -744,7 +761,7 @@ julia> readdir(abspath("base"), join=true) "/home/JuliaUser/dev/julia/base/weakkeydict.jl" ``` """ -function readdir(dir::AbstractString; join::Bool=false) +function readdir(dir::AbstractString; join::Bool=false, sort::Bool=true) # Allocate space for uv_fs_t struct uv_readdir_req = zeros(UInt8, ccall(:jl_sizeof_uv_fs_t, Int32, ())) @@ -764,9 +781,13 @@ function readdir(dir::AbstractString; join::Bool=false) # Clean up the request string ccall(:uv_fs_req_cleanup, Cvoid, (Ptr{UInt8},), uv_readdir_req) + # sort entries unless opted out + sort && sort!(entries) + return entries end -readdir(; join::Bool=false) = readdir(join ? pwd() : ".", join=join) +readdir(; join::Bool=false, sort::Bool=true) = + readdir(join ? pwd() : ".", join=join, sort=sort) """ walkdir(dir; topdown=true, follow_symlinks=false, onerror=throw) diff --git a/base/float.jl b/base/float.jl index feb30cdc416bc..7be2bf08e86a9 100644 --- a/base/float.jl +++ b/base/float.jl @@ -765,7 +765,7 @@ The highest finite value representable by the given floating-point DataType `T`. # Examples ```jldoctest julia> floatmax(Float16) -Float16(65500.0) +Float16(6.55e4) julia> floatmax(Float32) 3.4028235f38 diff --git a/base/floatfuncs.jl b/base/floatfuncs.jl index 9c90137e0d4fc..8c934284a44ad 100644 --- a/base/floatfuncs.jl +++ b/base/floatfuncs.jl @@ -313,7 +313,7 @@ fma_llvm(x::Float64, y::Float64, z::Float64) = fma_float(x, y, z) # 1.0000000009313226 = 1 + 1/2^30 # If fma_llvm() clobbers the rounding mode, the result of 0.1 + 0.2 will be 0.3 # instead of the properly-rounded 0.30000000000000004; check after calling fma -if (Sys.ARCH != :i686 && fma_llvm(1.0000305f0, 1.0000305f0, -1.0f0) == 6.103609f-5 && +if (Sys.ARCH !== :i686 && fma_llvm(1.0000305f0, 1.0000305f0, -1.0f0) == 6.103609f-5 && (fma_llvm(1.0000000009313226, 1.0000000009313226, -1.0) == 1.8626451500983188e-9) && 0.1 + 0.2 == 0.30000000000000004) fma(x::Float32, y::Float32, z::Float32) = fma_llvm(x,y,z) diff --git a/base/gcutils.jl b/base/gcutils.jl index 113ba91436f80..4ff580444c6db 100644 --- a/base/gcutils.jl +++ b/base/gcutils.jl @@ -12,7 +12,7 @@ Register a function `f(x)` to be called when there are no program-accessible ref this function is unpredictable. `f` must not cause a task switch, which excludes most I/O operations such as `println`. -`@schedule println("message")` or `ccall(:jl_, Void, (Any,), "message")` may be helpful for +`@schedule println("message")` or `ccall(:jl_, Cvoid, (Any,), "message")` may be helpful for debugging purposes. """ function finalizer(@nospecialize(f), @nospecialize(o)) diff --git a/base/gmp.jl b/base/gmp.jl index 32f21b8b9577b..10ee49e49f22c 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -188,7 +188,7 @@ for op in (:neg, :com, :sqrt, :set) $op!(x::BigInt, a::BigInt) = (ccall($(gmpz(op)), Cvoid, (mpz_t, mpz_t), x, a); x) $op(a::BigInt) = $op!(BigInt(), a) end - op == :set && continue # MPZ.set!(x) would make no sense + op === :set && continue # MPZ.set!(x) would make no sense @eval $op!(x::BigInt) = $op!(x, x) end diff --git a/base/indices.jl b/base/indices.jl index 6b76892ea3415..2cc822bdeeb8f 100644 --- a/base/indices.jl +++ b/base/indices.jl @@ -19,25 +19,49 @@ abstract type IndexStyle end Subtype of [`IndexStyle`](@ref) used to describe arrays which are optimally indexed by one linear index. -A linear indexing style uses one integer to describe the position in the array +A linear indexing style uses one integer index to describe the position in the array (even if it's a multidimensional array) and column-major -ordering is used to access the elements. For example, -if `A` were a `(2, 3)` custom matrix type with linear indexing, -and we referenced `A[5]` (using linear style), this would -be equivalent to referencing `A[1, 3]` (since `2*1 + 3 = 5`). +ordering is used to efficiently access the elements. This means that +requesting [`eachindex`](@ref) from an array that is `IndexLinear` will return +a simple one-dimensional range, even if it is multidimensional. + +A custom array that reports its `IndexStyle` as `IndexLinear` only needs +to implement indexing (and indexed assignment) with a single `Int` index; +all other indexing expressions — including multidimensional accesses — will +be recomputed to the linear index. For example, if `A` were a `2×3` custom +matrix with linear indexing, and we referenced `A[1, 3]`, this would be +recomputed to the equivalent linear index and call `A[5]` since `2*1 + 3 = 5`. + See also [`IndexCartesian`](@ref). """ struct IndexLinear <: IndexStyle end + """ IndexCartesian() Subtype of [`IndexStyle`](@ref) used to describe arrays which -are optimally indexed by a Cartesian index. - -A cartesian indexing style uses multiple integers/indices to describe the position in the array. -For example, if `A` were a `(2, 3, 4)` custom array type with cartesian indexing, -we could reference `A[2, 1, 3]` and Julia would automatically convert this into the -correct location in the underlying memory. See also [`IndexLinear`](@ref). +are optimally indexed by a Cartesian index. This is the default +for new custom [`AbstractArray`](@ref) subtypes. + +A Cartesian indexing style uses multiple integer indices to describe the position in +a multidimensional array, with exactly one index per dimension. This means that +requesting [`eachindex`](@ref) from an array that is `IndexCartesian` will return +a range of [`CartesianIndices`](@ref). + +A `N`-dimensional custom array that reports its `IndexStyle` as `IndexCartesian` needs +to implement indexing (and indexed assignment) with exactly `N` `Int` indices; +all other indexing expressions — including linear indexing — will +be recomputed to the equivalent Cartesian location. For example, if `A` were a `2×3` custom +matrix with cartesian indexing, and we referenced `A[5]`, this would be +recomputed to the equivalent Cartesian index and call `A[1, 3]` since `5 = 2*1 + 3`. + +It is significantly more expensive to compute Cartesian indices from a linear index than it is +to go the other way. The former operation requires division — a very costly operation — whereas +the latter only uses multiplication and addition and is essentially free. This asymmetry means it +is far more costly to use linear indexing with an `IndexCartesian` array than it is to use +Cartesian indexing with an `IndexLinear` array. + +See also [`IndexLinear`](@ref). """ struct IndexCartesian <: IndexStyle end @@ -48,7 +72,7 @@ struct IndexCartesian <: IndexStyle end `IndexStyle` specifies the "native indexing style" for array `A`. When you define a new [`AbstractArray`](@ref) type, you can choose to implement either linear indexing (with [`IndexLinear`](@ref)) or cartesian indexing. -If you decide to implement linear indexing, then you must set this trait for your array +If you decide to only implement linear indexing, then you must set this trait for your array type: Base.IndexStyle(::Type{<:MyArray}) = IndexLinear() @@ -56,7 +80,7 @@ type: The default is [`IndexCartesian()`](@ref). Julia's internal indexing machinery will automatically (and invisibly) -convert all indexing operations into the preferred style. This allows users +recompute all indexing operations into the preferred style. This allows users to access elements of your array using any indexing style, even when explicit methods have not been provided. @@ -84,14 +108,14 @@ promote_shape(::Tuple{}, ::Tuple{}) = () function promote_shape(a::Tuple{Int,}, b::Tuple{Int,}) if a[1] != b[1] - throw(DimensionMismatch("dimensions must match")) + throw(DimensionMismatch("dimensions must match: a has dims $a, b has dims $b")) end return a end function promote_shape(a::Tuple{Int,Int}, b::Tuple{Int,}) if a[1] != b[1] || a[2] != 1 - throw(DimensionMismatch("dimensions must match")) + throw(DimensionMismatch("dimensions must match: a has dims $a, b has dims $b")) end return a end @@ -100,7 +124,7 @@ promote_shape(a::Tuple{Int,}, b::Tuple{Int,Int}) = promote_shape(b, a) function promote_shape(a::Tuple{Int, Int}, b::Tuple{Int, Int}) if a[1] != b[1] || a[2] != b[2] - throw(DimensionMismatch("dimensions must match")) + throw(DimensionMismatch("dimensions must match: a has dims $a, b has dims $b")) end return a end @@ -130,12 +154,12 @@ function promote_shape(a::Dims, b::Dims) end for i=1:length(b) if a[i] != b[i] - throw(DimensionMismatch("dimensions must match")) + throw(DimensionMismatch("dimensions must match: a has dims $a, b has dims $b, mismatch at $i")) end end for i=length(b)+1:length(a) if a[i] != 1 - throw(DimensionMismatch("dimensions must match")) + throw(DimensionMismatch("dimensions must match: a has dims $a, must have singleton at dim $i")) end end return a @@ -151,12 +175,12 @@ function promote_shape(a::Indices, b::Indices) end for i=1:length(b) if a[i] != b[i] - throw(DimensionMismatch("dimensions must match")) + throw(DimensionMismatch("dimensions must match: a has dims $a, b has dims $b, mismatch at $i")) end end for i=length(b)+1:length(a) if a[i] != 1:1 - throw(DimensionMismatch("dimensions must match")) + throw(DimensionMismatch("dimensions must match: a has dims $a, must have singleton at dim $i")) end end return a diff --git a/base/initdefs.jl b/base/initdefs.jl index a810ceb01f20f..85a3d57cd3875 100644 --- a/base/initdefs.jl +++ b/base/initdefs.jl @@ -300,15 +300,23 @@ const atexit_hooks = Callable[Filesystem.temp_cleanup_purge] Register a zero-argument function `f()` to be called at process exit. `atexit()` hooks are called in last in first out (LIFO) order and run before object finalizers. + +Exit hooks are allowed to call `exit(n)`, in which case Julia will exit with +exit code `n` (instead of the original exit code). If more than one exit hook +calls `exit(n)`, then Julia will exit with the exit code corresponding to the +last called exit hook that calls `exit(n)`. (Because exit hooks are called in +LIFO order, "last called" is equivalent to "first registered".) """ atexit(f::Function) = (pushfirst!(atexit_hooks, f); nothing) function _atexit() - for f in atexit_hooks + while !isempty(atexit_hooks) + f = popfirst!(atexit_hooks) try f() - catch err - show(stderr, err) + catch ex + showerror(stderr, ex) + Base.show_backtrace(stderr, catch_backtrace()) println(stderr) end end diff --git a/base/iterators.jl b/base/iterators.jl index 77849b4c096e0..0b501006ab946 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -1022,37 +1022,56 @@ Iterate over a collection `n` elements at a time. # Examples ```jldoctest julia> collect(Iterators.partition([1,2,3,4,5], 2)) -3-element Array{Array{Int64,1},1}: +3-element Array{SubArray{Int64,1,Array{Int64,1},Tuple{UnitRange{Int64}},true},1}: [1, 2] [3, 4] [5] ``` """ -partition(c::T, n::Integer) where {T} = PartitionIterator{T}(c, Int(n)) +function partition(c, n::Integer) + n < 1 && throw(ArgumentError("cannot create partitions of length $n")) + return PartitionIterator(c, Int(n)) +end struct PartitionIterator{T} c::T n::Int end +# Partitions are explicitly a linear indexing operation, so reshape to 1-d immediately +PartitionIterator(A::AbstractArray, n::Int) = PartitionIterator(vec(A), n) +PartitionIterator(v::AbstractVector, n::Int) = PartitionIterator{typeof(v)}(v, n) eltype(::Type{PartitionIterator{T}}) where {T} = Vector{eltype(T)} +# Arrays use a generic `view`-of-a-`vec`, so we cannot exactly predict what we'll get back +eltype(::Type{PartitionIterator{T}}) where {T<:AbstractArray} = AbstractVector{eltype(T)} +# But for some common implementations in Base we know the answer exactly +eltype(::Type{PartitionIterator{T}}) where {T<:Vector} = SubArray{eltype(T), 1, T, Tuple{UnitRange{Int}}, true} + +IteratorEltype(::Type{<:PartitionIterator{T}}) where {T} = IteratorEltype(T) +IteratorEltype(::Type{<:PartitionIterator{T}}) where {T<:AbstractArray} = EltypeUnknown() +IteratorEltype(::Type{<:PartitionIterator{T}}) where {T<:Vector} = IteratorEltype(T) + partition_iteratorsize(::HasShape) = HasLength() partition_iteratorsize(isz) = isz function IteratorSize(::Type{PartitionIterator{T}}) where {T} partition_iteratorsize(IteratorSize(T)) end -IteratorEltype(::Type{<:PartitionIterator{T}}) where {T} = IteratorEltype(T) - function length(itr::PartitionIterator) l = length(itr.c) return div(l, itr.n) + ((mod(l, itr.n) > 0) ? 1 : 0) end -function iterate(itr::PartitionIterator{<:Vector}, state=1) +function iterate(itr::PartitionIterator{<:AbstractRange}, state=1) + state > length(itr.c) && return nothing + r = min(state + itr.n - 1, length(itr.c)) + return @inbounds itr.c[state:r], r + 1 +end + +function iterate(itr::PartitionIterator{<:AbstractArray}, state=1) state > length(itr.c) && return nothing r = min(state + itr.n - 1, length(itr.c)) - return view(itr.c, state:r), r + 1 + return @inbounds view(itr.c, state:r), r + 1 end struct IterationCutShort; end diff --git a/base/libuv.jl b/base/libuv.jl index 737dcc67d3efe..2ed665c46e663 100644 --- a/base/libuv.jl +++ b/base/libuv.jl @@ -98,10 +98,10 @@ uv_error(prefix::AbstractString, c::Integer) = c < 0 ? throw(_UVError(prefix, c) ## event loop ## -eventloop() = uv_eventloop::Ptr{Cvoid} +eventloop() = ccall(:jl_global_event_loop, Ptr{Cvoid}, ()) function process_events() - return ccall(:jl_process_events, Int32, (Ptr{Cvoid},), eventloop()) + return ccall(:jl_process_events, Int32, ()) end function uv_alloc_buf end @@ -119,7 +119,6 @@ function reinit_stdio() global uv_jl_asynccb = @cfunction(uv_asynccb, Cvoid, (Ptr{Cvoid},)) global uv_jl_timercb = @cfunction(uv_timercb, Cvoid, (Ptr{Cvoid},)) - global uv_eventloop = ccall(:jl_global_event_loop, Ptr{Cvoid}, ()) global stdin = init_stdio(ccall(:jl_stdin_stream, Ptr{Cvoid}, ())) global stdout = init_stdio(ccall(:jl_stdout_stream, Ptr{Cvoid}, ())) global stderr = init_stdio(ccall(:jl_stderr_stream, Ptr{Cvoid}, ())) diff --git a/base/linked_list.jl b/base/linked_list.jl index 195d2d02d61f1..beceb24a27f40 100644 --- a/base/linked_list.jl +++ b/base/linked_list.jl @@ -100,7 +100,7 @@ function list_deletefirst!(q::InvasiveLinkedList{T}, val::T) where T head_next = head.next while head_next !== val head = head_next - head_next = head.next + head_next = head.next::Union{T, Nothing} end if q.tail::T === val head.next = nothing diff --git a/base/loading.jl b/base/loading.jl index a4a40300c97c6..8c9efe1f63cc4 100644 --- a/base/loading.jl +++ b/base/loading.jl @@ -265,7 +265,7 @@ end """ pathof(m::Module) -Return the path of `m.jl` file that was used to `import` module `m`, +Return the path of the `m.jl` file that was used to `import` module `m`, or `nothing` if `m` was not imported from a package. Use [`dirname`](@ref) to get the directory part and [`basename`](@ref) @@ -277,6 +277,19 @@ function pathof(m::Module) return Base.locate_package(pkgid) end +""" + pkgdir(m::Module) + + Return the root directory of the package that imported module `m`, + or `nothing` if `m` was not imported from a package. + """ +function pkgdir(m::Module) + rootmodule = Base.moduleroot(m) + path = pathof(rootmodule) + path === nothing && return nothing + return dirname(dirname(path)) +end + ## generic project & manifest API ## const project_names = ("JuliaProject.toml", "Project.toml") @@ -477,15 +490,15 @@ function explicit_project_deps_get(project_file::String, name::String)::Union{No state = :top for line in eachline(io) if occursin(re_section, line) - state == :top && root_name == name && return root_uuid + state === :top && root_name == name && return root_uuid state = occursin(re_section_deps, line) ? :deps : :other - elseif state == :top + elseif state === :top if (m = match(re_name_to_string, line)) !== nothing root_name = String(m.captures[1]) elseif (m = match(re_uuid_to_string, line)) !== nothing root_uuid = UUID(m.captures[1]) end - elseif state == :deps + elseif state === :deps if (m = match(re_key_to_string, line)) !== nothing m.captures[1] == name && return UUID(m.captures[2]) end @@ -510,7 +523,7 @@ function explicit_manifest_deps_get(project_file::String, where::UUID, name::Str uuid == where && break uuid = deps = nothing state = :stanza - elseif state == :stanza + elseif state === :stanza if (m = match(re_uuid_to_string, line)) !== nothing uuid = UUID(m.captures[1]) elseif (m = match(re_deps_to_any, line)) !== nothing @@ -520,7 +533,7 @@ function explicit_manifest_deps_get(project_file::String, where::UUID, name::Str elseif occursin(re_section, line) state = :other end - elseif state == :deps && uuid == where + elseif state === :deps && uuid == where # [deps] section format gives both name and uuid if (m = match(re_key_to_string, line)) !== nothing m.captures[1] == name && return UUID(m.captures[2]) @@ -1191,7 +1204,10 @@ function compilecache_path(pkg::PkgId)::String if pkg.uuid === nothing abspath(cachepath, entryfile) * ".ji" else - project_precompile_slug = slug(_crc32c(something(Base.active_project(), "")), 5) + crc = _crc32c(something(Base.active_project(), "")) + crc = _crc32c(unsafe_string(JLOptions().image_file), crc) + crc = _crc32c(unsafe_string(JLOptions().julia_bin), crc) + project_precompile_slug = slug(crc, 5) abspath(cachepath, string(entryfile, "_", project_precompile_slug, ".ji")) end end diff --git a/base/lock.jl b/base/lock.jl index ad728280c43e9..646189ea196fc 100644 --- a/base/lock.jl +++ b/base/lock.jl @@ -145,6 +145,16 @@ function relockall(rl::ReentrantLock, n::Int) return end +""" + lock(f::Function, lock) + +Acquire the `lock`, execute `f` with the `lock` held, and release the `lock` when `f` +returns. If the lock is already locked by a different task/thread, wait for it to become +available. + +When this function returns, the `lock` has been released, so the caller should +not attempt to `unlock` it. +""" function lock(f, l::AbstractLock) lock(l) try diff --git a/base/locks-mt.jl b/base/locks-mt.jl index 3317bb4d6439d..fff8d98b35e5e 100644 --- a/base/locks-mt.jl +++ b/base/locks-mt.jl @@ -26,19 +26,42 @@ Test-and-test-and-set spin locks are quickest up to about 30ish contending threads. If you have more contention than that, different synchronization approaches should be considered. """ -struct SpinLock <: AbstractLock - handle::Atomic{Int} - SpinLock() = new(Atomic{Int}(0)) +mutable struct SpinLock <: AbstractLock + handle::Int + SpinLock() = new(0) end +import Base.Sys.WORD_SIZE + +@eval _xchg!(x::SpinLock, v::Int) = + llvmcall($""" + %ptr = inttoptr i$WORD_SIZE %0 to i$WORD_SIZE* + %rv = atomicrmw xchg i$WORD_SIZE* %ptr, i$WORD_SIZE %1 acq_rel + ret i$WORD_SIZE %rv + """, Int, Tuple{Ptr{Int}, Int}, unsafe_convert(Ptr{Int}, pointer_from_objref(x)), v) + +@eval _get(x::SpinLock) = + llvmcall($""" + %ptr = inttoptr i$WORD_SIZE %0 to i$WORD_SIZE* + %rv = load atomic i$WORD_SIZE, i$WORD_SIZE* %ptr acquire, align $(gc_alignment(Int)) + ret i$WORD_SIZE %rv + """, Int, Tuple{Ptr{Int}}, unsafe_convert(Ptr{Int}, pointer_from_objref(x))) + +@eval _set!(x::SpinLock, v::Int) = + llvmcall($""" + %ptr = inttoptr i$WORD_SIZE %0 to i$WORD_SIZE* + store atomic i$WORD_SIZE %1, i$WORD_SIZE* %ptr release, align $(gc_alignment(Int)) + ret void + """, Cvoid, Tuple{Ptr{Int}, Int}, unsafe_convert(Ptr{Int}, pointer_from_objref(x)), v) + # Note: this cannot assert that the lock is held by the correct thread, because we do not # track which thread locked it. Users beware. Base.assert_havelock(l::SpinLock) = islocked(l) ? nothing : concurrency_violation() function lock(l::SpinLock) while true - if l.handle[] == 0 - p = atomic_xchg!(l.handle, 1) + if _get(l) == 0 + p = _xchg!(l, 1) if p == 0 return end @@ -50,18 +73,18 @@ function lock(l::SpinLock) end function trylock(l::SpinLock) - if l.handle[] == 0 - return atomic_xchg!(l.handle, 1) == 0 + if _get(l) == 0 + return _xchg!(l, 1) == 0 end return false end function unlock(l::SpinLock) - l.handle[] = 0 + _set!(l, 0) ccall(:jl_cpu_wake, Cvoid, ()) return end function islocked(l::SpinLock) - return l.handle[] != 0 + return _get(l) != 0 end diff --git a/base/logging.jl b/base/logging.jl index 7d3b8d18cd497..0de8ae9337374 100644 --- a/base/logging.jl +++ b/base/logging.jl @@ -33,8 +33,9 @@ abstract type AbstractLogger ; end Log a message to `logger` at `level`. The logical location at which the message was generated is given by module `_module` and `group`; the source -location by `file` and `line`. `id` is an arbitrary unique [`Symbol`](@ref) to be used -as a key to identify the log statement when filtering. +location by `file` and `line`. `id` is an arbitrary unique value (typically a +[`Symbol`](@ref)) to be used as a key to identify the log statement when +filtering. """ function handle_message end @@ -260,20 +261,20 @@ function logmsg_code(_module, file, line, level, message, exs...) end k = ex.args[1] # Recognize several special keyword arguments - if k == :_id + if k === :_id # id may be overridden if you really want several log # statements to share the same id (eg, several pertaining to # the same progress step). In those cases it may be wise to # manually call log_record_id to get a unique id in the same # format. id = esc(v) - elseif k == :_module + elseif k === :_module _module = esc(v) - elseif k == :_line + elseif k === :_line line = esc(v) - elseif k == :_file + elseif k === :_file file = esc(v) - elseif k == :_group + elseif k === :_group group = esc(v) else # Copy across key value pairs for structured log records diff --git a/base/math.jl b/base/math.jl index c9e7397cbe30c..20cb374c5c33b 100644 --- a/base/math.jl +++ b/base/math.jl @@ -13,7 +13,7 @@ export sin, cos, sincos, tan, sinh, cosh, tanh, asin, acos, atan, cbrt, sqrt, significand, hypot, max, min, minmax, ldexp, frexp, clamp, clamp!, modf, ^, mod2pi, rem2pi, - @evalpoly + @evalpoly, evalpoly import .Base: log, exp, sin, cos, tan, sinh, cosh, tanh, asin, acos, atan, asinh, acosh, atanh, sqrt, log2, log10, @@ -81,18 +81,109 @@ function clamp!(x::AbstractArray, lo, hi) x end + +""" + evalpoly(x, p) + +Evaluate the polynomial ``\\sum_k p[k] x^{k-1}`` for the coefficients `p[1]`, `p[2]`, ...; +that is, the coefficients are given in ascending order by power of `x`. +Loops are unrolled at compile time if the number of coefficients is statically known, i.e. +when `p` is a `Tuple`. +This function generates efficient code using Horner's method if `x` is real, or using +a Goertzel-like [^DK62] algorithm if `x` is complex. + +[^DK62]: Donald Knuth, Art of Computer Programming, Volume 2: Seminumerical Algorithms, Sec. 4.6.4. + +!!! compat "Julia 1.4" + This function requires Julia 1.4 or later. + +# Example +```jldoctest +julia> evalpoly(2, (1, 2, 3)) +17 +``` +""" +function evalpoly(x, p::Tuple) + if @generated + N = length(p.parameters) + ex = :(p[end]) + for i in N-1:-1:1 + ex = :(muladd(x, $ex, p[$i])) + end + ex + else + _evalpoly(x, p) + end +end + +evalpoly(x, p::AbstractVector) = _evalpoly(x, p) + +function _evalpoly(x, p) + N = length(p) + ex = p[end] + for i in N-1:-1:1 + ex = muladd(x, ex, p[i]) + end + ex +end + +function evalpoly(z::Complex, p::Tuple) + if @generated + N = length(p.parameters) + a = :(p[end]) + b = :(p[end-1]) + as = [] + for i in N-2:-1:1 + ai = Symbol("a", i) + push!(as, :($ai = $a)) + a = :(muladd(r, $ai, $b)) + b = :(p[$i] - s * $ai) + end + ai = :a0 + push!(as, :($ai = $a)) + C = Expr(:block, + :(x = real(z)), + :(y = imag(z)), + :(r = x + x), + :(s = muladd(x, x, y*y)), + as..., + :(muladd($ai, z, $b))) + else + _evalpoly(z, p) + end +end +evalpoly(z::Complex, p::Tuple{<:Any}) = p[1] + + +evalpoly(z::Complex, p::AbstractVector) = _evalpoly(z, p) + +function _evalpoly(z::Complex, p) + length(p) == 1 && return p[1] + N = length(p) + a = p[end] + b = p[end-1] + + x = real(z) + y = imag(z) + r = 2x + s = muladd(x, x, y*y) + for i in N-2:-1:1 + ai = a + a = muladd(r, ai, b) + b = p[i] - s * ai + end + ai = a + muladd(ai, z, b) +end + """ @horner(x, p...) Evaluate `p[1] + x * (p[2] + x * (....))`, i.e. a polynomial via Horner's rule. """ macro horner(x, p...) - ex = esc(p[end]) - for i = length(p)-1:-1:1 - ex = :(muladd(t, $ex, $(esc(p[i])))) - end - ex = quote local r = $ex end # structure this to add exactly one line number node for the macro - return Expr(:block, :(local t = $(esc(x))), ex, :r) + xesc, pesc = esc(x), esc.(p) + :(invoke(evalpoly, Tuple{Any, Tuple}, $xesc, ($(pesc...),))) end # Evaluate p[1] + z*p[2] + z^2*p[3] + ... + z^(n-1)*p[n]. This uses @@ -121,28 +212,8 @@ julia> @evalpoly(2, 1, 1, 1) ``` """ macro evalpoly(z, p...) - a = :($(esc(p[end]))) - b = :($(esc(p[end-1]))) - as = [] - for i = length(p)-2:-1:1 - ai = Symbol("a", i) - push!(as, :($ai = $a)) - a = :(muladd(r, $ai, $b)) - b = :($(esc(p[i])) - s * $ai) # see issue #15985 on fused mul-subtract - end - ai = :a0 - push!(as, :($ai = $a)) - C = Expr(:block, - :(x = real(tt)), - :(y = imag(tt)), - :(r = x + x), - :(s = muladd(x, x, y*y)), - as..., - :(muladd($ai, tt, $b))) - R = Expr(:macrocall, Symbol("@horner"), (), :tt, map(esc, p)...) - :(let tt = $(esc(z)) - isa(tt, Complex) ? $C : $R - end) + zesc, pesc = esc(z), esc.(p) + :(evalpoly($zesc, ($(pesc...),))) end """ @@ -986,6 +1057,12 @@ This function computes a floating point representation of the modulus after divi numerically exact `2π`, and is therefore not exactly the same as `mod(x,2π)`, which would compute the modulus of `x` relative to division by the floating-point number `2π`. +!!! note + Depending on the format of the input value, the closest representable value to 2π may + be less than 2π. For example, the expression `mod2pi(2π)` will not return `0`, because + the intermediate value of `2*π` is a `Float64` and `2*Float64(π) < 2*big(π)`. See + [`rem2pi`](@ref) for more refined control of this behavior. + # Examples ```jldoctest julia> mod2pi(9*pi/4) diff --git a/base/methodshow.jl b/base/methodshow.jl index 68bfa5df750fa..df2de34f3740d 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -63,10 +63,10 @@ function arg_decl_parts(m::Method) for t in tv show_env = ImmutableDict(show_env, :unionall_env => t) end - decls = Any[argtype_decl(show_env, argnames[i], sig, i, m.nargs, m.isva) + decls = Tuple{String,String}[argtype_decl(show_env, argnames[i], sig, i, m.nargs, m.isva) for i = 1:m.nargs] else - decls = Any[("", "") for i = 1:length(sig.parameters)] + decls = Tuple{String,String}[("", "") for i = 1:length(sig.parameters::SimpleVector)] end return tv, decls, file, line end @@ -119,8 +119,25 @@ end # In case the line numbers in the source code have changed since the code was compiled, # allow packages to set a callback function that corrects them. # (Used by Revise and perhaps other packages.) -default_methodloc(method::Method) = method.file, method.line -const methodloc_callback = Ref{Function}(default_methodloc) +const methodloc_callback = Ref{Union{Function, Nothing}}(nothing) + +# This function does the method location updating +function updated_methodloc(m::Method)::Tuple{String, Int32} + file, line = m.file, m.line + if methodloc_callback[] !== nothing + try + file, line = invokelatest(methodloc_callback[], m) + catch + end + end + # The file defining Base.Sys gets included after this file is included so make sure + # this function is valid even in this intermediary state + if isdefined(@__MODULE__, :Sys) && Sys.BUILD_STDLIB_PATH != Sys.STDLIB + # BUILD_STDLIB_PATH gets defined in sysinfo.jl + file = replace(string(file), normpath(Sys.BUILD_STDLIB_PATH) => normpath(Sys.STDLIB)) + end + return string(file), line +end functionloc(m::Core.MethodInstance) = functionloc(m.def) @@ -130,7 +147,7 @@ functionloc(m::Core.MethodInstance) = functionloc(m.def) Returns a tuple `(filename,line)` giving the location of a `Method` definition. """ function functionloc(m::Method) - file, ln = invokelatest(methodloc_callback[], m) + file, ln = updated_methodloc(m) if ln <= 0 error("could not determine location of method definition") end @@ -166,9 +183,11 @@ function show(io::IO, m::Method) ft = unwrap_unionall(ft0) d1 = decls[1] if sig === Tuple - print(io, m.name) - decls = Any[(), ("...", "")] - elseif ft <: Function && isa(ft, DataType) && + # Builtin + print(io, m.name, "(...) in ", m.module) + return + end + if ft <: Function && isa(ft, DataType) && isdefined(ft.name.module, ft.name.mt.name) && # TODO: more accurate test? (tn.name === "#" name) ft0 === typeof(getfield(ft.name.module, ft.name.mt.name)) @@ -184,7 +203,7 @@ function show(io::IO, m::Method) print(io, "(", d1[1], "::", d1[2], ")") end print(io, "(") - join(io, [isempty(d[2]) ? d[1] : d[1]*"::"*d[2] for d in decls[2:end]], + join(io, String[isempty(d[2]) ? d[1] : d[1]*"::"*d[2] for d in decls[2:end]], ", ", ", ") kwargs = kwarg_decl(m) if !isempty(kwargs) @@ -195,10 +214,7 @@ function show(io::IO, m::Method) show_method_params(io, tv) print(io, " in ", m.module) if line > 0 - try - file, line = invokelatest(methodloc_callback[], m) - catch - end + file, line = updated_methodloc(m) print(io, " at ", file, ":", line) end end @@ -245,13 +261,9 @@ function show_method_table(io::IO, ms::MethodList, max::Int=-1, header::Bool=tru if max==-1 || n", d1[2], ")") end print(io, "(") - join(io, [isempty(d[2]) ? d[1] : d[1]*"::"*d[2]*"" + join(io, String[isempty(d[2]) ? d[1] : d[1]*"::"*d[2]*"" for d in decls[2:end]], ", ", ", ") kwargs = kwarg_decl(m) if !isempty(kwargs) @@ -361,10 +378,7 @@ function show(io::IO, ::MIME"text/html", m::Method) end print(io, " in ", m.module) if line > 0 - try - file, line = invokelatest(methodloc_callback[], m) - catch - end + file, line = updated_methodloc(m) u = url(m) if isempty(u) print(io, " at ", file, ":", line) @@ -398,11 +412,7 @@ function show(io::IO, mime::MIME"text/plain", mt::AbstractVector{Method}) first = false print(io, "[$(i)] ") show(io, m) - file, line = m.file, m.line - try - file, line = invokelatest(methodloc_callback[], m) - catch - end + file, line = updated_methodloc(m) push!(LAST_SHOWN_LINE_INFOS, (string(file), line)) end end diff --git a/base/mpfr.jl b/base/mpfr.jl index a404c392f5ce5..97bcf234ef9ce 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -953,27 +953,21 @@ end setprecision(f::Function, prec::Integer) = setprecision(f, BigFloat, prec) function string_mpfr(x::BigFloat, fmt::String) - buf = Base.StringVector(0) - s = _calculate_buffer_size!(buf, fmt, x) - resize!(buf, s) - _fill_buffer!(buf, fmt, x) - String(buf) -end - -function _calculate_buffer_size!(buf, fmt, x::BigFloat) - ccall((:mpfr_snprintf,:libmpfr), - Int32, (Ptr{UInt8}, Culong, Ptr{UInt8}, Ref{BigFloat}...), - buf, 0, fmt, x) -end - -function _fill_buffer!(buf, fmt, x::BigFloat) - s = length(buf) - # we temporarily need one more item in buffer to capture null termination - resize!(buf, s + 1) - n = ccall((:mpfr_sprintf,:libmpfr), Int32, (Ptr{UInt8}, Ptr{UInt8}, Ref{BigFloat}...), buf, fmt, x) - @assert n + 1 == length(buf) - @assert last(buf) == 0x00 - resize!(buf, s) + pc = Ref{Ptr{UInt8}}() + n = ccall((:mpfr_asprintf,:libmpfr), Cint, + (Ptr{Ptr{UInt8}}, Ptr{UInt8}, Ref{BigFloat}...), + pc, fmt, x) + p = pc[] + # convert comma decimal separator to dot + for i = 1:n + if unsafe_load(p, i) == UInt8(',') + unsafe_store!(p, '.', i) + break + end + end + str = unsafe_string(p) + ccall((:mpfr_free_str, :libmpfr), Cvoid, (Ptr{UInt8},), p) + return str end function _prettify_bigfloat(s::String)::String diff --git a/base/multidimensional.jl b/base/multidimensional.jl index f3cebeec249ff..3745186f5cc4f 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -8,8 +8,9 @@ module IteratorsMD import .Base: +, -, *, (:) import .Base: simd_outer_range, simd_inner_length, simd_index - using .Base: IndexLinear, IndexCartesian, AbstractCartesianIndex, fill_to_length, tail - using .Base.Iterators: Reverse + using .Base: IndexLinear, IndexCartesian, AbstractCartesianIndex, fill_to_length, tail, + ReshapedArray, ReshapedArrayLF, OneTo + using .Base.Iterators: Reverse, PartitionIterator export CartesianIndex, CartesianIndices @@ -463,6 +464,60 @@ module IteratorsMD iterate(iter::Reverse{<:CartesianIndices{0}}, state=false) = state ? nothing : (CartesianIndex(), true) Base.LinearIndices(inds::CartesianIndices{N,R}) where {N,R} = LinearIndices{N,R}(inds.indices) + + # Views of reshaped CartesianIndices are used for partitions — ensure these are fast + const CartesianPartition{T<:CartesianIndex, P<:CartesianIndices, R<:ReshapedArray{T,1,P}} = SubArray{T,1,R,Tuple{UnitRange{Int}},false} + eltype(::Type{PartitionIterator{T}}) where {T<:ReshapedArrayLF} = SubArray{eltype(T), 1, T, Tuple{UnitRange{Int}}, true} + eltype(::Type{PartitionIterator{T}}) where {T<:ReshapedArray} = SubArray{eltype(T), 1, T, Tuple{UnitRange{Int}}, false} + Iterators.IteratorEltype(::Type{<:PartitionIterator{T}}) where {T<:ReshapedArray} = Iterators.IteratorEltype(T) + + eltype(::Type{PartitionIterator{T}}) where {T<:OneTo} = UnitRange{eltype(T)} + eltype(::Type{PartitionIterator{T}}) where {T<:Union{UnitRange, StepRange, StepRangeLen, LinRange}} = T + Iterators.IteratorEltype(::Type{<:PartitionIterator{T}}) where {T<:Union{OneTo, UnitRange, StepRange, StepRangeLen, LinRange}} = Iterators.IteratorEltype(T) + + + @inline function iterate(iter::CartesianPartition) + isempty(iter) && return nothing + f = first(iter) + return (f, (f, 1)) + end + @inline function iterate(iter::CartesianPartition, (state, n)) + n >= length(iter) && return nothing + I = IteratorsMD.inc(state.I, first(iter.parent.parent).I, last(iter.parent.parent).I) + return I, (I, n+1) + end + + @inline function simd_outer_range(iter::CartesianPartition) + # In general, the Cartesian Partition might start and stop in the middle of the outer + # dimensions — thus the outer range of a CartesianPartition is itself a + # CartesianPartition. + t = tail(iter.parent.parent.indices) + ci = CartesianIndices(t) + li = LinearIndices(t) + return @inbounds view(ci, li[tail(iter[1].I)...]:li[tail(iter[end].I)...]) + end + function simd_outer_range(iter::CartesianPartition{CartesianIndex{2}}) + # But for two-dimensional Partitions the above is just a simple one-dimensional range + # over the second dimension; we don't need to worry about non-rectangular staggers in + # higher dimensions. + return @inbounds CartesianIndices((iter[1][2]:iter[end][2],)) + end + @inline function simd_inner_length(iter::CartesianPartition, I::CartesianIndex) + inner = iter.parent.parent.indices[1] + @inbounds fi = iter[1].I + @inbounds li = iter[end].I + inner_start = I.I == tail(fi) ? fi[1] : first(inner) + inner_end = I.I == tail(li) ? li[1] : last(inner) + return inner_end - inner_start + 1 + end + @inline function simd_index(iter::CartesianPartition, Ilast::CartesianIndex, I1::Int) + # I1 is the 0-based distance from the first dimension's offest + offset = first(iter.parent.parent.indices[1]) # (this is 1 for 1-based arrays) + # In the first column we need to also add in the iter's starting point (branchlessly) + f = @inbounds iter[1] + startoffset = (Ilast.I == tail(f.I))*(f[1] - 1) + CartesianIndex((I1 + offset + startoffset, Ilast.I...)) + end end # IteratorsMD diff --git a/base/operators.jl b/base/operators.jl index 51105d21c2816..049fdf3410273 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -718,6 +718,9 @@ julia> 9 ÷ 4 julia> -5 ÷ 3 -1 + +julia> 5.0 ÷ 2 +2.0 ``` """ div @@ -827,6 +830,13 @@ julia> [1:5;] |> x->x.^2 |> sum |> inv Compose functions: i.e. `(f ∘ g)(args...)` means `f(g(args...))`. The `∘` symbol can be entered in the Julia REPL (and most editors, appropriately configured) by typing `\\circ`. +Function composition also works in prefix form: `∘(f, g)` is the same as `f ∘ g`. +The prefix form supports composition of multiple functions: `∘(f, g, h) = f ∘ g ∘ h` +and splatting `∘(fs...)` for composing an iterable collection of functions. + +!!! compat "Julia 1.4" + Multiple function composition requires at least Julia 1.4. + # Examples ```jldoctest julia> map(uppercase∘first, ["apple", "banana", "carrot"]) @@ -834,10 +844,20 @@ julia> map(uppercase∘first, ["apple", "banana", "carrot"]) 'A' 'B' 'C' + +julia> fs = [ + x -> 2x + x -> x/2 + x -> x-1 + x -> x+1 + ]; + +julia> ∘(fs...)(3) +3.0 ``` """ ∘(f, g) = (x...)->f(g(x...)) - +∘(f, g, h...) = ∘(f ∘ g, h...) """ !f::Function diff --git a/base/ordering.jl b/base/ordering.jl index 52320ac83ae89..ea1887c6ae471 100644 --- a/base/ordering.jl +++ b/base/ordering.jl @@ -27,11 +27,12 @@ end ReverseOrdering(rev::ReverseOrdering) = rev.fwd ReverseOrdering(fwd::Fwd) where {Fwd} = ReverseOrdering{Fwd}(fwd) +ReverseOrdering() = ReverseOrdering(ForwardOrdering()) const DirectOrdering = Union{ForwardOrdering,ReverseOrdering{ForwardOrdering}} const Forward = ForwardOrdering() -const Reverse = ReverseOrdering(Forward) +const Reverse = ReverseOrdering() struct By{T} <: Ordering by::T diff --git a/base/path.jl b/base/path.jl index 2a9ac3cdaab7f..ae681dc8c43fe 100644 --- a/base/path.jl +++ b/base/path.jl @@ -198,14 +198,6 @@ function splitext(path::String) a*m.captures[1], String(m.captures[2]) end -function pathsep(paths::AbstractString...) - for path in paths - m = match(path_separator_re, String(path)) - m !== nothing && return m.match[1:1] - end - return path_separator -end - """ splitpath(path::AbstractString) -> Vector{String} @@ -246,36 +238,88 @@ function splitpath(p::String) return out end +joinpath(path::AbstractString)::String = path + +if Sys.iswindows() + +function joinpath(path::AbstractString, paths::AbstractString...)::String + result_drive, result_path = splitdrive(path) + + local p_drive, p_path + for p in paths + p_drive, p_path = splitdrive(p) + + if startswith(p_path, ('\\', '/')) + # second path is absolute + if !isempty(p_drive) || !isempty(result_drive) + result_drive = p_drive + end + result_path = p_path + continue + elseif !isempty(p_drive) && p_drive != result_drive + if lowercase(p_drive) != lowercase(result_drive) + # different drives, ignore the first path entirely + result_drive = p_drive + result_path = p_path + continue + end + end + + # second path is relative to the first + if !isempty(result_path) && result_path[end] ∉ ('\\', '/') + result_path *= "\\" + end + + result_path = result_path * p_path + end + + # add separator between UNC and non-absolute path + if !isempty(p_path) && result_path[1] ∉ ('\\', '/') && !isempty(result_drive) && result_drive[end] != ':' + return result_drive * "\\" * result_path + end + + return result_drive * result_path +end + +else + +function joinpath(path::AbstractString, paths::AbstractString...)::String + for p in paths + if isabspath(p) + path = p + elseif isempty(path) || path[end] == '/' + path *= p + else + path *= "/" * p + end + end + return path +end + +end # os-test + """ - joinpath(parts...) -> AbstractString + joinpath(parts::AbstractString...) -> String Join path components into a full path. If some argument is an absolute path or (on Windows) has a drive specification that doesn't match the drive computed for the join of the preceding paths, then prior components are dropped. +Note on Windows since there is a current directory for each drive, `joinpath("c:", "foo")` +represents a path relative to the current directory on drive "c:" so this is equal to "c:foo", +not "c:\\foo". Furthermore, `joinpath` treats this as a non-absolute path and ignores the drive +letter casing, hence `joinpath("C:\\A","c:b") = "C:\\A\\b"`. + # Examples ```jldoctest julia> joinpath("/home/myuser", "example.jl") "/home/myuser/example.jl" ``` """ -joinpath(a::AbstractString, b::AbstractString, c::AbstractString...) = joinpath(joinpath(a,b), c...) -joinpath(a::AbstractString) = a - -function joinpath(a::String, b::String) - isabspath(b) && return b - A, a = splitdrive(a) - B, b = splitdrive(b) - !isempty(B) && A != B && return string(B,b) - C = isempty(B) ? A : B - isempty(a) ? string(C,b) : - occursin(path_separator_re, a[end:end]) ? string(C,a,b) : - string(C,a,pathsep(a,b),b) -end -joinpath(a::AbstractString, b::AbstractString) = joinpath(String(a), String(b)) +joinpath """ - normpath(path::AbstractString) -> AbstractString + normpath(path::AbstractString) -> String Normalize a path, removing "." and ".." entries. @@ -318,10 +362,17 @@ function normpath(path::String) end string(drive,path) end + +""" + normpath(path::AbstractString, paths::AbstractString...) -> String + +Convert a set of paths to a normalized path by joining them together and removing +"." and ".." entries. Equivalent to `normpath(joinpath(path, paths...))`. +""" normpath(a::AbstractString, b::AbstractString...) = normpath(joinpath(a,b...)) """ - abspath(path::AbstractString) -> AbstractString + abspath(path::AbstractString) -> String Convert a path to an absolute path by adding the current directory if necessary. Also normalizes the path as in [`normpath`](@ref). @@ -329,7 +380,7 @@ Also normalizes the path as in [`normpath`](@ref). abspath(a::String) = normpath(isabspath(a) ? a : joinpath(pwd(),a)) """ - abspath(path::AbstractString, paths::AbstractString...) -> AbstractString + abspath(path::AbstractString, paths::AbstractString...) -> String Convert a set of paths to an absolute path by joining them together and adding the current directory if necessary. Equivalent to `abspath(joinpath(path, paths...))`. diff --git a/base/process.jl b/base/process.jl index f9ac44c1bc361..4d1fa0437dfc9 100644 --- a/base/process.jl +++ b/base/process.jl @@ -539,7 +539,7 @@ function pipeline_error(procs::ProcessChain) end """ - kill(p::Process, signum=SIGTERM) + kill(p::Process, signum=Base.SIGTERM) Send a signal to a process. The default is to terminate the process. Returns successfully if the process has already exited, but throws an diff --git a/base/promotion.jl b/base/promotion.jl index e5188755a3244..6436ee3982dcd 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -104,7 +104,9 @@ function typejoin(@nospecialize(a), @nospecialize(b)) if ai === bi || (isa(ai,Type) && isa(bi,Type) && ai <: bi && bi <: ai) aprimary = aprimary{ai} else - pushfirst!(vars, aprimary.var) + # pushfirst!(vars, aprimary.var) + _growbeg!(vars, 1) + arrayset(false, vars, aprimary.var, 1) aprimary = aprimary.body end end diff --git a/base/range.jl b/base/range.jl index 1c6338e6717f4..5877fc8bc2ea0 100644 --- a/base/range.jl +++ b/base/range.jl @@ -1010,14 +1010,14 @@ function _define_range_op(@nospecialize f) function $f(r1::OrdinalRange, r2::OrdinalRange) r1l = length(r1) (r1l == length(r2) || - throw(DimensionMismatch("argument dimensions must match"))) + throw(DimensionMismatch("argument dimensions must match: length of r1 is $r1l, length of r2 is $(length(r2))"))) range($f(first(r1), first(r2)), step=$f(step(r1), step(r2)), length=r1l) end function $f(r1::LinRange{T}, r2::LinRange{T}) where T len = r1.len (len == r2.len || - throw(DimensionMismatch("argument dimensions must match"))) + throw(DimensionMismatch("argument dimensions must match: length of r1 is $len, length of r2 is $(r2.len)"))) LinRange{T}(convert(T, $f(first(r1), first(r2))), convert(T, $f(last(r1), last(r2))), len) end @@ -1033,7 +1033,7 @@ _define_range_op(:-) function +(r1::StepRangeLen{T,S}, r2::StepRangeLen{T,S}) where {T,S} len = length(r1) (len == length(r2) || - throw(DimensionMismatch("argument dimensions must match"))) + throw(DimensionMismatch("argument dimensions must match: length of r1 is $len, length of r2 is $(length(r2))"))) StepRangeLen(first(r1)+first(r2), step(r1)+step(r2), len) end diff --git a/base/rational.jl b/base/rational.jl index 0140cf8bedec0..6e7bb6eb52746 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -299,11 +299,11 @@ for rel in (:<,:<=,:cmp) for (Tx,Ty) in ((Rational,AbstractFloat), (AbstractFloat,Rational)) @eval function ($rel)(x::$Tx, y::$Ty) if isnan(x) - $(rel == :cmp ? :(return isnan(y) ? 0 : 1) : + $(rel === :cmp ? :(return isnan(y) ? 0 : 1) : :(return false)) end if isnan(y) - $(rel == :cmp ? :(return -1) : + $(rel === :cmp ? :(return -1) : :(return false)) end diff --git a/base/reduce.jl b/base/reduce.jl index 4cd8bfcf144c3..eae5da9820b8c 100644 --- a/base/reduce.jl +++ b/base/reduce.jl @@ -36,18 +36,18 @@ mul_prod(x::Real, y::Real)::Real = x * y ## foldl && mapfoldl -function mapfoldl_impl(f, op, nt, itr) +function mapfoldl_impl(f::F, op::OP, nt, itr) where {F,OP} op′, itr′ = _xfadjoint(BottomRF(op), Generator(f, itr)) return foldl_impl(op′, nt, itr′) end -function foldl_impl(op, nt, itr) +function foldl_impl(op::OP, nt, itr) where {OP} v = _foldl_impl(op, get(nt, :init, _InitialValue()), itr) v isa _InitialValue && return reduce_empty_iter(op, itr) return v end -function _foldl_impl(op, init, itr) +function _foldl_impl(op::OP, init, itr) where {OP} # Unroll the while loop once; if init is known, the call to op may # be evaluated at compile time y = iterate(itr) diff --git a/base/reflection.jl b/base/reflection.jl index 813ab0f4bc1ee..a3319ca988d12 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -317,10 +317,10 @@ datatype_fieldtypes(x::DataType) = ccall(:jl_get_fieldtypes, Any, (Any,), x) struct DataTypeLayout nfields::UInt32 + npointers::UInt32 alignment::UInt32 - # alignment : 28; + # alignment : 9; # haspadding : 1; - # pointerfree : 1; # fielddesc_type : 2; end @@ -380,8 +380,8 @@ Can be called on any `isconcretetype`. function datatype_pointerfree(dt::DataType) @_pure_meta dt.layout == C_NULL && throw(UndefRefError()) - alignment = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).alignment - return (alignment >> 10) & 0xFFFFF == 0 + npointers = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).npointers + return npointers == 0 end """ @@ -397,7 +397,7 @@ function datatype_fielddesc_type(dt::DataType) @_pure_meta dt.layout == C_NULL && throw(UndefRefError()) alignment = unsafe_load(convert(Ptr{DataTypeLayout}, dt.layout)).alignment - return (alignment >> 30) & 3 + return (alignment >> 10) & 3 end """ @@ -589,7 +589,9 @@ end Compute a type that contains the intersection of `T` and `S`. Usually this will be the smallest such type or one close to it. """ -typeintersect(@nospecialize(a),@nospecialize(b)) = (@_pure_meta; ccall(:jl_type_intersection, Any, (Any,Any), a, b)) +typeintersect(@nospecialize(a), @nospecialize(b)) = (@_pure_meta; ccall(:jl_type_intersection, Any, (Any, Any), a, b)) + +morespecific(@nospecialize(a), @nospecialize(b)) = ccall(:jl_type_morespecific, Cint, (Any, Any), a, b) != 0 """ fieldoffset(type, i) @@ -792,10 +794,10 @@ Note that an error will be thrown if `types` are not leaf types when `generated` function code_lowered(@nospecialize(f), @nospecialize(t=Tuple); generated::Bool=true, debuginfo::Symbol=:default) if @isdefined(IRShow) debuginfo = IRShow.debuginfo(debuginfo) - elseif debuginfo == :default + elseif debuginfo === :default debuginfo = :source end - if debuginfo != :source && debuginfo != :none + if debuginfo !== :source && debuginfo !== :none throw(ArgumentError("'debuginfo' must be either :source or :none")) end return map(method_instances(f, t)) do m @@ -809,7 +811,7 @@ function code_lowered(@nospecialize(f), @nospecialize(t=Tuple); generated::Bool= end end code = uncompressed_ast(m.def::Method) - debuginfo == :none && remove_linenums!(code) + debuginfo === :none && remove_linenums!(code) return code end end @@ -1079,10 +1081,10 @@ function code_typed(@nospecialize(f), @nospecialize(types=Tuple); end if @isdefined(IRShow) debuginfo = IRShow.debuginfo(debuginfo) - elseif debuginfo == :default + elseif debuginfo === :default debuginfo = :source end - if debuginfo != :source && debuginfo != :none + if debuginfo !== :source && debuginfo !== :none throw(ArgumentError("'debuginfo' must be either :source or :none")) end types = to_tuple_type(types) @@ -1091,7 +1093,7 @@ function code_typed(@nospecialize(f), @nospecialize(types=Tuple); meth = func_for_method_checked(x[3], types, x[2]) (code, ty) = Core.Compiler.typeinf_code(meth, x[1], x[2], optimize, params) code === nothing && error("inference not successful") # inference disabled? - debuginfo == :none && remove_linenums!(code) + debuginfo === :none && remove_linenums!(code) push!(asts, code => ty) end return asts diff --git a/base/regex.jl b/base/regex.jl index 5c1ba8c37e956..0f5074bf026f8 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -643,7 +643,7 @@ function *(r1::Union{Regex,AbstractString,AbstractChar}, rs::Union{Regex,Abstrac shared = mask for r in (r1, rs...) r isa Regex || continue - if match_opts == nothing + if match_opts === nothing match_opts = r.match_options compile_opts = r.compile_options & ~mask else diff --git a/base/ryu/Ryu.jl b/base/ryu/Ryu.jl index 1b0286901c159..62600236b20da 100644 --- a/base/ryu/Ryu.jl +++ b/base/ryu/Ryu.jl @@ -1,6 +1,6 @@ module Ryu -import .Base: significand_bits, exponent_bits, exponent_bias, uinttype +import .Base: significand_bits, significand_mask, exponent_bits, exponent_mask, exponent_bias, exponent_max, uinttype include("utils.jl") include("shortest.jl") @@ -108,12 +108,12 @@ function writeexp(x::T, return String(resize!(buf, pos - 1)) end -function Base.show(io::IO, x::T, forceuntyped::Bool=false) where {T <: Base.IEEEFloat} +function Base.show(io::IO, x::T, forceuntyped::Bool=false, fromprint::Bool=false) where {T <: Base.IEEEFloat} compact = get(io, :compact, false) buf = Base.StringVector(neededdigits(T)) typed = !forceuntyped && !compact && get(io, :typeinfo, Any) != typeof(x) pos = writeshortest(buf, 1, x, false, false, true, -1, - x isa Float32 ? UInt8('f') : UInt8('e'), false, UInt8('.'), typed, compact) + (x isa Float32 && !fromprint) ? UInt8('f') : UInt8('e'), false, UInt8('.'), typed, compact) write(io, resize!(buf, pos - 1)) return end @@ -121,10 +121,10 @@ end function Base.string(x::T) where {T <: Base.IEEEFloat} buf = Base.StringVector(neededdigits(T)) pos = writeshortest(buf, 1, x, false, false, true, -1, - x isa Float32 ? UInt8('f') : UInt8('e'), false, UInt8('.'), false, false) + UInt8('e'), false, UInt8('.'), false, false) return String(resize!(buf, pos - 1)) end -Base.print(io::IO, x::Union{Float16, Float32}) = show(io, x, true) +Base.print(io::IO, x::Union{Float16, Float32}) = show(io, x, true, true) end # module diff --git a/base/ryu/shortest.jl b/base/ryu/shortest.jl index 5b1758ef7ca5b..5066bbae0fe29 100644 --- a/base/ryu/shortest.jl +++ b/base/ryu/shortest.jl @@ -1,6 +1,234 @@ +""" + b, e10 = reduce_shortest(f[, maxsignif]) + +Reduce to shortest decimal representation where `abs(f) == b * 10^e10` and `b` is an +integer. If a `maxsignif` argument is provided, then `b < maxsignif`. +""" +@inline function reduce_shortest(f::T, maxsignif=nothing) where {T} + U = uinttype(T) + uf = reinterpret(U, f) + m = uf & significand_mask(T) + e = Int((uf & exponent_mask(T)) >> significand_bits(T)) + + ## Step 1 + # mf * 2^ef == f + mf = (one(U) << significand_bits(T)) | m + ef = e - exponent_bias(T) - significand_bits(T) + f_isinteger = mf & ((one(U) << -ef) - one(U)) == 0 + + if ef > 0 || ef < -Base.significand_bits(T) || !f_isinteger + # fixup subnormals + if e == 0 + ef = 1 - exponent_bias(T) - significand_bits(T) + mf = m + end + + ## Step 2 + # u * 2^e2 == (f + prevfloat(f))/2 + # v * 2^e2 == f + # w * 2^e2 == (f + nextfloat(f))/2 + e2 = ef - 2 + mf_iseven = iseven(mf) # trailing bit of significand is zero + + v = U(4) * mf + w = v + U(2) + u_shift_half = m == 0 && e > 1 # if first element of binade, other than first normal one + u = v - U(2) + u_shift_half + + ## Step 3 + # a == floor(u * 2^e2 / 10^e10), exact if a_allzero + # b == floor(v * 2^e2 / 10^e10), exact if b_allzero + # c == floor(w * 2^e2 / 10^e10) + a_allzero = false + b_allzero = false + b_lastdigit = 0x00 + if e2 >= 0 + q = log10pow2(e2) - (T == Float64 ? (e2 > 3) : 0) + e10 = q + k = pow5_inv_bitcount(T) + pow5bits(q) - 1 + i = -e2 + q + k + a, b, c = mulshiftinvsplit(T, u, v, w, q, i) + if T == Float32 || T == Float16 + if q != 0 && div(c - 1, 10) <= div(a, 10) + l = pow5_inv_bitcount(T) + pow5bits(q - 1) - 1 + mul = pow5invsplit_lookup(T, q-1) + b_lastdigit = (mulshift(v, mul, -e2 + q - 1 + l) % 10) % UInt8 + end + end + if q <= qinvbound(T) + if ((v % UInt32) - 5 * div(v, 5)) == 0 + b_allzero = pow5(v, q) + elseif mf_iseven + a_allzero = pow5(u, q) + else + c -= pow5(w, q) + end + end + else + q = log10pow5(-e2) - (T == Float64 ? (-e2 > 1) : 0) + e10 = q + e2 + i = -e2 - q + k = pow5bits(i) - pow5_bitcount(T) + j = q - k + a, b, c = mulshiftsplit(T, u, v, w, i, j) + if T == Float32 || T == Float16 + if q != 0 && div(c - 1, 10) <= div(a, 10) + j = q - 1 - (pow5bits(i + 1) - pow5_bitcount(T)) + mul = pow5split_lookup(T, i+1) + b_lastdigit = (mulshift(v, mul, j) % 10) % UInt8 + end + end + if q <= 1 + b_allzero = true + if mf_iseven + a_allzero = !u_shift_half + else + c -= 1 + end + elseif q < qbound(T) + b_allzero = pow2(v, q - (T != Float64)) + end + end + + ## Step 4: reduction + if a_allzero || b_allzero + # a) slow loop + while true + c_div10 = div(c, 10) + a_div10 = div(a, 10) + if c_div10 <= a_div10 + break + end + a_mod10 = (a % UInt32) - UInt32(10) * (a_div10 % UInt32) + b_div10 = div(b, 10) + b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + a_allzero &= a_mod10 == 0 + b_allzero &= b_lastdigit == 0 + b_lastdigit = b_mod10 % UInt8 + b = b_div10 + c = c_div10 + a = a_div10 + e10 += 1 + end + if a_allzero + while true + a_div10 = div(a, 10) + a_mod10 = (a % UInt32) - UInt32(10) * (a_div10 % UInt32) + if a_mod10 != 0 && (maxsignif === nothing || b < maxsignif) + break + end + c_div10 = div(c, 10) + b_div10 = div(b, 10) + b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + b_allzero &= b_lastdigit == 0 + b_lastdigit = b_mod10 % UInt8 + b = b_div10 + c = c_div10 + a = a_div10 + e10 += 1 + end + end + if b_allzero && b_lastdigit == 5 && iseven(b) + b_lastdigit = UInt8(4) + end + roundup = (b == a && (!mf_iseven || !a_allzero)) || b_lastdigit >= 5 + else + # b) specialized for common case (99% Float64, 96% Float32) + roundup = b_lastdigit >= 5 + c_div100 = div(c, 100) + a_div100 = div(a, 100) + if c_div100 > a_div100 + b_div100 = div(b, 100) + b_mod100 = (b % UInt32) - UInt32(100) * (b_div100 % UInt32) + roundup = b_mod100 >= 50 + b = b_div100 + c = c_div100 + a = a_div100 + e10 += 2 + end + while true + c_div10 = div(c, 10) + a_div10 = div(a, 10) + if c_div10 <= a_div10 + break + end + b_div10 = div(b, 10) + b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + roundup = b_mod10 >= 5 + b = b_div10 + c = c_div10 + a = a_div10 + e10 += 1 + end + roundup = (b == a || roundup) + end + if maxsignif !== nothing && b > maxsignif + # reduce to max significant digits + while true + b_div10 = div(b, 10) + b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + if b <= maxsignif + break + end + b = b_div10 + roundup = (b_allzero && iseven(b)) ? b_mod10 > 5 : b_mod10 >= 5 + b_allzero &= b_mod10 == 0 + e10 += 1 + end + b = b + roundup + + # remove trailing zeros + while true + b_div10 = div(b, 10) + b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + if b_mod10 != 0 + break + end + b = b_div10 + e10 += 1 + end + else + b = b + roundup + end + else + # c) specialized f an integer < 2^53 + b = mf >> -ef + e10 = 0 + + if maxsignif !== nothing && b > maxsignif + b_allzero = true + # reduce to max significant digits + while true + b_div10 = div(b, 10) + b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + if b <= maxsignif + break + end + b = b_div10 + roundup = (b_allzero && iseven(b)) ? b_mod10 > 5 : b_mod10 >= 5 + b_allzero &= b_mod10 == 0 + e10 += 1 + end + b = b + roundup + end + while true + b_div10 = div(b, 10) + b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + if b_mod10 != 0 + break + end + b = b_div10 + e10 += 1 + end + end + return b, e10 +end + + @inline function writeshortest(buf::Vector{UInt8}, pos, x::T, - plus=false, space=false, hash=true, - precision=-1, expchar=UInt8('e'), padexp=false, decchar=UInt8('.'), typed=false, compact=false) where {T} + plus=false, space=false, hash=true, + precision=-1, expchar=UInt8('e'), padexp=false, decchar=UInt8('.'), + typed=false, compact=false) where {T} @assert 0 < pos <= length(buf) neg = signbit(x) # special cases @@ -75,150 +303,7 @@ return pos + neg + 3 + (typed && x isa Union{Float32, Float16} ? 2 : 0) end - bits = reinterpret(Unsigned, x) - mant = bits & (oftype(bits, 1) << significand_bits(T) - oftype(bits, 1)) - exp = Int((bits >> significand_bits(T)) & ((Int64(1) << exponent_bits(T)) - 1)) - m2 = oftype(bits, Int64(1) << significand_bits(T)) | mant - e2 = exp - exponent_bias(T) - significand_bits(T) - fraction = m2 & ((oftype(bits, 1) << -e2) - 1) - if e2 > 0 || e2 < -52 || fraction != 0 - if exp == 0 - e2 = 1 - exponent_bias(T) - significand_bits(T) - 2 - m2 = mant - else - e2 -= 2 - end - even = (m2 & 1) == 0 - mv = oftype(m2, 4 * m2) - mp = oftype(m2, mv + 2) - mmShift = mant != 0 || exp <= 1 - mm = oftype(m2, mv - 1 - mmShift) - vmIsTrailingZeros = false - vrIsTrailingZeros = false - lastRemovedDigit = 0x00 - if e2 >= 0 - q = log10pow2(e2) - (T == Float64 ? (e2 > 3) : 0) - e10 = q - k = pow5_inv_bitcount(T) + pow5bits(q) - 1 - i = -e2 + q + k - vr, vp, vm = mulshiftinvsplit(T, mv, mp, mm, q, i) - if T == Float32 || T == Float16 - if q != 0 && div(vp - 1, 10) <= div(vm, 10) - l = pow5_inv_bitcount(T) + pow5bits(q - 1) - 1 - mul = T == Float32 ? FLOAT_POW5_INV_SPLIT[q] : HALF_POW5_INV_SPLIT[q] - lastRemovedDigit = (mulshift(mv, mul, -e2 + q - 1 + l) % 10) % UInt8 - end - end - if q <= qinvbound(T) - if ((mv % UInt32) - 5 * div(mv, 5)) == 0 - vrIsTrailingZeros = pow5(mv, q) - elseif even - vmIsTrailingZeros = pow5(mm, q) - else - vp -= pow5(mp, q) - end - end - else - q = log10pow5(-e2) - (T == Float64 ? (-e2 > 1) : 0) - e10 = q + e2 - i = -e2 - q - k = pow5bits(i) - pow5_bitcount(T) - j = q - k - vr, vp, vm = mulshiftsplit(T, mv, mp, mm, i, j) - if T == Float32 || T == Float16 - if q != 0 && div(vp - 1, 10) <= div(vm, 10) - j = q - 1 - (pow5bits(i + 1) - pow5_bitcount(T)) - mul = T == Float32 ? FLOAT_POW5_SPLIT[i + 2] : HALF_POW5_SPLIT[i + 2] - lastRemovedDigit = (mulshift(mv, mul, j) % 10) % UInt8 - end - end - if q <= 1 - vrIsTrailingZeros = true - if even - vmIsTrailingZeros = mmShift - else - vp -= 1 - end - elseif q < qbound(T) - vrIsTrailingZeros = pow2(mv, q - (T != Float64)) - end - end - removed = 0 - if vmIsTrailingZeros || vrIsTrailingZeros - while true - vpDiv10 = div(vp, 10) - vmDiv10 = div(vm, 10) - vpDiv10 <= vmDiv10 && break - vmMod10 = (vm % UInt32) - UInt32(10) * (vmDiv10 % UInt32) - vrDiv10 = div(vr, 10) - vrMod10 = (vr % UInt32) - UInt32(10) * (vrDiv10 % UInt32) - vmIsTrailingZeros &= vmMod10 == 0 - vrIsTrailingZeros &= lastRemovedDigit == 0 - lastRemovedDigit = vrMod10 % UInt8 - vr = vrDiv10 - vp = vpDiv10 - vm = vmDiv10 - removed += 1 - end - if vmIsTrailingZeros - while true - vmDiv10 = div(vm, 10) - vmMod10 = (vm % UInt32) - UInt32(10) * (vmDiv10 % UInt32) - vmMod10 != 0 && break - vpDiv10 = div(vp, 10) - vrDiv10 = div(vr, 10) - vrMod10 = (vr % UInt32) - UInt32(10) * (vrDiv10 % UInt32) - vrIsTrailingZeros &= lastRemovedDigit == 0 - lastRemovedDigit = vrMod10 % UInt8 - vr = vrDiv10 - vp = vpDiv10 - vm = vmDiv10 - removed += 1 - end - end - if vrIsTrailingZeros && lastRemovedDigit == 5 && vr % 2 == 0 - lastRemovedDigit = UInt8(4) - end - output = vr + ((vr == vm && (!even || !vmIsTrailingZeros)) || lastRemovedDigit >= 5) - else - roundUp = false - vpDiv100 = div(vp, 100) - vmDiv100 = div(vm, 100) - if vpDiv100 > vmDiv100 - vrDiv100 = div(vr, 100) - vrMod100 = (vr % UInt32) - UInt32(100) * (vrDiv100 % UInt32) - roundUp = vrMod100 >= 50 - vr = vrDiv100 - vp = vpDiv100 - vm = vmDiv100 - removed += 2 - end - while true - vpDiv10 = div(vp, 10) - vmDiv10 = div(vm, 10) - vpDiv10 <= vmDiv10 && break - vrDiv10 = div(vr, 10) - vrMod10 = (vr % UInt32) - UInt32(10) * (vrDiv10 % UInt32) - roundUp = vrMod10 >= 5 - vr = vrDiv10 - vp = vpDiv10 - vm = vmDiv10 - removed += 1 - end - output = vr + (vr == vm || roundUp || lastRemovedDigit >= 5) - end - nexp = e10 + removed - else - output = m2 >> -e2 - nexp = 0 - while true - q = div(output, 10) - r = (output % UInt32) - UInt32(10) * (q % UInt32) - r != 0 && break - output = q - nexp += 1 - end - end + output, nexp = reduce_shortest(x, compact ? 999_999 : nothing) if typed && x isa Float16 buf[pos] = UInt8('F') @@ -242,40 +327,10 @@ pos += 1 end - if compact && output > 999999 - lastdigit = output % 10 - while true - output = div(output, 10) - nexp += nexp != 0 - output > 999999 || break - lastdigit = output % 10 - end - if lastdigit == 9 - output += 1 - lastdigit = 0 - end - if lastdigit == 9 - while true - output = div(output, 10) - nexp += nexp != 0 - output % 10 == 9 || break - end - output += 1 - elseif output % 10 == 0 - while true - output = div(output, 10) - nexp += nexp != 0 - output % 10 == 0 || break - end - else - output += lastdigit > 4 - end - end - olength = decimallength(output) exp_form = true pt = nexp + olength - if -4 < pt <= (precision == -1 ? (T == Float16 ? 5 : 6) : precision) + if -4 < pt <= (precision == -1 ? (T == Float16 ? 3 : 6) : precision) exp_form = false if pt <= 0 buf[pos] = UInt8('0') @@ -286,9 +341,9 @@ buf[pos] = UInt8('0') pos += 1 end - # elseif pt >= olength + # elseif pt >= olength # nothing to do at this point - # else + # else # nothing to do at this point end else diff --git a/base/ryu/utils.jl b/base/ryu/utils.jl index c0bab2cb57f65..46a88c0ef9d9f 100644 --- a/base/ryu/utils.jl +++ b/base/ryu/utils.jl @@ -4,6 +4,8 @@ const EXP_MASK = Base.exponent_mask(Float64) >> Base.significand_bits(Float64) memcpy(d, doff, s, soff, n) = ccall(:memcpy, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}, Int), d + doff - 1, s + soff - 1, n) memmove(d, doff, s, soff, n) = ccall(:memmove, Cvoid, (Ptr{Cvoid}, Ptr{Cvoid}, Int), d + doff - 1, s + soff - 1, n) +# Note: these are smaller than the values given in Figure 4 from the paper +# see https://github.com/ulfjack/ryu/issues/119 pow5_bitcount(::Type{Float16}) = 30 pow5_bitcount(::Type{Float32}) = 61 pow5_bitcount(::Type{Float64}) = 121 @@ -123,48 +125,16 @@ end return 1 end -@inline function mulshiftinvsplit(::Type{Float64}, mv, mp, mm, i, j) - @inbounds mul = DOUBLE_POW5_INV_SPLIT[i + 1] +@inline function mulshiftinvsplit(::Type{T}, mv, mp, mm, i, j) where {T} + mul = pow5invsplit_lookup(T, i) vr = mulshift(mv, mul, j) vp = mulshift(mp, mul, j) vm = mulshift(mm, mul, j) return vr, vp, vm end -@inline function mulshiftinvsplit(::Type{Float32}, mv, mp, mm, i, j) - @inbounds mul = FLOAT_POW5_INV_SPLIT[i + 1] - vr = mulshift(mv, mul, j) - vp = mulshift(mp, mul, j) - vm = mulshift(mm, mul, j) - return vr, vp, vm -end - -@inline function mulshiftinvsplit(::Type{Float16}, mv, mp, mm, i, j) - @inbounds mul = HALF_POW5_INV_SPLIT[i + 1] - vr = mulshift(mv, mul, j) - vp = mulshift(mp, mul, j) - vm = mulshift(mm, mul, j) - return vr, vp, vm -end - -@inline function mulshiftsplit(::Type{Float64}, mv, mp, mm, i, j) - @inbounds mul = DOUBLE_POW5_SPLIT[i + 1] - vr = mulshift(mv, mul, j) - vp = mulshift(mp, mul, j) - vm = mulshift(mm, mul, j) - return vr, vp, vm -end - -@inline function mulshiftsplit(::Type{Float32}, mv, mp, mm, i, j) - @inbounds mul = FLOAT_POW5_SPLIT[i + 1] - vr = mulshift(mv, mul, j) - vp = mulshift(mp, mul, j) - vm = mulshift(mm, mul, j) - return vr, vp, vm -end - -@inline function mulshiftsplit(::Type{Float16}, mv, mp, mm, i, j) - @inbounds mul = HALF_POW5_SPLIT[i + 1] +@inline function mulshiftsplit(::Type{T}, mv, mp, mm, i, j) where {T} + mul = pow5split_lookup(T, i) vr = mulshift(mv, mul, j) vp = mulshift(mp, mul, j) vm = mulshift(mm, mul, j) @@ -376,6 +346,20 @@ function pow5invsplit(::Type{T}, i) where {T<:AbstractFloat} return W(inv) end +""" + Ryu.pow5invsplit_lookup(T, i) + +[`pow5invsplit`](@ref) computed via lookup table. +""" +function pow5invsplit_lookup end +for T in (Float64, Float32, Float16) + e2_max = exponent_max(T) - precision(T) - 2 + i_max = log10pow2(e2_max) + table = [pow5invsplit(T, i) for i = 0:i_max] + @eval pow5invsplit_lookup(::Type{$T}, i) = @inbounds($table[i+1]) +end + + """ Ryu.pow5split(T, i) @@ -389,13 +373,18 @@ function pow5split(::Type{T}, i) where {T<:AbstractFloat} return W(pow >> (ndigits(pow, base=2) - pow5_bitcount(T))) end -const DOUBLE_POW5_INV_SPLIT = map(i->pow5invsplit(Float64, i), 0:291) -const FLOAT_POW5_INV_SPLIT = map(i->pow5invsplit(Float32, i), 0:30) -const HALF_POW5_INV_SPLIT = map(i->pow5invsplit(Float16, i), 0:17) +""" + Ryu.pow5split_lookup(T, i) -const DOUBLE_POW5_SPLIT = map(i->pow5split(Float64, i), 0:325) -const FLOAT_POW5_SPLIT = map(i->pow5split(Float32, i), 0:46) -const HALF_POW5_SPLIT = map(i->pow5split(Float16, i), 0:23) +[`pow5split`](@ref) computed via lookup table. +""" +function pow5split_lookup end +for T in (Float64, Float32, Float16) + e2_min = 1 - exponent_bias(T) - significand_bits(T) - 2 + i_max = 1 - e2_min - log10pow5(-e2_min) + table = [pow5split(T, i) for i = 0:i_max] + @eval pow5split_lookup(::Type{$T}, i) = @inbounds($table[i+1]) +end const DIGIT_TABLE = UInt8[ '0','0','0','1','0','2','0','3','0','4','0','5','0','6','0','7','0','8','0','9', diff --git a/base/set.jl b/base/set.jl index d4f91ee27d36f..9f98aa0133ee6 100644 --- a/base/set.jl +++ b/base/set.jl @@ -373,7 +373,7 @@ function allunique(C) true end -allunique(::Set) = true +allunique(::Union{AbstractSet,AbstractDict}) = true allunique(r::AbstractRange{T}) where {T} = (step(r) != zero(T)) || (length(r) <= 1) allunique(r::StepRange{T,S}) where {T,S} = (step(r) != zero(S)) || (length(r) <= 1) @@ -439,7 +439,10 @@ julia> replace!([1, 2, 1, 3], 1=>0, 2=>4, count=2) 3 julia> replace!(Set([1, 2, 3]), 1=>0) -Set([0, 2, 3]) +Set{Int64} with 3 elements: + 0 + 2 + 3 ``` """ replace!(A, old_new::Pair...; count::Integer=typemax(Int)) = @@ -479,7 +482,9 @@ Dict{Int64,Int64} with 2 entries: 1 => 3 julia> replace!(x->2x, Set([3, 6])) -Set([6, 12]) +Set{Int64} with 2 elements: + 6 + 12 ``` """ replace!(new::Callable, A; count::Integer=typemax(Int)) = diff --git a/base/shell.jl b/base/shell.jl index 099aefbb26ee0..85243a3e40542 100644 --- a/base/shell.jl +++ b/base/shell.jl @@ -253,3 +253,62 @@ julia> Base.shell_escape_posixly("echo", "this", "&&", "that") """ shell_escape_posixly(args::AbstractString...) = sprint(print_shell_escaped_posixly, args...) + + +function print_shell_escaped_winsomely(io::IO, args::AbstractString...) + first = true + for arg in args + first || write(io, ' ') + first = false + # Quote any arg that contains a whitespace (' ' or '\t') or a double quote mark '"'. + # It's also valid to quote an arg with just a whitespace, + # but the following may be 'safer', and both implementations are valid anyways. + quotes = any(c -> c in (' ', '\t', '"'), arg) || isempty(arg) + quotes && write(io, '"') + backslashes = 0 + for c in arg + if c == '\\' + backslashes += 1 + else + # escape all backslashes and the following double quote + c == '"' && (backslashes = backslashes * 2 + 1) + for j = 1:backslashes + # backslashes aren't special here + write(io, '\\') + end + backslashes = 0 + write(io, c) + end + end + # escape all backslashes, letting the terminating double quote we add below to then be interpreted as a special char + quotes && (backslashes *= 2) + for j = 1:backslashes + write(io, '\\') + end + quotes && write(io, '"') + end + return nothing +end + + +""" + shell_escaped_winsomely(args::Union{Cmd,AbstractString...})::String + +Convert the collection of strings `args` into single string suitable for passing as the argument +string for a Windows command line. Windows passes the entire command line as a single string to +the application (unlike POSIX systems, where the list of arguments are passed separately). +Many Windows API applications (including julia.exe), use the conventions of the [Microsoft C +runtime](https://docs.microsoft.com/en-us/cpp/c-language/parsing-c-command-line-arguments) to +split that command line into a list of strings. This function implements the inverse of such a +C runtime command-line parser. It joins command-line arguments to be passed to a Windows console +application into a command line, escaping or quoting meta characters such as space, +double quotes and backslash where needed. This may be useful in concert with the `windows_verbatim` +flag to [`Cmd`](@ref) when constructing process pipelines. + +# Example +```jldoctest +julia> println(shell_escaped_winsomely("A B\\", "C")) +"A B\\" C +""" +shell_escape_winsomely(args::AbstractString...) = + sprint(print_shell_escaped_winsomely, args..., sizehint=(sum(length, args)) + 3*length(args)) diff --git a/base/show.jl b/base/show.jl index 22ff592ace180..3ed3737d70656 100644 --- a/base/show.jl +++ b/base/show.jl @@ -113,7 +113,10 @@ function show(io::IO, ::MIME"text/plain", t::AbstractDict{K,V}) where {K,V} for (i, (k, v)) in enumerate(t) print(io, "\n ") - i == rows < length(t) && (print(io, rpad("⋮", keylen), " => ⋮"); break) + if i == rows < length(t) + print(io, rpad("⋮", keylen), " => ⋮") + break + end if limit key = rpad(_truncate_at_width_or_chars(ks[i], keylen, "\r\n"), keylen) @@ -132,6 +135,48 @@ function show(io::IO, ::MIME"text/plain", t::AbstractDict{K,V}) where {K,V} end end +function summary(io::IO, t::AbstractSet) + n = length(t) + showarg(io, t, true) + print(io, " with ", n, (n==1 ? " element" : " elements")) +end + +function show(io::IO, ::MIME"text/plain", t::AbstractSet{T}) where T + # show more descriptively, with one line per value + recur_io = IOContext(io, :SHOWN_SET => t) + limit::Bool = get(io, :limit, false) + + summary(io, t) + isempty(t) && return + print(io, ":") + show_circular(io, t) && return + if limit + sz = displaysize(io) + rows, cols = sz[1] - 3, sz[2] + rows < 2 && (print(io, " …"); return) + cols -= 2 # Subtract the width of prefix " " + cols < 4 && (cols = 4) # Minimum widths of 4 for value + rows -= 1 # Subtract the summary + else + rows = cols = typemax(Int) + end + + for (i, v) in enumerate(t) + print(io, "\n ") + if i == rows < length(t) + print(io, rpad("⋮", 2)) + break + end + + if limit + str = sprint(show, v, context=recur_io, sizehint=0) + print(io, _truncate_at_width_or_chars(str, cols, "\r\n")) + else + show(recur_io, v) + end + end +end + function show(io::IO, ::MIME"text/plain", opt::JLOptions) println(io, "JLOptions(") fields = fieldnames(JLOptions) @@ -150,7 +195,7 @@ end function show(io::IO, ::MIME"text/plain", t::Task) show(io, t) - if t.state == :failed + if t.state === :failed println(io) showerror(io, CapturedException(t.result, t.backtrace)) end @@ -332,8 +377,8 @@ show(io::IO, @nospecialize(x)) = show_default(io, x) show_default(io::IO, @nospecialize(x)) = _show_default(io, inferencebarrier(x)) function _show_default(io::IO, @nospecialize(x)) - t = typeof(x)::DataType - show(io, t) + t = typeof(x) + show(io, inferencebarrier(t)) print(io, '(') nf = nfields(x) nb = sizeof(x) @@ -450,7 +495,7 @@ function show(io::IO, @nospecialize(x::Type)) return show(io, unwrap_unionall(x).name) end - if x.var.name == :_ || io_has_tvar_name(io, x.var.name, x) + if x.var.name === :_ || io_has_tvar_name(io, x.var.name, x) counter = 1 while true newname = Symbol(x.var.name, counter) @@ -721,7 +766,7 @@ end show(io::IO, t::Tuple) = show_delim_array(io, t, '(', ',', ')', true) show(io::IO, v::SimpleVector) = show_delim_array(io, v, "svec(", ',', ')', false) -show(io::IO, s::Symbol) = show_unquoted_quote_expr(io, s, 0, 0) +show(io::IO, s::Symbol) = show_unquoted_quote_expr(io, s, 0, 0, 0) ## Abstract Syntax Tree (AST) printing ## @@ -752,12 +797,19 @@ const ExprNode = Union{Expr, QuoteNode, Slot, LineNumberNode, SSAValue, Core.Compiler.GotoIfNot, Core.Compiler.ReturnNode} # Operators have precedence levels from 1-N, and show_unquoted defaults to a # precedence level of 0 (the fourth argument). The top-level print and show -# methods use a precedence of -1 to specially allow space-separated macro syntax -print( io::IO, ex::ExprNode) = (show_unquoted(io, ex, 0, -1); nothing) -show( io::IO, ex::ExprNode) = show_unquoted_quote_expr(io, ex, 0, -1) +# methods use a precedence of -1 to specially allow space-separated macro syntax. +# IOContext(io, :unquote_fallback => false) tells show_unquoted to treat any +# Expr whose head is :$ as if it is inside a quote, preventing fallback to the +# "unhandled" case: this is used by print/string to be lawful to Rule 1 above. +# On the countrary, show/repr have to follow Rule 2, requiring any Expr whose +# head is :$ and which is not inside a quote to fallback to the "unhandled" case: +# this is behavior is triggered by IOContext(io, :unquote_fallback => true) +print( io::IO, ex::ExprNode) = (show_unquoted(IOContext(io, :unquote_fallback => false), ex, 0, -1); nothing) +show( io::IO, ex::ExprNode) = show_unquoted_quote_expr(IOContext(io, :unquote_fallback => true), ex, 0, -1, 0) show_unquoted(io::IO, ex) = show_unquoted(io, ex, 0, 0) show_unquoted(io::IO, ex, indent::Int) = show_unquoted(io, ex, indent, 0) show_unquoted(io::IO, ex, ::Int,::Int) = show(io, ex) +show_unquoted(io::IO, ex, indent::Int, prec::Int, ::Int) = show_unquoted(io, ex, indent, prec) ## AST printing constants ## @@ -768,7 +820,7 @@ const uni_ops = Set{Symbol}([:(+), :(-), :(!), :(¬), :(~), :(<:), :(>:), :(√) const expr_infix_wide = Set{Symbol}([ :(=), :(+=), :(-=), :(*=), :(/=), :(\=), :(^=), :(&=), :(|=), :(÷=), :(%=), :(>>>=), :(>>=), :(<<=), :(.=), :(.+=), :(.-=), :(.*=), :(./=), :(.\=), :(.^=), :(.&=), :(.|=), :(.÷=), :(.%=), :(.>>>=), :(.>>=), :(.<<=), - :(&&), :(||), :(<:), :($=), :(⊻=)]) + :(&&), :(||), :(<:), :($=), :(⊻=), :(>:)]) const expr_infix = Set{Symbol}([:(:), :(->), Symbol("::")]) const expr_infix_any = union(expr_infix, expr_infix_wide) const expr_calls = Dict(:call => ('(',')'), :calldecl => ('(',')'), @@ -783,6 +835,7 @@ is_id_start_char(c::AbstractChar) = ccall(:jl_id_start_char, Cint, (UInt32,), c) is_id_char(c::AbstractChar) = ccall(:jl_id_char, Cint, (UInt32,), c) != 0 function isidentifier(s::AbstractString) isempty(s) && return false + (s == "true" || s == "false") && return false c, rest = Iterators.peel(s) is_id_start_char(c) || return false return all(is_id_char, rest) @@ -909,36 +962,36 @@ show_linenumber(io::IO, line, file) = print(io, "#= ", file, ":", line, " =#") show_linenumber(io::IO, line, file::Nothing) = show_linenumber(io, line) # show a block, e g if/for/etc -function show_block(io::IO, head, args::Vector, body, indent::Int) +function show_block(io::IO, head, args::Vector, body, indent::Int, quote_level::Int) print(io, head) if !isempty(args) print(io, ' ') if head === :elseif - show_list(io, args, " ", indent) + show_list(io, args, " ", indent, 0, quote_level) else - show_list(io, args, ", ", indent) + show_list(io, args, ", ", indent, 0, quote_level) end end ind = head === :module || head === :baremodule ? indent : indent + indent_width - exs = is_expr(body, :block) ? body.args : Any[body] + exs = (is_expr(body, :block) || is_expr(body, :quote)) ? body.args : Any[body] for ex in exs print(io, '\n', " "^ind) - show_unquoted(io, ex, ind, -1) + show_unquoted(io, ex, ind, -1, quote_level) end print(io, '\n', " "^indent) end -show_block(io::IO,head, block,i::Int) = show_block(io,head, [], block,i) -function show_block(io::IO, head, arg, block, i::Int) - if is_expr(arg, :block) - show_block(io, head, arg.args, block, i) +show_block(io::IO,head, block,i::Int, quote_level::Int) = show_block(io,head, [], block,i, quote_level) +function show_block(io::IO, head, arg, block, i::Int, quote_level::Int) + if is_expr(arg, :block) || is_expr(arg, :quote) + show_block(io, head, arg.args, block, i, quote_level) else - show_block(io, head, Any[arg], block, i) + show_block(io, head, Any[arg], block, i, quote_level) end end # show an indented list -function show_list(io::IO, items, sep, indent::Int, prec::Int=0, enclose_operators::Bool=false) +function show_list(io::IO, items, sep, indent::Int, prec::Int=0, quote_level::Int=0, enclose_operators::Bool=false) n = length(items) n == 0 && return indent += indent_width @@ -951,41 +1004,41 @@ function show_list(io::IO, items, sep, indent::Int, prec::Int=0, enclose_operato (item isa Real && item < 0))) || (enclose_operators && item isa Symbol && isoperator(item)) parens && print(io, '(') - show_unquoted(io, item, indent, parens ? 0 : prec) + show_unquoted(io, item, indent, parens ? 0 : prec, quote_level) parens && print(io, ')') first = false end end # show an indented list inside the parens (op, cl) -function show_enclosed_list(io::IO, op, items, sep, cl, indent, prec=0, encl_ops=false) +function show_enclosed_list(io::IO, op, items, sep, cl, indent, prec=0, quote_level=0, encl_ops=false) print(io, op) - show_list(io, items, sep, indent, prec, encl_ops) + show_list(io, items, sep, indent, prec, quote_level, encl_ops) print(io, cl) end # show a normal (non-operator) function call, e.g. f(x, y) or A[z] -function show_call(io::IO, head, func, func_args, indent) +function show_call(io::IO, head, func, func_args, indent, quote_level) op, cl = expr_calls[head] if (isa(func, Symbol) && func !== :(:) && !(head === :. && isoperator(func))) || - (isa(func, Expr) && (func.head == :. || func.head == :curly || func.head == :macroname)) || + (isa(func, Expr) && (func.head === :. || func.head === :curly || func.head === :macroname)) || isa(func, GlobalRef) - show_unquoted(io, func, indent) + show_unquoted(io, func, indent, 0, quote_level) else print(io, '(') - show_unquoted(io, func, indent) + show_unquoted(io, func, indent, 0, quote_level) print(io, ')') end - if head == :(.) + if head === :(.) print(io, '.') end if !isempty(func_args) && isa(func_args[1], Expr) && func_args[1].head === :parameters print(io, op) - show_list(io, func_args[2:end], ", ", indent) + show_list(io, func_args[2:end], ", ", indent, 0, quote_level) print(io, "; ") - show_list(io, func_args[1].args, ", ", indent) + show_list(io, func_args[1].args, ", ", indent, 0, quote_level) print(io, cl) else - show_enclosed_list(io, op, func_args, ", ", cl, indent) + show_enclosed_list(io, op, func_args, ", ", cl, indent, 0, quote_level) end end @@ -993,7 +1046,7 @@ end # * Print valid identifiers & operators literally; also macros names if allow_macroname=true # * Escape invalid identifiers with var"" syntax function show_sym(io::IO, sym; allow_macroname=false) - if isidentifier(sym) || isoperator(sym) + if isidentifier(sym) || (isoperator(sym) && sym !== Symbol("'")) print(io, sym) elseif allow_macroname && (sym_str = string(sym); startswith(sym_str, '@')) print(io, '@') @@ -1006,7 +1059,7 @@ end ## AST printing ## show_unquoted(io::IO, val::SSAValue, ::Int, ::Int) = print(io, "%", val.id) -show_unquoted(io::IO, sym::Symbol, ::Int, ::Int) = show_sym(io, sym) +show_unquoted(io::IO, sym::Symbol, ::Int, ::Int) = show_sym(io, sym, allow_macroname=false) show_unquoted(io::IO, ex::LineNumberNode, ::Int, ::Int) = show_linenumber(io, ex.line, ex.file) show_unquoted(io::IO, ex::GotoNode, ::Int, ::Int) = print(io, "goto %", ex.label) function show_unquoted(io::IO, ex::GlobalRef, ::Int, ::Int) @@ -1038,18 +1091,21 @@ end function show_unquoted(io::IO, ex::QuoteNode, indent::Int, prec::Int) if isa(ex.value, Symbol) - show_unquoted_quote_expr(io, ex.value, indent, prec) + show_unquoted_quote_expr(io, ex.value, indent, prec, 0) else print(io, "\$(QuoteNode(") + # QuoteNode does not allows for interpolation, so if ex.value is an + # Expr it should be shown with quote_level equal to zero. + # Calling show(io, ex.value) like this implicitly enforce that. show(io, ex.value) print(io, "))") end end -function show_unquoted_quote_expr(io::IO, @nospecialize(value), indent::Int, prec::Int) +function show_unquoted_quote_expr(io::IO, @nospecialize(value), indent::Int, prec::Int, quote_level::Int) if isa(value, Symbol) && !(value in quoted_syms) s = string(value) - if isidentifier(s) || isoperator(value) + if isidentifier(s) || (isoperator(value) && value !== Symbol("'")) print(io, ":") print(io, value) else @@ -1057,17 +1113,17 @@ function show_unquoted_quote_expr(io::IO, @nospecialize(value), indent::Int, pre end else if isa(value,Expr) && value.head === :block - show_block(io, "quote", value, indent) + show_block(io, "quote", value, indent, quote_level) print(io, "end") else print(io, ":(") - show_unquoted(io, value, indent+2, -1) # +2 for `:(` + show_unquoted(io, value, indent+2, -1, quote_level) # +2 for `:(` print(io, ")") end end end -function show_generator(io, ex, indent) +function show_generator(io, ex, indent, quote_level) if ex.head === :flatten fg = ex ranges = Any[] @@ -1076,52 +1132,64 @@ function show_generator(io, ex, indent) fg = fg.args[1].args[1] end push!(ranges, fg.args[2:end]) - show_unquoted(io, fg.args[1], indent) + show_unquoted(io, fg.args[1], indent, 0, quote_level) for r in ranges print(io, " for ") - show_list(io, r, ", ", indent) + show_list(io, r, ", ", indent, 0, quote_level) end else - show_unquoted(io, ex.args[1], indent) + show_unquoted(io, ex.args[1], indent, 0, quote_level) print(io, " for ") - show_list(io, ex.args[2:end], ", ", indent) + show_list(io, ex.args[2:end], ", ", indent, 0, quote_level) end end function valid_import_path(@nospecialize ex) - return Meta.isexpr(ex, :(.)) && length(ex.args) > 0 && all(a->isa(a,Symbol), ex.args) + return Meta.isexpr(ex, :(.)) && length((ex::Expr).args) > 0 && all(a->isa(a,Symbol), (ex::Expr).args) end -function show_import_path(io::IO, ex) +function show_import_path(io::IO, ex, quote_level) if !isa(ex, Expr) show_unquoted(io, ex) elseif ex.head === :(:) - show_import_path(io, ex.args[1]) + show_import_path(io, ex.args[1], quote_level) print(io, ": ") for i = 2:length(ex.args) if i > 2 print(io, ", ") end - show_import_path(io, ex.args[i]) + show_import_path(io, ex.args[i], quote_level) end elseif ex.head === :(.) for i = 1:length(ex.args) - if i > 1 && ex.args[i-1] != :(.) + if i > 1 && ex.args[i-1] !== :(.) print(io, '.') end show_sym(io, ex.args[i], allow_macroname=(i==length(ex.args))) end else - show_unquoted(io, ex) + show_unquoted(io, ex, 0, 0, quote_level) end end # Wrap symbols for macro names to allow them to be printed literally -allow_macroname(ex) = ex isa Symbol && first(string(ex)) == '@' ? - Expr(:macroname, ex) : ex +function allow_macroname(ex) + if (ex isa Symbol && first(string(ex)) == '@') || + (is_expr(ex, :(.)) && length(ex.args) == 2 && + (ex.args[1] isa Symbol || ex.args[1] isa Module) && + (is_expr(ex.args[2], :quote) || ex.args[2] isa QuoteNode)) + return Expr(:macroname, ex) + else + ex + end +end + +function is_core_macro(arg, macro_name::AbstractString) + arg === GlobalRef(Core, Symbol(macro_name)) +end # TODO: implement interpolated strings -function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) +function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int, quote_level::Int = 0) head, args, nargs = ex.head, ex.args, length(ex.args) unhandled = false # dot (i.e. "x.y"), but not compact broadcast exps @@ -1132,7 +1200,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) field = unquoted(args[2]) parens = !is_quoted(item) && !(item isa Symbol && isidentifier(item)) && !Meta.isexpr(item, :(.)) parens && print(io, '(') - show_unquoted(io, item, indent) + show_unquoted(io, item, indent, 0, quote_level) parens && print(io, ')') # . print(io, '.') @@ -1141,7 +1209,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) quoted = parens || isoperator(field) quoted && print(io, ':') parens && print(io, '(') - show_unquoted(io, field, indent) + show_unquoted(io, field, indent, 0, quote_level) parens && print(io, ')') else unhandled = true @@ -1152,13 +1220,25 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) func_prec = operator_precedence(head) head_ = head in expr_infix_wide ? " $head " : head if func_prec <= prec - show_enclosed_list(io, '(', args, head_, ')', indent, func_prec, true) + show_enclosed_list(io, '(', args, head_, ')', indent, func_prec, quote_level, true) else - show_list(io, args, head_, indent, func_prec, true) + show_list(io, args, head_, indent, func_prec, quote_level, true) end # list (i.e. "(1, 2, 3)" or "[1, 2, 3]") - elseif haskey(expr_parens, head) # :tuple/:vcat + elseif haskey(expr_parens, head) || # :tuple/:vcat + head === :typed_vcat || head === :typed_hcat + # print the type and defer to the untyped case + if head === :typed_vcat || head === :typed_hcat + show_unquoted(io, args[1], indent, prec, quote_level) + if head === :typed_vcat + head = :vcat + else + head = :hcat + end + args = args[2:end] + nargs = nargs - 1 + end op, cl = expr_parens[head] if head === :vcat || head === :bracescat sep = "; " @@ -1168,7 +1248,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) sep = ", " end head !== :row && print(io, op) - show_list(io, args, sep, indent) + show_list(io, args, sep, indent, 0, quote_level) if nargs == 1 if head === :tuple print(io, ',') @@ -1192,19 +1272,19 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) if (func === :* && length(func_args)==2 && isa(func_args[1], Real) && isa(func_args[2], Symbol)) if func_prec <= prec - show_enclosed_list(io, '(', func_args, "", ')', indent, func_prec) + show_enclosed_list(io, '(', func_args, "", ')', indent, func_prec, quote_level) else - show_list(io, func_args, "", indent, func_prec) + show_list(io, func_args, "", indent, func_prec, quote_level) end # unary operator (i.e. "!z") elseif isa(func,Symbol) && func in uni_ops && length(func_args) == 1 - show_unquoted(io, func, indent) + show_unquoted(io, func, indent, 0, quote_level) arg1 = func_args[1] if isa(arg1, Expr) || (isa(arg1, Symbol) && isoperator(arg1)) show_enclosed_list(io, '(', func_args, ", ", ')', indent, func_prec) else - show_unquoted(io, arg1, indent, func_prec) + show_unquoted(io, arg1, indent, func_prec, quote_level) end # binary operator (i.e. "x + y") @@ -1215,70 +1295,70 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) sep = func === :(:) ? "$func" : " $func " if func_prec <= prec - show_enclosed_list(io, '(', func_args, sep, ')', indent, func_prec, true) + show_enclosed_list(io, '(', func_args, sep, ')', indent, func_prec, quote_level, true) else - show_list(io, func_args, sep, indent, func_prec, true) + show_list(io, func_args, sep, indent, func_prec, quote_level, true) end elseif na == 1 # 1-argument call to normally-binary operator op, cl = expr_calls[head] print(io, "(") - show_unquoted(io, func, indent) + show_unquoted(io, func, indent, 0, quote_level) print(io, ")") - show_enclosed_list(io, op, func_args, ", ", cl, indent) + show_enclosed_list(io, op, func_args, ", ", cl, indent, 0, quote_level) else - show_call(io, head, func, func_args, indent) + show_call(io, head, func, func_args, indent, quote_level) end # normal function (i.e. "f(x,y)") else - show_call(io, head, func, func_args, indent) + show_call(io, head, func, func_args, indent, quote_level) end # new expr elseif head === :new || head === :splatnew - show_enclosed_list(io, "%$head(", args, ", ", ")", indent) + show_enclosed_list(io, "%$head(", args, ", ", ")", indent, 0, quote_level) # other call-like expressions ("A[1,2]", "T{X,Y}", "f.(X,Y)") elseif haskey(expr_calls, head) && nargs >= 1 # :ref/:curly/:calldecl/:(.) - funcargslike = head == :(.) ? args[2].args : args[2:end] - show_call(io, head, args[1], funcargslike, indent) + funcargslike = head === :(.) ? args[2].args : args[2:end] + show_call(io, head, args[1], funcargslike, indent, quote_level) # comprehensions elseif head === :typed_comprehension && nargs == 2 - show_unquoted(io, args[1], indent) + show_unquoted(io, args[1], indent, 0, quote_level) print(io, '[') - show_generator(io, args[2], indent) + show_generator(io, args[2], indent, quote_level) print(io, ']') elseif head === :comprehension && nargs == 1 print(io, '[') - show_generator(io, args[1], indent) + show_generator(io, args[1], indent, quote_level) print(io, ']') elseif (head === :generator && nargs >= 2) || (head === :flatten && nargs == 1) print(io, '(') - show_generator(io, ex, indent) + show_generator(io, ex, indent, quote_level) print(io, ')') elseif head === :filter && nargs == 2 - show_unquoted(io, args[2], indent) + show_unquoted(io, args[2], indent, 0, quote_level) print(io, " if ") - show_unquoted(io, args[1], indent) + show_unquoted(io, args[1], indent, 0, quote_level) # comparison (i.e. "x < y < z") elseif head === :comparison && nargs >= 3 && (nargs&1==1) comp_prec = minimum(operator_precedence, args[2:2:end]) if comp_prec <= prec - show_enclosed_list(io, '(', args, " ", ')', indent, comp_prec) + show_enclosed_list(io, '(', args, " ", ')', indent, comp_prec, quote_level) else - show_list(io, args, " ", indent, comp_prec) + show_list(io, args, " ", indent, comp_prec, quote_level) end # function calls need to transform the function from :call to :calldecl # so that operators are printed correctly elseif head === :function && nargs==2 && is_expr(args[1], :call) - show_block(io, head, Expr(:calldecl, args[1].args...), args[2], indent) + show_block(io, head, Expr(:calldecl, args[1].args...), args[2], indent, quote_level) print(io, "end") elseif (head === :function || head === :macro) && nargs == 1 @@ -1287,47 +1367,51 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) print(io, " end") elseif head === :do && nargs == 2 - show_unquoted(io, args[1], indent, -1) + show_unquoted(io, args[1], indent, -1, quote_level) print(io, " do ") - show_list(io, args[2].args[1].args, ", ", 0) + show_list(io, args[2].args[1].args, ", ", 0, 0, quote_level) for stmt in args[2].args[2].args print(io, '\n', " "^(indent + indent_width)) - show_unquoted(io, stmt, indent + indent_width, -1) + show_unquoted(io, stmt, indent + indent_width, -1, quote_level) end print(io, '\n', " "^indent) print(io, "end") # block with argument elseif head in (:for,:while,:function,:macro,:if,:elseif,:let) && nargs==2 - show_block(io, head, args[1], args[2], indent) + if Meta.isexpr(args[2], :block) + show_block(io, head, args[1], args[2], indent, quote_level) + else + show_block(io, head, args[1], Expr(:block, args[2]), indent, quote_level) + end print(io, "end") elseif (head === :if || head === :elseif) && nargs == 3 - show_block(io, head, args[1], args[2], indent) - if isa(args[3],Expr) && args[3].head == :elseif - show_unquoted(io, args[3], indent, prec) + show_block(io, head, args[1], args[2], indent, quote_level) + if isa(args[3],Expr) && args[3].head === :elseif + show_unquoted(io, args[3], indent, prec, quote_level) else - show_block(io, "else", args[3], indent) + show_block(io, "else", args[3], indent, quote_level) print(io, "end") end elseif head === :module && nargs==3 && isa(args[1],Bool) - show_block(io, args[1] ? :module : :baremodule, args[2], args[3], indent) + show_block(io, args[1] ? :module : :baremodule, args[2], args[3], indent, quote_level) print(io, "end") # type declaration elseif head === :struct && nargs==3 - show_block(io, args[1] ? Symbol("mutable struct") : Symbol("struct"), args[2], args[3], indent) + show_block(io, args[1] ? Symbol("mutable struct") : Symbol("struct"), args[2], args[3], indent, quote_level) print(io, "end") elseif head === :primitive && nargs == 2 print(io, "primitive type ") - show_list(io, args, ' ', indent) + show_list(io, args, ' ', indent, 0, quote_level) print(io, " end") elseif head === :abstract && nargs == 1 print(io, "abstract type ") - show_list(io, args, ' ', indent) + show_list(io, args, ' ', indent, 0, quote_level) print(io, " end") # empty return (i.e. "function f() return end") @@ -1337,12 +1421,12 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) # type annotation (i.e. "::Int") elseif head in uni_syms && nargs == 1 print(io, head) - show_unquoted(io, args[1], indent) + show_unquoted(io, args[1], indent, 0, quote_level) # var-arg declaration or expansion # (i.e. "function f(L...) end" or "f(B...)") elseif head === :(...) && nargs == 1 - show_unquoted(io, args[1], indent) + show_unquoted(io, args[1], indent, 0, quote_level) print(io, "...") elseif (nargs == 0 && head in (:break, :continue)) @@ -1351,33 +1435,73 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) elseif (nargs == 1 && head in (:return, :const)) || head in (:local, :global) print(io, head, ' ') - show_list(io, args, ", ", indent) + show_list(io, args, ", ", indent, 0, quote_level) elseif head === :export print(io, head, ' ') show_list(io, allow_macroname.(args), ", ", indent) elseif head === :macrocall && nargs >= 2 - # first show the line number argument as a comment - if isa(args[2], LineNumberNode) || is_expr(args[2], :line) - print(io, args[2], ' ') - end - # Use the functional syntax unless specifically designated with prec=-1 - # and hide the line number argument from the argument list - mname = allow_macroname(args[1]) - if prec >= 0 - show_call(io, :call, mname, args[3:end], indent) + # handle some special syntaxes + # `a b c` + if is_core_macro(args[1], "@cmd") + print(io, "`", args[3], "`") + # 11111111111111111111, 0xfffffffffffffffff, 1111...many digits... + elseif is_core_macro(args[1], "@int128_str") || + is_core_macro(args[1], "@uint128_str") || + is_core_macro(args[1], "@big_str") + print(io, args[3]) + # x"y" and x"y"z + elseif isa(args[1], Symbol) && + startswith(string(args[1]::Symbol), "@") && + endswith(string(args[1]::Symbol), "_str") + s = string(args[1]::Symbol) + print(io, s[2:prevind(s,end,4)], "\"", args[3], "\"") + if nargs == 4 + print(io, args[4]) + end + # general case else - show_args = Vector{Any}(undef, nargs - 1) - show_args[1] = mname - show_args[2:end] = args[3:end] - show_list(io, show_args, ' ', indent) + # first show the line number argument as a comment + if isa(args[2], LineNumberNode) || is_expr(args[2], :line) + print(io, args[2], ' ') + end + # Use the functional syntax unless specifically designated with + # prec=-1 and hide the line number argument from the argument list + mname = allow_macroname(args[1]) + if prec >= 0 + show_call(io, :call, mname, args[3:end], indent, quote_level) + else + show_args = Vector{Any}(undef, nargs - 1) + show_args[1] = mname + show_args[2:end] = args[3:end] + show_list(io, show_args, ' ', indent, 0, quote_level) + end end elseif head === :macroname && nargs == 1 arg1 = args[1] if arg1 isa Symbol show_sym(io, arg1, allow_macroname=true) + elseif is_expr(arg1, :(.)) && length(arg1.args) == 2 + if arg1.args[1] isa Module + print(io, '(') + print(io, arg1.args[1]) + print(io, ").") + else + print(io, arg1.args[1]) + print(io, '.') + end + if is_expr(arg1.args[2], :quote) + mname = arg1.args[2].args[1] + else + mname = arg1.args[2].value + end + if mname isa Symbol + show_sym(io, mname, allow_macroname=true) + else + show_unquoted(io, mname) + end else show_unquoted(io, arg1) end @@ -1386,25 +1510,36 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) show_linenumber(io, args...) elseif head === :try && 3 <= nargs <= 4 - show_block(io, "try", args[1], indent) + show_block(io, "try", args[1], indent, quote_level) if is_expr(args[3], :block) - show_block(io, "catch", args[2] === false ? Any[] : args[2], args[3], indent) + show_block(io, "catch", args[2] === false ? Any[] : args[2], args[3], indent, quote_level) end if nargs >= 4 && is_expr(args[4], :block) - show_block(io, "finally", Any[], args[4], indent) + show_block(io, "finally", Any[], args[4], indent, quote_level) end print(io, "end") elseif head === :block - show_block(io, "begin", ex, indent) + show_block(io, "begin", ex, indent, quote_level) print(io, "end") elseif head === :quote && nargs == 1 && isa(args[1], Symbol) - show_unquoted_quote_expr(io, args[1]::Symbol, indent, 0) + show_unquoted_quote_expr(io, args[1]::Symbol, indent, 0, quote_level+1) + elseif head === :quote && nargs == 1 && Meta.isexpr(args[1], :block) + show_block(io, "quote", Expr(:quote, args[1].args...), indent, + quote_level+1) + print(io, "end") + elseif head === :quote && nargs == 1 + print(io, ":(") + show_unquoted(io, args[1], indent+2, 0, quote_level+1) + print(io, ")") + elseif head === :quote + show_block(io, "quote", ex, indent, quote_level+1) + print(io, "end") elseif head === :gotoifnot && nargs == 2 && isa(args[2], Int) print(io, "unless ") - show_unquoted(io, args[1], indent, 0) + show_unquoted(io, args[1], indent, 0, quote_level) print(io, " goto %") print(io, args[2]::Int) @@ -1415,16 +1550,20 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) print(io, "nothing") elseif head === :kw && nargs == 2 - show_unquoted(io, args[1], indent+indent_width) + show_unquoted(io, args[1], indent+indent_width, 0, quote_level) print(io, '=') - show_unquoted(io, args[2], indent+indent_width) + show_unquoted(io, args[2], indent+indent_width, 0, quote_level) elseif head === :string print(io, '"') for x in args if !isa(x,AbstractString) print(io, "\$(") - show_unquoted(io, x) + if isa(x,Symbol) && !(x in quoted_syms) + show_sym(io, x) + else + show_unquoted(io, x, 0, 0, quote_level) + end print(io, ")") else escape_string(io, x, "\"\$") @@ -1432,21 +1571,29 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) end print(io, '"') - elseif (head === :&#= || head === :$=#) && nargs == 1 - print(io, head) - a1 = args[1] - parens = (isa(a1,Expr) && a1.head !== :tuple) || (isa(a1,Symbol) && isoperator(a1)) - parens && print(io, "(") - show_unquoted(io, a1) - parens && print(io, ")") + elseif (head === :& || head === :$) && nargs == 1 + if head === :$ + quote_level -= 1 + end + if head === :$ && quote_level < 0 && get(io, :unquote_fallback, true) + unhandled = true + else + print(io, head) + a1 = args[1] + parens = (isa(a1,Expr) && (a1.head !== :tuple && a1.head !== :$)) || + (isa(a1,Symbol) && isoperator(a1)) + parens && print(io, "(") + show_unquoted(io, a1, 0, 0, quote_level) + parens && print(io, ")") + end # transpose elseif head === Symbol('\'') && nargs == 1 if isa(args[1], Symbol) - show_unquoted(io, args[1]) + show_unquoted(io, args[1], 0, 0, quote_level) else print(io, "(") - show_unquoted(io, args[1]) + show_unquoted(io, args[1], 0, 0, quote_level) print(io, ")") end print(io, head) @@ -1455,20 +1602,23 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) elseif head === :where && nargs > 1 parens = 1 <= prec parens && print(io, "(") - show_unquoted(io, args[1], indent, operator_precedence(:(::))) + show_unquoted(io, args[1], indent, operator_precedence(:(::)), quote_level) print(io, " where ") if nargs == 2 - show_unquoted(io, args[2], indent, 1) + show_unquoted(io, args[2], indent, 1, quote_level) else print(io, "{") - show_list(io, args[2:end], ", ", indent) + show_list(io, args[2:end], ", ", indent, 0, quote_level) print(io, "}") end parens && print(io, ")") - elseif (head === :import || head === :using) && nargs == 1 && - (valid_import_path(args[1]) || - (Meta.isexpr(args[1], :(:)) && length(args[1].args) > 1 && all(valid_import_path, args[1].args))) + elseif (head === :import || head === :using) && + ((nargs == 1 && (valid_import_path(args[1]) || + (Meta.isexpr(args[1], :(:)) && + length((args[1]::Expr).args) > 1 && + all(valid_import_path, (args[1]::Expr).args)))) || + all(valid_import_path, args)) print(io, head) print(io, ' ') first = true @@ -1477,7 +1627,7 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) print(io, ", ") end first = false - show_import_path(io, a) + show_import_path(io, a, quote_level) end elseif head === :meta && nargs >= 2 && args[1] === :push_loc print(io, "# meta: location ", join(args[2:end], " ")) @@ -1494,7 +1644,13 @@ function show_unquoted(io::IO, ex::Expr, indent::Int, prec::Int) show(io, ex.head) for arg in args print(io, ", ") - show(io, arg) + if isa(arg, Expr) + print(io, ":(") + show_unquoted(io, arg, indent, 0, quote_level+1) + print(io, ")") + else + show(io, arg) + end end print(io, "))") end @@ -1567,7 +1723,7 @@ resolvebinding(@nospecialize(ex)) = ex resolvebinding(ex::QuoteNode) = ex.value resolvebinding(ex::Symbol) = resolvebinding(GlobalRef(Main, ex)) function resolvebinding(ex::Expr) - if ex.head == :. && isa(ex.args[2], Symbol) + if ex.head === :. && isa(ex.args[2], Symbol) parent = resolvebinding(ex.args[1]) if isa(parent, Module) return resolvebinding(GlobalRef(parent, ex.args[2])) @@ -1584,7 +1740,7 @@ function resolvebinding(ex::GlobalRef) end function ismodulecall(ex::Expr) - return ex.head == :call && (ex.args[1] === GlobalRef(Base,:getfield) || + return ex.head === :call && (ex.args[1] === GlobalRef(Base,:getfield) || ex.args[1] === GlobalRef(Core,:getfield)) && isa(resolvebinding(ex.args[2]), Module) end @@ -1641,7 +1797,7 @@ module IRShow :none => src -> Base.IRShow.lineinfo_disabled, ) const default_debuginfo = Ref{Symbol}(:none) - debuginfo(sym) = sym == :default ? default_debuginfo[] : sym + debuginfo(sym) = sym === :default ? default_debuginfo[] : sym end function show(io::IO, src::CodeInfo; debuginfo::Symbol=:source) @@ -1779,6 +1935,10 @@ function dump(io::IOContext, x::DataType, n::Int, indent) tvar_io = IOContext(tvar_io, :unionall_env => tparam) end end + if x.name === NamedTuple_typename && !(x.parameters[1] isa Tuple) + # named tuple type with unknown field names + return + end fields = fieldnames(x) fieldtypes = datatype_fieldtypes(x) for idx in 1:length(fields) diff --git a/base/simdloop.jl b/base/simdloop.jl index 0fdedba06e1cd..933a309461d38 100644 --- a/base/simdloop.jl +++ b/base/simdloop.jl @@ -15,7 +15,7 @@ end # symbol '=' range # symbol 'in' range function parse_iteration_space(x) - (isa(x, Expr) && (x.head == :(=) || x.head == :in)) || throw(SimdError("= or in expected")) + (isa(x, Expr) && (x.head === :(=) || x.head === :in)) || throw(SimdError("= or in expected")) length(x.args) == 2 || throw(SimdError("simd range syntax is wrong")) isa(x.args[1], Symbol) || throw(SimdError("simd loop index must be a symbol")) x.args # symbol, range @@ -23,7 +23,7 @@ end # reject invalid control flow statements in @simd loop body function check_body!(x::Expr) - if x.head === :break || x.head == :continue + if x.head === :break || x.head === :continue throw(SimdError("$(x.head) is not allowed inside a @simd loop body")) elseif x.head === :macrocall && x.args[1] === Symbol("@goto") throw(SimdError("$(x.args[1]) is not allowed inside a @simd loop body")) @@ -55,7 +55,7 @@ simd_outer_range(r) = 0:0 # Compile Expr x in context of @simd. function compile(x, ivdep) - (isa(x, Expr) && x.head == :for) || throw(SimdError("for loop expected")) + (isa(x, Expr) && x.head === :for) || throw(SimdError("for loop expected")) length(x.args) == 2 || throw(SimdError("1D for loop expected")) check_body!(x) @@ -129,7 +129,7 @@ macro simd(forloop) end macro simd(ivdep, forloop) - if ivdep == :ivdep + if ivdep === :ivdep esc(compile(forloop, Symbol("julia.ivdep"))) else throw(SimdError("Only ivdep is valid as the first argument to @simd")) diff --git a/base/sort.jl b/base/sort.jl index c72570ec9c2e6..4e2eca7e31715 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -91,7 +91,7 @@ issorted(itr; lt=isless, by=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward) = issorted(itr, ord(lt,by,rev,order)) -function partialsort!(v::AbstractVector, k::Union{Int,OrdinalRange}, o::Ordering) +function partialsort!(v::AbstractVector, k::Union{Integer,OrdinalRange}, o::Ordering) inds = axes(v, 1) sort!(v, first(inds), last(inds), PartialQuickSort(k), o) maybeview(v, k) @@ -151,7 +151,7 @@ julia> a 1 ``` """ -partialsort!(v::AbstractVector, k::Union{Int,OrdinalRange}; +partialsort!(v::AbstractVector, k::Union{Integer,OrdinalRange}; lt=isless, by=identity, rev::Union{Bool,Nothing}=nothing, order::Ordering=Forward) = partialsort!(v, k, ord(lt,by,rev,order)) @@ -161,7 +161,7 @@ partialsort!(v::AbstractVector, k::Union{Int,OrdinalRange}; Variant of [`partialsort!`](@ref) which copies `v` before partially sorting it, thereby returning the same thing as `partialsort!` but leaving `v` unmodified. """ -partialsort(v::AbstractVector, k::Union{Int,OrdinalRange}; kws...) = +partialsort(v::AbstractVector, k::Union{Integer,OrdinalRange}; kws...) = partialsort!(copymutable(v), k; kws...) @@ -387,7 +387,7 @@ struct QuickSortAlg <: Algorithm end struct MergeSortAlg <: Algorithm end """ - PartialQuickSort{T <: Union{Int,OrdinalRange}} + PartialQuickSort{T <: Union{Integer,OrdinalRange}} Indicate that a sorting function should use the partial quick sort algorithm. Partial quick sort returns the smallest `k` elements sorted from smallest @@ -400,7 +400,7 @@ Characteristics: * *in-place* in memory. * *divide-and-conquer*: sort strategy similar to [`MergeSort`](@ref). """ -struct PartialQuickSort{T <: Union{Int,OrdinalRange}} <: Algorithm +struct PartialQuickSort{T <: Union{Integer,OrdinalRange}} <: Algorithm k::T end @@ -460,7 +460,7 @@ const DEFAULT_STABLE = MergeSort const SMALL_ALGORITHM = InsertionSort const SMALL_THRESHOLD = 20 -function sort!(v::AbstractVector, lo::Int, hi::Int, ::InsertionSortAlg, o::Ordering) +function sort!(v::AbstractVector, lo::Integer, hi::Integer, ::InsertionSortAlg, o::Ordering) @inbounds for i = lo+1:hi j = i x = v[i] @@ -485,7 +485,7 @@ end # Upon return, the pivot is in v[lo], and v[hi] is guaranteed to be # greater than the pivot -@inline function selectpivot!(v::AbstractVector, lo::Int, hi::Int, o::Ordering) +@inline function selectpivot!(v::AbstractVector, lo::Integer, hi::Integer, o::Ordering) @inbounds begin mi = (lo+hi)>>>1 @@ -511,7 +511,7 @@ end # # select a pivot, and partition v according to the pivot -function partition!(v::AbstractVector, lo::Int, hi::Int, o::Ordering) +function partition!(v::AbstractVector, lo::Integer, hi::Integer, o::Ordering) pivot = selectpivot!(v, lo, hi, o) # pivot == v[lo], v[hi] > pivot i, j = lo, hi @@ -530,7 +530,7 @@ function partition!(v::AbstractVector, lo::Int, hi::Int, o::Ordering) return j end -function sort!(v::AbstractVector, lo::Int, hi::Int, a::QuickSortAlg, o::Ordering) +function sort!(v::AbstractVector, lo::Integer, hi::Integer, a::QuickSortAlg, o::Ordering) @inbounds while lo < hi hi-lo <= SMALL_THRESHOLD && return sort!(v, lo, hi, SMALL_ALGORITHM, o) j = partition!(v, lo, hi, o) @@ -548,7 +548,7 @@ function sort!(v::AbstractVector, lo::Int, hi::Int, a::QuickSortAlg, o::Ordering return v end -function sort!(v::AbstractVector, lo::Int, hi::Int, a::MergeSortAlg, o::Ordering, t=similar(v,0)) +function sort!(v::AbstractVector, lo::Integer, hi::Integer, a::MergeSortAlg, o::Ordering, t=similar(v,0)) @inbounds if lo < hi hi-lo <= SMALL_THRESHOLD && return sort!(v, lo, hi, SMALL_ALGORITHM, o) @@ -586,7 +586,7 @@ function sort!(v::AbstractVector, lo::Int, hi::Int, a::MergeSortAlg, o::Ordering return v end -function sort!(v::AbstractVector, lo::Int, hi::Int, a::PartialQuickSort{Int}, +function sort!(v::AbstractVector, lo::Integer, hi::Integer, a::PartialQuickSort{<:Integer}, o::Ordering) @inbounds while lo < hi hi-lo <= SMALL_THRESHOLD && return sort!(v, lo, hi, SMALL_ALGORITHM, o) @@ -609,7 +609,7 @@ function sort!(v::AbstractVector, lo::Int, hi::Int, a::PartialQuickSort{Int}, end -function sort!(v::AbstractVector, lo::Int, hi::Int, a::PartialQuickSort{T}, +function sort!(v::AbstractVector, lo::Integer, hi::Integer, a::PartialQuickSort{T}, o::Ordering) where T<:OrdinalRange @inbounds while lo < hi hi-lo <= SMALL_THRESHOLD && return sort!(v, lo, hi, SMALL_ALGORITHM, o) @@ -832,7 +832,7 @@ julia> partialsortperm!(ix, v, 2:3, initialized=true) ``` """ function partialsortperm!(ix::AbstractVector{<:Integer}, v::AbstractVector, - k::Union{Int, OrdinalRange}; + k::Union{Integer, OrdinalRange}; lt::Function=isless, by::Function=identity, rev::Union{Bool,Nothing}=nothing, @@ -901,8 +901,9 @@ function sortperm(v::AbstractVector; end end end - p = similar(Vector{Int}, axes(v, 1)) - for (i,ind) in zip(eachindex(p), axes(v, 1)) + ax = axes(v, 1) + p = similar(Vector{eltype(ax)}, ax) + for (i,ind) in zip(eachindex(p), ax) p[i] = ind end sort!(p, alg, Perm(ordr,v)) @@ -1112,9 +1113,9 @@ lt(::Left, x::T, y::T) where {T<:Floats} = slt_int(y, x) lt(::Right, x::T, y::T) where {T<:Floats} = slt_int(x, y) isnan(o::DirectOrdering, x::Floats) = (x!=x) -isnan(o::Perm, i::Int) = isnan(o.order,o.data[i]) +isnan(o::Perm, i::Integer) = isnan(o.order,o.data[i]) -function nans2left!(v::AbstractVector, o::Ordering, lo::Int=first(axes(v,1)), hi::Int=last(axes(v,1))) +function nans2left!(v::AbstractVector, o::Ordering, lo::Integer=first(axes(v,1)), hi::Integer=last(axes(v,1))) i = lo @inbounds while i <= hi && isnan(o,v[i]) i += 1 @@ -1129,7 +1130,7 @@ function nans2left!(v::AbstractVector, o::Ordering, lo::Int=first(axes(v,1)), hi end return i, hi end -function nans2right!(v::AbstractVector, o::Ordering, lo::Int=first(axes(v,1)), hi::Int=last(axes(v,1))) +function nans2right!(v::AbstractVector, o::Ordering, lo::Integer=first(axes(v,1)), hi::Integer=last(axes(v,1))) i = hi @inbounds while lo <= i && isnan(o,v[i]) i -= 1 @@ -1147,12 +1148,12 @@ end nans2end!(v::AbstractVector, o::ForwardOrdering) = nans2right!(v,o) nans2end!(v::AbstractVector, o::ReverseOrdering) = nans2left!(v,o) -nans2end!(v::AbstractVector{Int}, o::Perm{<:ForwardOrdering}) = nans2right!(v,o) -nans2end!(v::AbstractVector{Int}, o::Perm{<:ReverseOrdering}) = nans2left!(v,o) +nans2end!(v::AbstractVector{<:Integer}, o::Perm{<:ForwardOrdering}) = nans2right!(v,o) +nans2end!(v::AbstractVector{<:Integer}, o::Perm{<:ReverseOrdering}) = nans2left!(v,o) issignleft(o::ForwardOrdering, x::Floats) = lt(o, x, zero(x)) issignleft(o::ReverseOrdering, x::Floats) = lt(o, x, -zero(x)) -issignleft(o::Perm, i::Int) = issignleft(o.order, o.data[i]) +issignleft(o::Perm, i::Integer) = issignleft(o.order, o.data[i]) function fpsort!(v::AbstractVector, a::Algorithm, o::Ordering) i, j = lo, hi = nans2end!(v,o) diff --git a/base/stacktraces.jl b/base/stacktraces.jl index e04512905bf13..e63449d1ec23a 100644 --- a/base/stacktraces.jl +++ b/base/stacktraces.jl @@ -7,6 +7,7 @@ module StackTraces import Base: hash, ==, show +import Core: CodeInfo, MethodInstance export StackTrace, StackFrame, stacktrace @@ -52,7 +53,7 @@ struct StackFrame # this type should be kept platform-agnostic so that profiles "the line number in the file containing the execution context" line::Int "the MethodInstance or CodeInfo containing the execution context (if it could be found)" - linfo::Union{Core.MethodInstance, Core.CodeInfo, Nothing} + linfo::Union{MethodInstance, CodeInfo, Nothing} "true if the code is from C" from_c::Bool "true if the code is from an inlined frame" @@ -118,7 +119,7 @@ end const top_level_scope_sym = Symbol("top-level scope") function lookup(ip::Base.InterpreterIP) - if ip.code isa Core.MethodInstance && ip.code.def isa Method + if ip.code isa MethodInstance && ip.code.def isa Method codeinfo = ip.code.uninferred func = ip.code.def.name file = ip.code.def.file @@ -127,7 +128,7 @@ function lookup(ip::Base.InterpreterIP) # interpreted top-level expression with no CodeInfo return [StackFrame(top_level_scope_sym, empty_sym, 0, nothing, false, false, 0)] else - @assert ip.code isa Core.CodeInfo + @assert ip.code isa CodeInfo codeinfo = ip.code func = top_level_scope_sym file = empty_sym @@ -206,7 +207,7 @@ function remove_frames!(stack::StackTrace, m::Module) return stack end -is_top_level_frame(f::StackFrame) = f.linfo isa Core.CodeInfo || (f.linfo === nothing && f.func === top_level_scope_sym) +is_top_level_frame(f::StackFrame) = f.linfo isa CodeInfo || (f.linfo === nothing && f.func === top_level_scope_sym) function show_spec_linfo(io::IO, frame::StackFrame) if frame.linfo === nothing @@ -220,7 +221,7 @@ function show_spec_linfo(io::IO, frame::StackFrame) :nothing printstyled(io, Base.demangle_function_name(string(frame.func)), color=color) end - elseif frame.linfo isa Core.MethodInstance + elseif frame.linfo isa MethodInstance def = frame.linfo.def if isa(def, Method) sig = frame.linfo.specTypes @@ -243,7 +244,7 @@ function show_spec_linfo(io::IO, frame::StackFrame) else Base.show(io, frame.linfo) end - elseif frame.linfo isa Core.CodeInfo + elseif frame.linfo isa CodeInfo print(io, "top-level scope") end end @@ -276,7 +277,7 @@ function from(frame::StackFrame, m::Module) finfo = frame.linfo result = false - if finfo isa Core.MethodInstance + if finfo isa MethodInstance frame_m = finfo.def isa(frame_m, Method) && (frame_m = frame_m.module) result = nameof(frame_m) === nameof(m) diff --git a/base/stream.jl b/base/stream.jl index 7e80e241d2a78..933db06f67fee 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -917,7 +917,7 @@ function readuntil(x::LibuvStream, c::UInt8; keep::Bool=false) return bytes end -uv_write(s::LibuvStream, p::Vector{UInt8}) = uv_write(s, pointer(p), UInt(sizeof(p))) +uv_write(s::LibuvStream, p::Vector{UInt8}) = GC.@preserve p uv_write(s, pointer(p), UInt(sizeof(p))) # caller must have acquired the iolock function uv_write(s::LibuvStream, p::Ptr{UInt8}, n::UInt) @@ -1036,8 +1036,9 @@ function write(s::LibuvStream, b::UInt8) if buf !== nothing iolock_begin() if bytesavailable(buf) + 1 < buf.maxsize + n = write(buf, b) iolock_end() - return write(buf, b) + return n end iolock_end() end diff --git a/base/strings/io.jl b/base/strings/io.jl index 3d4f8139a8425..0afddcbdefb88 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -107,11 +107,19 @@ function sprint(f::Function, args...; context=nothing, sizehint::Integer=0) String(resize!(s.data, s.size)) end -tostr_sizehint(x) = 8 -tostr_sizehint(x::AbstractString) = lastindex(x) -tostr_sizehint(x::Union{String,SubString{String}}) = sizeof(x) -tostr_sizehint(x::Float64) = 20 -tostr_sizehint(x::Float32) = 12 +function _str_sizehint(x) + if x isa Float64 + return 20 + elseif x isa Float32 + return 12 + elseif x isa String || x isa SubString{String} + return sizeof(x) + elseif x isa Char + return ncodeunits(x) + else + return 8 + end +end function print_to_string(xs...) if isempty(xs) @@ -119,7 +127,7 @@ function print_to_string(xs...) end siz::Int = 0 for x in xs - siz += tostr_sizehint(x) + siz += _str_sizehint(x) end # specialized for performance reasons s = IOBuffer(sizehint=siz) @@ -135,7 +143,7 @@ function string_with_env(env, xs...) end siz::Int = 0 for x in xs - siz += tostr_sizehint(x) + siz += _str_sizehint(x) end # specialized for performance reasons s = IOBuffer(sizehint=siz) diff --git a/base/strings/search.jl b/base/strings/search.jl index 9840944da35df..15d0a968a0226 100644 --- a/base/strings/search.jl +++ b/base/strings/search.jl @@ -34,8 +34,8 @@ function _search(a::Union{String,ByteArray}, b::Union{Int8,UInt8}, i::Integer = return i == n+1 ? 0 : throw(BoundsError(a, i)) end p = pointer(a) - q = ccall(:memchr, Ptr{UInt8}, (Ptr{UInt8}, Int32, Csize_t), p+i-1, b, n-i+1) - q == C_NULL ? 0 : Int(q-p+1) + q = GC.@preserve a ccall(:memchr, Ptr{UInt8}, (Ptr{UInt8}, Int32, Csize_t), p+i-1, b, n-i+1) + return q == C_NULL ? 0 : Int(q-p+1) end function _search(a::ByteArray, b::AbstractChar, i::Integer = 1) @@ -74,8 +74,8 @@ function _rsearch(a::Union{String,ByteArray}, b::Union{Int8,UInt8}, i::Integer = return i == n+1 ? 0 : throw(BoundsError(a, i)) end p = pointer(a) - q = ccall(:memrchr, Ptr{UInt8}, (Ptr{UInt8}, Int32, Csize_t), p, b, i) - q == C_NULL ? 0 : Int(q-p+1) + q = GC.@preserve a ccall(:memrchr, Ptr{UInt8}, (Ptr{UInt8}, Int32, Csize_t), p, b, i) + return q == C_NULL ? 0 : Int(q-p+1) end function _rsearch(a::ByteArray, b::AbstractChar, i::Integer = length(a)) diff --git a/base/strings/string.jl b/base/strings/string.jl index a7f4777e594b6..56b603f0cce35 100644 --- a/base/strings/string.jl +++ b/base/strings/string.jl @@ -66,7 +66,7 @@ Convert a string to a contiguous byte array representation encoded as UTF-8 byte This representation is often appropriate for passing strings to C. """ String(s::AbstractString) = print_to_string(s) -String(s::Symbol) = unsafe_string(unsafe_convert(Ptr{UInt8}, s)) +@pure String(s::Symbol) = unsafe_string(unsafe_convert(Ptr{UInt8}, s)) unsafe_wrap(::Type{Vector{UInt8}}, s::String) = ccall(:jl_string_to_array, Ref{Vector{UInt8}}, (Any,), s) @@ -81,13 +81,14 @@ String(s::CodeUnits{UInt8,String}) = s.s pointer(s::String) = unsafe_convert(Ptr{UInt8}, s) pointer(s::String, i::Integer) = pointer(s)+(i-1) -ncodeunits(s::String) = Core.sizeof(s) -sizeof(s::String) = Core.sizeof(s) +@pure ncodeunits(s::String) = Core.sizeof(s) +@pure sizeof(s::String) = Core.sizeof(s) codeunit(s::String) = UInt8 @inline function codeunit(s::String, i::Integer) @boundscheck checkbounds(s, i) - GC.@preserve s unsafe_load(pointer(s, i)) + b = GC.@preserve s unsafe_load(pointer(s, i)) + return b end ## comparison ## @@ -112,7 +113,7 @@ typemin(::String) = typemin(String) ## thisind, nextind ## -Base.@propagate_inbounds thisind(s::String, i::Int) = _thisind_str(s, i) +@propagate_inbounds thisind(s::String, i::Int) = _thisind_str(s, i) # s should be String or SubString{String} @inline function _thisind_str(s, i::Int) @@ -133,7 +134,7 @@ Base.@propagate_inbounds thisind(s::String, i::Int) = _thisind_str(s, i) return i end -Base.@propagate_inbounds nextind(s::String, i::Int) = _nextind_str(s, i) +@propagate_inbounds nextind(s::String, i::Int) = _nextind_str(s, i) # s should be String or SubString{String} @inline function _nextind_str(s, i::Int) @@ -251,7 +252,7 @@ getindex(s::String, r::UnitRange{<:Integer}) = s[Int(first(r)):Int(last(r))] j = nextind(s, j) - 1 n = j - i + 1 ss = _string_n(n) - unsafe_copyto!(pointer(ss), pointer(s, i), n) + GC.@preserve s ss unsafe_copyto!(pointer(ss), pointer(s, i), n) return ss end @@ -323,7 +324,7 @@ function repeat(c::Char, r::Integer) n = 4 - (leading_zeros(u | 0xff) >> 3) s = _string_n(n*r) p = pointer(s) - if n == 1 + GC.@preserve s if n == 1 ccall(:memset, Ptr{Cvoid}, (Ptr{UInt8}, Cint, Csize_t), p, u % UInt8, r) elseif n == 2 p16 = reinterpret(Ptr{UInt16}, p) @@ -340,7 +341,7 @@ function repeat(c::Char, r::Integer) unsafe_store!(p, b3, 3i + 3) end elseif n == 4 - p32 = reinterpret(Ptr{UInt32}, pointer(s)) + p32 = reinterpret(Ptr{UInt32}, p) for i = 1:r unsafe_store!(p32, u, i) end @@ -349,11 +350,11 @@ function repeat(c::Char, r::Integer) end function filter(f, s::String) - out = Base.StringVector(sizeof(s)) + out = StringVector(sizeof(s)) offset = 1 for c in s if f(c) - offset += Base.__unsafe_string!(out, c, offset) + offset += __unsafe_string!(out, c, offset) end end resize!(out, offset-1) diff --git a/base/strings/substring.jl b/base/strings/substring.jl index a83c5d17d9e18..cfebe9991eb7f 100644 --- a/base/strings/substring.jl +++ b/base/strings/substring.jl @@ -51,7 +51,11 @@ convert(::Type{SubString{S}}, s::AbstractString) where {S<:AbstractString} = SubString(convert(S, s)) convert(::Type{T}, s::T) where {T<:SubString} = s -String(s::SubString{String}) = unsafe_string(pointer(s.string, s.offset+1), s.ncodeunits) +function String(s::SubString{String}) + parent = s.string + copy = GC.@preserve parent unsafe_string(pointer(parent, s.offset+1), s.ncodeunits) + return copy +end ncodeunits(s::SubString) = s.ncodeunits codeunit(s::SubString) = codeunit(s.string) @@ -151,25 +155,27 @@ end string(a::String) = String(a) string(a::SubString{String}) = String(a) -@inline function __unsafe_string!(out, c::Char, offs::Integer) +@inline function __unsafe_string!(out, c::Char, offs::Integer) # out is a (new) String (or StringVector) x = bswap(reinterpret(UInt32, c)) n = ncodeunits(c) - unsafe_store!(pointer(out, offs), x % UInt8) - n == 1 && return n - x >>= 8 - unsafe_store!(pointer(out, offs+1), x % UInt8) - n == 2 && return n - x >>= 8 - unsafe_store!(pointer(out, offs+2), x % UInt8) - n == 3 && return n - x >>= 8 - unsafe_store!(pointer(out, offs+3), x % UInt8) + GC.@preserve out begin + unsafe_store!(pointer(out, offs), x % UInt8) + n == 1 && return n + x >>= 8 + unsafe_store!(pointer(out, offs+1), x % UInt8) + n == 2 && return n + x >>= 8 + unsafe_store!(pointer(out, offs+2), x % UInt8) + n == 3 && return n + x >>= 8 + unsafe_store!(pointer(out, offs+3), x % UInt8) + end return n end @inline function __unsafe_string!(out, s::Union{String, SubString{String}}, offs::Integer) n = sizeof(s) - unsafe_copyto!(pointer(out, offs), pointer(s), n) + GC.@preserve s out unsafe_copyto!(pointer(out, offs), pointer(s), n) return n end @@ -200,7 +206,7 @@ function repeat(s::Union{String, SubString{String}}, r::Integer) ccall(:memset, Ptr{Cvoid}, (Ptr{UInt8}, Cint, Csize_t), out, b, r) else for i = 0:r-1 - unsafe_copyto!(pointer(out, i*n+1), pointer(s), n) + GC.@preserve s out unsafe_copyto!(pointer(out, i*n+1), pointer(s), n) end end return out diff --git a/base/strings/unicode.jl b/base/strings/unicode.jl index f203e2751301a..5d5bc93ef592b 100644 --- a/base/strings/unicode.jl +++ b/base/strings/unicode.jl @@ -198,11 +198,11 @@ function normalize( end function normalize(s::AbstractString, nf::Symbol) - utf8proc_map(s, nf == :NFC ? (UTF8PROC_STABLE | UTF8PROC_COMPOSE) : - nf == :NFD ? (UTF8PROC_STABLE | UTF8PROC_DECOMPOSE) : - nf == :NFKC ? (UTF8PROC_STABLE | UTF8PROC_COMPOSE + utf8proc_map(s, nf === :NFC ? (UTF8PROC_STABLE | UTF8PROC_COMPOSE) : + nf === :NFD ? (UTF8PROC_STABLE | UTF8PROC_DECOMPOSE) : + nf === :NFKC ? (UTF8PROC_STABLE | UTF8PROC_COMPOSE | UTF8PROC_COMPAT) : - nf == :NFKD ? (UTF8PROC_STABLE | UTF8PROC_DECOMPOSE + nf === :NFKD ? (UTF8PROC_STABLE | UTF8PROC_DECOMPOSE | UTF8PROC_COMPAT) : throw(ArgumentError(":$nf is not one of :NFC, :NFD, :NFKC, :NFKD"))) end diff --git a/base/strings/util.jl b/base/strings/util.jl index ecc1b11a60575..c84cbf1cb82c3 100644 --- a/base/strings/util.jl +++ b/base/strings/util.jl @@ -104,7 +104,7 @@ end # chop(s::AbstractString) = SubString(s, firstindex(s), prevind(s, lastindex(s))) """ - chomp(s::AbstractString) + chomp(s::AbstractString) -> SubString Remove a single trailing newline from a string. @@ -133,8 +133,8 @@ function chomp(s::String) end """ - lstrip([pred=isspace,] str::AbstractString) - lstrip(str::AbstractString, chars) + lstrip([pred=isspace,] str::AbstractString) -> SubString + lstrip(str::AbstractString, chars) -> SubString Remove leading characters from `str`, either those specified by `chars` or those for which the function `pred` returns `true`. @@ -165,8 +165,8 @@ lstrip(s::AbstractString) = lstrip(isspace, s) lstrip(s::AbstractString, chars::Chars) = lstrip(in(chars), s) """ - rstrip([pred=isspace,] str::AbstractString) - rstrip(str::AbstractString, chars) + rstrip([pred=isspace,] str::AbstractString) -> SubString + rstrip(str::AbstractString, chars) -> SubString Remove trailing characters from `str`, either those specified by `chars` or those for which the function `pred` returns `true`. @@ -196,8 +196,8 @@ rstrip(s::AbstractString) = rstrip(isspace, s) rstrip(s::AbstractString, chars::Chars) = rstrip(in(chars), s) """ - strip([pred=isspace,] str::AbstractString) - strip(str::AbstractString, chars) + strip([pred=isspace,] str::AbstractString) -> SubString + strip(str::AbstractString, chars) -> SubString Remove leading and trailing characters from `str`, either those specified by `chars` or those for which the function `pred` returns `true`. @@ -442,7 +442,7 @@ function replace(str::String, pat_repl::Pair; count::Integer=typemax(Int)) out = IOBuffer(sizehint=floor(Int, 1.2sizeof(str))) while j != 0 if i == a || i <= k - unsafe_write(out, pointer(str, i), UInt(j-i)) + GC.@preserve str unsafe_write(out, pointer(str, i), UInt(j-i)) _replace(out, repl, str, r, pattern) end if k < j diff --git a/base/subarray.jl b/base/subarray.jl index 59cc8cbd41699..a1b68ceaadf4f 100644 --- a/base/subarray.jl +++ b/base/subarray.jl @@ -4,7 +4,13 @@ abstract type AbstractCartesianIndex{N} end # This is a hacky forward declaratio const ViewIndex = Union{Real, AbstractArray} const ScalarIndex = Real -# L is true if the view itself supports fast linear indexing +""" + SubArray{T,N,P,I,L} <: AbstractArray{T,N} + +`N`-dimensional view into a parent array (of type `P`) with an element type `T`, restricted by a tuple of indices (of type `I`). `L` is true for types that support fast linear indexing, and `false` otherwise. + +Construct `SubArray`s using the [`view`](@ref) function. +""" struct SubArray{T,N,P,I,L} <: AbstractArray{T,N} parent::P indices::I diff --git a/base/summarysize.jl b/base/summarysize.jl index 714b60ed8dfea..69aa791669b66 100644 --- a/base/summarysize.jl +++ b/base/summarysize.jl @@ -125,7 +125,7 @@ function (ss::SummarySize)(obj::Array) dsize += length(obj) end size += dsize - if !isbitstype(eltype(obj)) && !isempty(obj) + if !isempty(obj) && !Base.allocatedinline(eltype(obj)) push!(ss.frontier_x, obj) push!(ss.frontier_i, 1) end diff --git a/base/sysinfo.jl b/base/sysinfo.jl index df71537ad05f5..e9b454ebdaf66 100644 --- a/base/sysinfo.jl +++ b/base/sysinfo.jl @@ -49,6 +49,9 @@ A string containing the full path to the directory containing the `julia` execut A string containing the full path to the directory containing the `stdlib` packages. """ STDLIB = "$BINDIR/../share/julia/stdlib/v$(VERSION.major).$(VERSION.minor)" # for bootstrap +# In case STDLIB change after julia is built, the variable below can be used +# to update cached method locations to updated ones. +const BUILD_STDLIB_PATH = STDLIB # helper to avoid triggering precompile warnings diff --git a/base/task.jl b/base/task.jl index 25b2459160b95..efad78c0acba8 100644 --- a/base/task.jl +++ b/base/task.jl @@ -141,7 +141,7 @@ julia> istaskdone(b) true ``` """ -istaskdone(t::Task) = ((t.state == :done) | istaskfailed(t)) +istaskdone(t::Task) = ((t.state === :done) | istaskfailed(t)) """ istaskstarted(t::Task) -> Bool @@ -182,7 +182,7 @@ julia> istaskfailed(b) true ``` """ -istaskfailed(t::Task) = (t.state == :failed) +istaskfailed(t::Task) = (t.state === :failed) Threads.threadid(t::Task) = Int(ccall(:jl_get_task_tid, Int16, (Any,), t)+1) @@ -244,6 +244,22 @@ function _wait(t::Task) nothing end +# have `waiter` wait for `t` +function _wait2(t::Task, waiter::Task) + if !istaskdone(t) + lock(t.donenotify) + if !istaskdone(t) + push!(t.donenotify.waitq, waiter) + unlock(t.donenotify) + return nothing + else + unlock(t.donenotify) + end + end + schedule(waiter) + nothing +end + function wait(t::Task) t === current_task() && error("deadlock detected: cannot wait on current task") _wait(t) @@ -352,12 +368,6 @@ macro sync_add(expr) end end -function register_taskdone_hook(t::Task, hook) - tls = get_task_tls(t) - push!(get!(tls, :TASKDONE_HOOKS, []), hook) - return t -end - # runtime system hook called when a task finishes function task_done_hook(t::Task) # `finish_task` sets `sigatomic` before entering this function @@ -381,16 +391,9 @@ function task_done_hook(t::Task) end end - # Execute any other hooks registered in the TLS - if isa(t.storage, IdDict) && haskey(t.storage, :TASKDONE_HOOKS) - foreach(hook -> hook(t), t.storage[:TASKDONE_HOOKS]) - delete!(t.storage, :TASKDONE_HOOKS) - handled = true - end - if err && !handled && Threads.threadid() == 1 if isa(result, InterruptException) && isdefined(Base, :active_repl_backend) && - active_repl_backend.backend_task.state == :runnable && isempty(Workqueue) && + active_repl_backend.backend_task.state === :runnable && isempty(Workqueue) && active_repl_backend.in_eval throwto(active_repl_backend.backend_task, result) # this terminates the task end @@ -405,7 +408,7 @@ function task_done_hook(t::Task) # issue #19467 if Threads.threadid() == 1 && isa(e, InterruptException) && isdefined(Base, :active_repl_backend) && - active_repl_backend.backend_task.state == :runnable && isempty(Workqueue) && + active_repl_backend.backend_task.state === :runnable && isempty(Workqueue) && active_repl_backend.in_eval throwto(active_repl_backend.backend_task, e) else @@ -482,7 +485,7 @@ function __preinit_threads__() end function enq_work(t::Task) - (t.state == :runnable && t.queue === nothing) || error("schedule: Task not runnable") + (t.state === :runnable && t.queue === nothing) || error("schedule: Task not runnable") tid = Threads.threadid(t) # Note there are three reasons a Task might be put into a sticky queue # even if t.sticky == false: @@ -542,13 +545,13 @@ true """ function schedule(t::Task, @nospecialize(arg); error=false) # schedule a task to be (re)started with the given value or exception - t.state == :runnable || Base.error("schedule: Task not runnable") + t.state === :runnable || Base.error("schedule: Task not runnable") if error t.queue === nothing || Base.list_deletefirst!(t.queue, t) - t.exception = arg + setfield!(t, :exception, arg) else t.queue === nothing || Base.error("schedule: Task not runnable") - t.result = arg + setfield!(t, :result, arg) end enq_work(t) return t @@ -624,7 +627,7 @@ end function ensure_rescheduled(othertask::Task) ct = current_task() W = Workqueues[Threads.threadid()] - if ct !== othertask && othertask.state == :runnable + if ct !== othertask && othertask.state === :runnable # we failed to yield to othertask # return it to the head of a queue to be retried later tid = Threads.threadid(othertask) @@ -641,7 +644,7 @@ end function trypoptask(W::StickyWorkqueue) isempty(W) && return t = popfirst!(W) - if t.state != :runnable + if t.state !== :runnable # assume this somehow got queued twice, # probably broken now, but try discarding this switch and keep going # can't throw here, because it's probably not the fault of the caller to wait @@ -656,8 +659,7 @@ end @noinline function poptaskref(W::StickyWorkqueue) task = trypoptask(W) if !(task isa Task) - gettask = () -> trypoptask(W) - task = ccall(:jl_task_get_next, Any, (Any,), gettask)::Task + task = ccall(:jl_task_get_next, Ref{Task}, (Any, Any), trypoptask, W) end return Ref(task) end diff --git a/base/threadcall.jl b/base/threadcall.jl index 4754afec0d6ac..2267e4ea2228c 100644 --- a/base/threadcall.jl +++ b/base/threadcall.jl @@ -18,7 +18,7 @@ Note that the called function should never call back into Julia. """ macro threadcall(f, rettype, argtypes, argvals...) # check for usage errors - isa(argtypes,Expr) && argtypes.head == :tuple || + isa(argtypes,Expr) && argtypes.head === :tuple || error("threadcall: argument types must be a tuple") length(argtypes.args) == length(argvals) || error("threadcall: wrong number of arguments to C function") diff --git a/base/tuple.jl b/base/tuple.jl index 7881d57323845..861be57ef04eb 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -224,6 +224,9 @@ if nameof(@__MODULE__) === :Base (::Type{T})(x::Tuple) where {T<:Tuple} = convert(T, x) # still use `convert` for tuples +Tuple(x::Ref) = tuple(getindex(x)) # faster than iterator for one element +Tuple(x::Array{T,0}) where {T} = tuple(getindex(x)) + (::Type{T})(itr) where {T<:Tuple} = _totuple(T, itr) _totuple(::Type{Tuple{}}, itr, s...) = () diff --git a/base/util.jl b/base/util.jl index 7a9fc8ee28988..eba03fde0e894 100644 --- a/base/util.jl +++ b/base/util.jl @@ -3,20 +3,20 @@ # This type must be kept in sync with the C struct in src/gc.h struct GC_Num - allocd ::Int64 # GC internal - deferred_alloc::Int64 # GC internal - freed ::Int64 # GC internal - malloc ::UInt64 - realloc ::UInt64 - poolalloc ::UInt64 - bigalloc ::UInt64 - freecall ::UInt64 - total_time ::UInt64 - total_allocd::UInt64 # GC internal - since_sweep ::UInt64 # GC internal - collect ::Csize_t # GC internal - pause ::Cint - full_sweep ::Cint + allocd ::Int64 # GC internal + deferred_alloc ::Int64 # GC internal + freed ::Int64 # GC internal + malloc ::Int64 + realloc ::Int64 + poolalloc ::Int64 + bigalloc ::Int64 + freecall ::Int64 + total_time ::Int64 + total_allocd ::Int64 # GC internal + since_sweep ::Int64 # GC internal + collect ::Csize_t # GC internal + pause ::Cint + full_sweep ::Cint end gc_num() = ccall(:jl_gc_num, GC_Num, ()) @@ -35,21 +35,21 @@ struct GC_Diff end gc_total_bytes(gc_num::GC_Num) = - (gc_num.allocd + gc_num.deferred_alloc + Int64(gc_num.total_allocd)) + gc_num.allocd + gc_num.deferred_alloc + gc_num.total_allocd function GC_Diff(new::GC_Num, old::GC_Num) # logic from `src/gc.c:jl_gc_total_bytes` old_allocd = gc_total_bytes(old) new_allocd = gc_total_bytes(new) return GC_Diff(new_allocd - old_allocd, - Int64(new.malloc - old.malloc), - Int64(new.realloc - old.realloc), - Int64(new.poolalloc - old.poolalloc), - Int64(new.bigalloc - old.bigalloc), - Int64(new.freecall - old.freecall), - Int64(new.total_time - old.total_time), - new.pause - old.pause, - new.full_sweep - old.full_sweep) + new.malloc - old.malloc, + new.realloc - old.realloc, + new.poolalloc - old.poolalloc, + new.bigalloc - old.bigalloc, + new.freecall - old.freecall, + new.total_time - old.total_time, + new.pause - old.pause, + new.full_sweep - old.full_sweep) end function gc_alloc_count(diff::GC_Diff) @@ -60,9 +60,6 @@ end # total time spend in garbage collection, in nanoseconds gc_time_ns() = ccall(:jl_gc_total_hrtime, UInt64, ()) -# total number of bytes allocated so far -gc_bytes() = ccall(:jl_gc_total_bytes, Int64, ()) - # print elapsed time, return expression value const _mem_units = ["byte", "KiB", "MiB", "GiB", "TiB", "PiB"] const _cnt_units = ["", " k", " M", " G", " T", " P"] @@ -159,6 +156,7 @@ julia> @time begin """ macro time(ex) quote + while false; end # compiler heuristic: compile this block (alter this if the heuristic changes) local stats = gc_num() local elapsedtime = time_ns() local val = $(esc(ex)) @@ -192,6 +190,7 @@ malloc() calls: 1 """ macro timev(ex) quote + while false; end # compiler heuristic: compile this block (alter this if the heuristic changes) local stats = gc_num() local elapsedtime = time_ns() local val = $(esc(ex)) @@ -217,27 +216,21 @@ julia> @elapsed sleep(0.3) """ macro elapsed(ex) quote + while false; end # compiler heuristic: compile this block (alter this if the heuristic changes) local t0 = time_ns() - local val = $(esc(ex)) - (time_ns()-t0)/1e9 + $(esc(ex)) + (time_ns() - t0) / 1e9 end end -# measure bytes allocated without *most* contamination from compilation -# Note: This reports a different value from the @time macros, because -# it wraps the call in a function, however, this means that things -# like: @allocated y = foo() -# will not work correctly, because it will set y in the context of -# the local function made by the macro, not the current function +# total number of bytes allocated so far +gc_bytes(b::Ref{Int64}) = ccall(:jl_gc_get_total_bytes, Cvoid, (Ptr{Int64},), b) + """ @allocated A macro to evaluate an expression, discarding the resulting value, instead returning the -total number of bytes allocated during evaluation of the expression. Note: the expression is -evaluated inside a local function, instead of the current context, in order to eliminate the -effects of compilation, however, there still may be some allocations due to JIT compilation. -This also makes the results inconsistent with the `@time` macros, which do not try to adjust -for the effects of compilation. +total number of bytes allocated during evaluation of the expression. See also [`@time`](@ref), [`@timev`](@ref), [`@timed`](@ref), and [`@elapsed`](@ref). @@ -249,15 +242,13 @@ julia> @allocated rand(10^6) """ macro allocated(ex) quote - let - local f - function f() - b0 = gc_bytes() - $(esc(ex)) - gc_bytes() - b0 - end - f() - end + while false; end # compiler heuristic: compile this block (alter this if the heuristic changes) + local b0 = Ref{Int64}(0) + local b1 = Ref{Int64}(0) + gc_bytes(b0) + $(esc(ex)) + gc_bytes(b1) + b1[] - b0[] end end @@ -292,6 +283,7 @@ julia> memallocs.total_time """ macro timed(ex) quote + while false; end # compiler heuristic: compile this block (alter this if the heuristic changes) local stats = gc_num() local elapsedtime = time_ns() local val = $(esc(ex)) @@ -304,7 +296,7 @@ end ## printing with color ## -const text_colors = AnyDict( +const text_colors = Dict{Union{Symbol,Int},String}( :black => "\033[30m", :red => "\033[31m", :green => "\033[32m", @@ -334,7 +326,7 @@ for i in 0:255 text_colors[i] = "\033[38;5;$(i)m" end -const disable_text_style = AnyDict( +const disable_text_style = Dict{Symbol,String}( :bold => "\033[22m", :underline => "\033[24m", :blink => "\033[25m", @@ -377,7 +369,7 @@ function with_output_color(f::Function, color::Union{Int, Symbol}, io::IO, args. if !iscolor print(io, str) else - bold && color == :bold && (color = :nothing) + bold && color === :bold && (color = :nothing) enable_ansi = get(text_colors, color, text_colors[:default]) * (bold ? text_colors[:bold] : "") disable_ansi = (bold ? disable_text_style[:bold] : "") * @@ -714,9 +706,9 @@ Stacktrace: """ macro kwdef(expr) expr = macroexpand(__module__, expr) # to expand @static - expr isa Expr && expr.head == :struct || error("Invalid usage of @kwdef") + expr isa Expr && expr.head === :struct || error("Invalid usage of @kwdef") T = expr.args[2] - if T isa Expr && T.head == :<: + if T isa Expr && T.head === :<: T = T.args[1] end @@ -729,13 +721,13 @@ macro kwdef(expr) if !isempty(params_ex.args) if T isa Symbol kwdefs = :(($(esc(T)))($params_ex) = ($(esc(T)))($(call_args...))) - elseif T isa Expr && T.head == :curly + elseif T isa Expr && T.head === :curly # if T == S{A<:AA,B<:BB}, define two methods # S(...) = ... # S{A,B}(...) where {A<:AA,B<:BB} = ... S = T.args[1] P = T.args[2:end] - Q = [U isa Expr && U.head == :<: ? U.args[1] : U for U in P] + Q = [U isa Expr && U.head === :<: ? U.args[1] : U for U in P] SQ = :($S{$(Q...)}) kwdefs = quote ($(esc(S)))($params_ex) =($(esc(S)))($(call_args...)) @@ -764,12 +756,12 @@ function _kwdef!(blk, params_args, call_args) push!(params_args, ei) push!(call_args, ei) elseif ei isa Expr - if ei.head == :(=) + if ei.head === :(=) lhs = ei.args[1] if lhs isa Symbol # var = defexpr var = lhs - elseif lhs isa Expr && lhs.head == :(::) && lhs.args[1] isa Symbol + elseif lhs isa Expr && lhs.head === :(::) && lhs.args[1] isa Symbol # var::T = defexpr var = lhs.args[1] else @@ -781,12 +773,12 @@ function _kwdef!(blk, params_args, call_args) push!(params_args, Expr(:kw, var, esc(defexpr))) push!(call_args, var) blk.args[i] = lhs - elseif ei.head == :(::) && ei.args[1] isa Symbol + elseif ei.head === :(::) && ei.args[1] isa Symbol # var::Typ var = ei.args[1] push!(params_args, var) push!(call_args, var) - elseif ei.head == :block + elseif ei.head === :block # can arise with use of @static inside type decl _kwdef!(ei, params_args, call_args) end @@ -821,6 +813,7 @@ function runtests(tests = ["all"]; ncores = ceil(Int, Sys.CPU_THREADS / 2), try run(setenv(`$(julia_cmd()) $(joinpath(Sys.BINDIR::String, Base.DATAROOTDIR, "julia", "test", "runtests.jl")) $tests`, ENV2)) + nothing catch buf = PipeBuffer() Base.require(Base, :InteractiveUtils).versioninfo(buf) diff --git a/base/version.jl b/base/version.jl index 16c9ccc83a8cf..c8d754915f06e 100644 --- a/base/version.jl +++ b/base/version.jl @@ -237,7 +237,13 @@ catch e VersionNumber(0) end -const libllvm_version = VersionNumber(libllvm_version_string) +const libllvm_version = if endswith(libllvm_version_string, "jl") + # strip the "jl" SONAME suffix (JuliaLang/julia#33058) + # (LLVM does never report a prerelease version anyway) + VersionNumber(libllvm_version_string[1:end-2]) +else + VersionNumber(libllvm_version_string) +end function banner(io::IO = stdout) if GIT_VERSION_INFO.tagged_commit diff --git a/base/views.jl b/base/views.jl index 915ba0a9e7d64..b1be0dc0962a8 100644 --- a/base/views.jl +++ b/base/views.jl @@ -18,11 +18,11 @@ replace_ref_end!(ex) = replace_ref_end_!(ex, nothing)[1] # replace_ref_end_!(ex,withex) returns (new ex, whether withex was used) function replace_ref_end_!(ex, withex) used_withex = false - if isa(ex,Symbol) && ex == :end + if isa(ex,Symbol) && ex === :end withex === nothing && error("Invalid use of end") return withex, true elseif isa(ex,Expr) - if ex.head == :ref + if ex.head === :ref ex.args[1], used_withex = replace_ref_end_!(ex.args[1],withex) S = isa(ex.args[1],Symbol) ? ex.args[1]::Symbol : gensym(:S) # temp var to cache ex.args[1] if needed used_S = false # whether we actually need S @@ -40,7 +40,7 @@ function replace_ref_end_!(ex, withex) exj, used = replace_ref_end_!(ex.args[j],:($lastindex($S,$n))) used_S |= used ex.args[j] = exj - if isa(exj,Expr) && exj.head == :... + if isa(exj,Expr) && exj.head === :... # splatted object exjs = exj.args[1] n = :($n + length($exjs)) @@ -141,7 +141,7 @@ function _views(ex::Expr) Expr(ex.head, Meta.isexpr(lhs, :ref) ? Expr(:ref, _views.(lhs.args)...) : _views(lhs), _views(ex.args[2])) - elseif ex.head == :ref + elseif ex.head === :ref Expr(:call, maybeview, _views.(ex.args)...) else h = string(ex.head) diff --git a/base/weakkeydict.jl b/base/weakkeydict.jl index db1b60e27d771..ce8da7edd35db 100644 --- a/base/weakkeydict.jl +++ b/base/weakkeydict.jl @@ -6,7 +6,7 @@ WeakKeyDict([itr]) `WeakKeyDict()` constructs a hash table where the keys are weak -references to objects, and thus may be garbage collected even when +references to objects which may be garbage collected even when referenced in a hash table. See [`Dict`](@ref) for further help. Note, unlike [`Dict`](@ref), @@ -112,24 +112,12 @@ getindex(wkh::WeakKeyDict{K}, key) where {K} = lock(() -> getindex(wkh.ht, key), isempty(wkh::WeakKeyDict) = isempty(wkh.ht) length(t::WeakKeyDict) = length(t.ht) -function iterate(t::WeakKeyDict{K,V}) where V where K - gc_token = Ref{Bool}(false) # no keys will be deleted via finalizers until this token is gc'd - finalizer(gc_token) do r - if r[] - r[] = false - unlock(t.lock) - end - end - s = lock(t.lock) - iterate(t, (gc_token,)) -end -function iterate(t::WeakKeyDict{K,V}, state) where V where K - gc_token = first(state) - y = iterate(t.ht, tail(state)...) +function iterate(t::WeakKeyDict{K,V}, state...) where {K, V} + y = lock(() -> iterate(t.ht, state...), t) y === nothing && return nothing - wkv, i = y + wkv, newstate = y kv = Pair{K,V}(wkv[1].value::K, wkv[2]) - return (kv, (gc_token, i)) + return (kv, newstate) end filter!(f, d::WeakKeyDict) = filter_in_one_pass!(f, d) diff --git a/contrib/fixup-libgfortran.sh b/contrib/fixup-libgfortran.sh index fbe744bff65c7..d1f00bdf85950 100755 --- a/contrib/fixup-libgfortran.sh +++ b/contrib/fixup-libgfortran.sh @@ -3,6 +3,7 @@ # Run as: fixup-libgfortran.sh [--verbose] <$private_libdir> FC=${FC:-gfortran} +PATCHELF=${PATCHELF:-patchelf} # If we're invoked with "--verbose", create a `debug` function that prints stuff out if [ "$1" = "--verbose" ] || [ "$1" = "-v" ]; then @@ -126,7 +127,7 @@ change_linkage() echo " $old_link" install_name_tool -change "$old_link" "@rpath/$soname" "$lib_path" else # $UNAME is "Linux", we only have two options, see above - patchelf --set-rpath \$ORIGIN "$lib_path" + ${PATCHELF} --set-rpath \$ORIGIN "$lib_path" fi } diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index 97c4297a18d24..a4f97e8f8fe53 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -35,6 +35,19 @@ julia_exepath() = joinpath(Sys.BINDIR, Base.julia_exename()) have_repl = haskey(Base.loaded_modules, Base.PkgId(Base.UUID("3fa0cd96-eef1-5676-8a61-b3b8758bbffb"), "REPL")) + +Distributed = get(Base.loaded_modules, + Base.PkgId(Base.UUID("8ba89e20-285c-5b6f-9357-94700520ee1b"), "Distributed"), + nothing) +if Distributed !== nothing + precompile_script *= """ + using Distributed + addprocs(2) + pmap(x->iseven(x) ? 1 : 0, 1:4) + @distributed (+) for i = 1:100 Int(rand(Bool)) end + """ +end + Pkg = get(Base.loaded_modules, Base.PkgId(Base.UUID("44cfe95a-1eb2-52ea-b672-e2afdf69b78f"), "Pkg"), nothing) @@ -131,7 +144,7 @@ function generate_precompile_statements() close(pty_master) write(debug_output, "\n#### FINISHED ####\n") - # Extract the precompile statements from stderr + # Extract the precompile statements from the precompile file statements = Set{String}() for statement in eachline(precompile_file_h) # Main should be completely clean diff --git a/deps/Makefile b/deps/Makefile index 500d6120bce0c..e6a975ba18731 100644 --- a/deps/Makefile +++ b/deps/Makefile @@ -23,19 +23,14 @@ BUILDDIR := $(BUILDDIR)$(MAYBE_HOST) # additionally all targets should be listed in the getall target for easier off-line compilation # if you are adding a new target, it can help to copy an similar, existing target # -# autoconf configure-driven scripts: llvm pcre unwind gmp mpfr patchelf libuv curl +# autoconf configure-driven scripts: pcre unwind gmp mpfr patchelf libuv curl # custom Makefile rules: openlibm dsfmt suitesparse-wrapper suitesparse lapack openblas utf8proc objconv osxunwind libwhich -# CMake libs: libgit2 libssh2 mbedtls +# CMake libs: llvm libgit2 libssh2 mbedtls # -# downloaded from git: llvm-svn, libuv, libopenlibm, utf8proc, libgit2, libssh2 -# -# there are rules in this file with the . replaced by a % -# this is some magic Makefile trick that tells make -# that all targets with a % in them on that line will -# be rebuilt in a single invocation +# downloadable via git: llvm-svn, libuv, libopenlibm, utf8proc, libgit2, libssh2 # # to debug 'define' rules, replace eval at the usage site with info or error -# + ## Overall configuration of which rules exist and should be run by default ## @@ -162,7 +157,7 @@ ifneq ($(OS), WINNT) DEP_LIBS += libwhich endif -DEP_LIBS_STAGED := $(filter-out suitesparse suitesparse-wrapper osxunwind,$(DEP_LIBS)) # unlist targets that have not been converted to use the staged-install +DEP_LIBS_STAGED := $(filter-out suitesparse-wrapper osxunwind,$(DEP_LIBS)) # unlist targets that have not been converted to use the staged-install ## Common build target prefixes @@ -176,7 +171,6 @@ check: $(addprefix check-, $(DEP_LIBS)) fastcheck: $(addprefix fastcheck-, $(DEP_LIBS)) stage: $(addprefix stage-, $(DEP_LIBS_STAGED)) install: $(addprefix install-, $(DEP_LIBS)) -uninstall: $(addprefix uninstall-, $(DEP_LIBS_STAGED)) cleanall: $(addprefix clean-, $(DEP_LIBS)) distcleanall: $(addprefix distclean-, $(DEP_LIBS)) rm -rf $(build_prefix) @@ -202,3 +196,5 @@ include $(SRCDIR)/libgit2.mk include $(SRCDIR)/libwhich.mk include $(SRCDIR)/zlib.mk include $(SRCDIR)/p7zip.mk + +include $(SRCDIR)/tools/uninstallers.mk diff --git a/deps/Versions.make b/deps/Versions.make index bedbe140dd9ba..ba287ea76536b 100644 --- a/deps/Versions.make +++ b/deps/Versions.make @@ -1,5 +1,5 @@ -LLVM_VER = 6.0.1 -LLVM_BB_REL = 7+nowasm +LLVM_VER = 8.0.1 +LLVM_BB_REL = 3 PCRE_VER = 10.31 PCRE_BB_REL = 0 DSFMT_VER = 2.2.3 @@ -33,11 +33,11 @@ LIBUV_BB_REL = 0 OBJCONV_VER = 2.49.0 OBJCONV_BB_REL = 0 ZLIB_VER = 1.2.11 -ZLIB_BB_REL = 3 +ZLIB_BB_REL = 6 P7ZIP_VER = 16.2.0 P7ZIP_BB_REL = 1 # Specify the version of the Mozilla CA Certificate Store to obtain. # The versions of cacert.pem are identified by the date (YYYY-MM-DD) of their changes. # See https://curl.haxx.se/docs/caextract.html for more details. -MOZILLA_CACERT_VERSION := 2019-05-15 +MOZILLA_CACERT_VERSION := 2019-10-16 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc4.tar.gz/md5 deleted file mode 100644 index 30714194d4996..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc4.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -32c5f933b5e8758e35c87f5054afa26c diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc4.tar.gz/sha512 deleted file mode 100644 index 61c93951581de..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc4.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -93c65ed59636696b98860c8227e8750636a5b40f0061bc1eb9d0811b4fb813c823fe631fc03ea74446306b9bf4643c0534ee6cafd6bc8aacf44a02a4ca11a572 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc7.tar.gz/md5 deleted file mode 100644 index ea531d98915ca..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc7.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -9658cbfc2e72d0176d358f51c11367e7 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc7.tar.gz/sha512 deleted file mode 100644 index 5452c5569f036..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc7.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -ba3bf88bfba4da816c9a5a68f16e3af0b9d7cb5999d605a9a63d06a241c8c342beb46d96db1130eebb00ae46aed958fd9541868fd7f9cb1c51d0b3305509fcc0 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc8.tar.gz/md5 deleted file mode 100644 index 41d4287743b3a..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -0b0920e2a2e279e92e739e3fffd18d6e diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc8.tar.gz/sha512 deleted file mode 100644 index 35414ac1d1d19..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.aarch64-linux-gnu-gcc8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -9ca4f3967c83ed3b5a51ec97a44169218e4822eb40c8b1a7d8060258709f72290f4d76dc9c26e6ae41a9bb6eddf76b3908dcc01b839a7b9b7e62ccbb6446e948 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc4.tar.gz/md5 deleted file mode 100644 index a7785568f6942..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc4.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -c3ad727fb901f0f663175b307de5b1b8 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc4.tar.gz/sha512 deleted file mode 100644 index cd6396af27d92..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc4.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -63f351c3c58f8858db69dd964f909f6ea4891f836c20ff18d7344f787e57f5764febce79237aaa6b25a02964eb470c81d2881878aab25c753e79e210c8f7c8af diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc7.tar.gz/md5 deleted file mode 100644 index 4425c9540495f..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc7.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -f7f0818187f0d0b3291a7d27e83a1d53 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc7.tar.gz/sha512 deleted file mode 100644 index abcca986f87c5..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc7.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -cae860ca5aeda88742a8916caa4e74d382d3b631f8b5f7b1ebbc62540a7cb2d9890a8a6c8249ab13e3ec3edb4b928ec622b5044066995ec5b91afb851922671c diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc8.tar.gz/md5 deleted file mode 100644 index d73f1daf0c765..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -8d832d7c4a1ffe5057ac9257ae417d2c diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc8.tar.gz/sha512 deleted file mode 100644 index 2be0593ed7f62..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.arm-linux-gnueabihf-gcc8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -0ea0b64f64de19a24c3184d790772535c2985355d0eb7113d58c3f4bffa66c9c9edff33640c169cc05c3edca6d0abe025e0cf0552a81c0d1e612c3047fb96804 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc4.tar.gz/md5 deleted file mode 100644 index b86f4e99bee06..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc4.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -de0f9114398066ee9e4dc7ad2dc5239a diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc4.tar.gz/sha512 deleted file mode 100644 index 260af1001d4ea..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc4.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -5811214cd654a08e44a7a103bd5a21e574d45336b579d862485c248464792ca96800ee47490a01387df8aa2af8affa8dac9b8a7a9a5a35815eede112df162325 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc7.tar.gz/md5 deleted file mode 100644 index 33902f37d045b..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc7.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -9d076399fbc7e0d400e6b4723223de70 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc7.tar.gz/sha512 deleted file mode 100644 index 8e85095ccb5d4..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc7.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -a4903d8af622fa5f08d0f790dbbc4d1cf7e46b05d44407b1bc757f86b8ddf1f45f728b406fc82b10b8e5c92cd029d182d99b6ab1344d7de645ef343247c66af1 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc8.tar.gz/md5 deleted file mode 100644 index 27711b0dcd791..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -9df643893cf28239e4f2ec1a302178ef diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc8.tar.gz/sha512 deleted file mode 100644 index 04224b030b953..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-linux-gnu-gcc8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -8b22d1ae21746d8ce2f73a75b05cae169c68eed8b2a64cebbfe286e377b31887d2412c68d001dc6e0b92c65a4500779e3a7b9a21d7ef0dbc88c43c6037319eb5 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc4.tar.gz/md5 deleted file mode 100644 index a3d675f0a2320..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc4.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -e2962033d78f4562aed94f1d7e0ec396 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc4.tar.gz/sha512 deleted file mode 100644 index d365ce9965223..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc4.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -2bd15d7c3d86534ee34097c5a5d31e4f4497979664b8e85f51dbd79e2a48eb25aa1925fa67325581f4717711ee82d91a5d8f6805f0aa3eac1198817814b9d675 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc7.tar.gz/md5 deleted file mode 100644 index ea690d199ac64..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc7.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -e6d9664644ce47046334290735b3f508 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc7.tar.gz/sha512 deleted file mode 100644 index 4df66829c10f7..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc7.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -4df060f23066788d93a8122ca6ea6abf7fa76cdde0c05d1cd50188298504150bf48fd115e2a78e332ee12ebd240e2e53397daf21d82235db9eafaf09b5df8347 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc8.tar.gz/md5 deleted file mode 100644 index 5c4a726857222..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -2db2bccfa589095c8d1f4254d9cfa253 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc8.tar.gz/sha512 deleted file mode 100644 index bcd79d6ab48f9..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.i686-w64-mingw32-gcc8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -0be45ceb79877f6037d9b0c8ba7439ffe2873b681a949c57573ba9a73c4b224a05c8fcdac6cbde9df7c8ddab324f3f44b9e91c1d120ce427baf4f35ef152bef3 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc4.tar.gz/md5 deleted file mode 100644 index 496db2a674b29..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc4.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -8cf440fc1f74d790954a513e81e1334f diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 deleted file mode 100644 index b8b9393f164c4..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -5ecaf6d3343d3afe6b43686b835c75e35b925c1766459af47ed18bc140e615203a59e6f1df63c4803f0cf7603e406b0e72b824cc8b77a765f894bc40a8d1a8b8 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc7.tar.gz/md5 deleted file mode 100644 index 1d911959d9185..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc7.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -71c9a128b6a41dde8672da7540ceb5a1 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 deleted file mode 100644 index b6973fe2ccfd4..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -11d3719960c8ba4e68aa02a0c6364bb997a2fa734451c19c4b02027c81240c87355f71c2df776c25eac6d62967c9f06a20af7e502bd3a7def6698a0a0d01de16 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc8.tar.gz/md5 deleted file mode 100644 index 85282ad7d8ab5..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -c2ce6cffa40a4dadf59e99d04ad495b6 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 deleted file mode 100644 index f2a628d138c75..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -854c5269e252aa1f076b75a0b8afb369a0ada62ee91365b30d3d108e01008785ebeb511575e65c03080e489c6e3db12db88c6e1729462bf2c6e95e3d757450e2 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc4.tar.gz/md5 deleted file mode 100644 index fd1cc7d9a1325..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc4.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -acc66103bc030b0691da5425de12d0d4 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc4.tar.gz/sha512 deleted file mode 100644 index 2933aed19efa8..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc4.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -e7fc442db075a588995def014174f8f631b7a06fec3cc21b2f2f4f42a4416fbd9f840d054bc89ab04b20e4e2614ed33fc7c75f7189a10c46b40b06ebb8b6589d diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc7.tar.gz/md5 deleted file mode 100644 index 0e966e6deda50..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc7.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -7dc70a32fd21745441387b7f7b7b71cc diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc7.tar.gz/sha512 deleted file mode 100644 index 1b168cb299d0a..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc7.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -a858cdb39074e02d4b2d02fc168df8605181bb100ca5877ad8607d2a49603b591e010a6dd8ce5bb97828bde7f948e9e85f537a00ac01a709914663d69566baec diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc8.tar.gz/md5 deleted file mode 100644 index 9bad624cc9ac7..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -f2dcde5881cb4d47c4c954210013c4db diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc8.tar.gz/sha512 deleted file mode 100644 index 5dc9ee7a83973..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-apple-darwin14-gcc8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -a900666f3827cdd67953d4caaf2084d5beb680d2cd1a73d8f2640144fc88107d2a7ff8357c7907d57e541be8a1ca3b751c281597840c790837238ec292635203 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc4.tar.gz/md5 deleted file mode 100644 index 726d5e08a62af..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc4.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -88ca9ab7847a5cc7136a356a7c91a7cc diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc4.tar.gz/sha512 deleted file mode 100644 index d9803542f1b88..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc4.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -6139c8df8232f749beb718b480d3093ef5b7640526a559c0affefdd2c09223d3df859a0a21c4a7d40fc7797acb7e2a6784aea78b742584d09faff16d01943f7b diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc7.tar.gz/md5 deleted file mode 100644 index 68963207d42e7..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc7.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -8a88e8676ebe377f9245d751fbcb3295 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc7.tar.gz/sha512 deleted file mode 100644 index bb4885f225b45..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc7.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -404238618223d952de2366601b55eeae18f0cfd4d02f5eabe70ee504791fb5dd2131001826cbd1b2f642d914f91ded47379be8439818d20b8234885c64d4a908 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc8.tar.gz/md5 deleted file mode 100644 index 4b4bcb26f3197..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -6e48d53154e7697e7f01c1bb47e8e54e diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc8.tar.gz/sha512 deleted file mode 100644 index 56c5fd16383b2..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-gnu-gcc8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -17adb6066b6962a76d06b0ae492a9d7330db51b26b9fe1925ebbd3874981a8a8848794fff88bf924fb405cf71ddb48e1efe6c4bf30293c28d342e4c160053702 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc4.tar.gz/md5 deleted file mode 100644 index 76df83ba871cf..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc4.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -b320e6bcb0184851cc5c8f776424b73f diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc4.tar.gz/sha512 deleted file mode 100644 index 163ebf3b39ef0..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc4.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -fd3f5a20f29d64173f02b35e0d9edc6a4df6a81a5e7c2355dd0f925869b8fb7258ab43ec0911c4e91d10a3c59432d9c709cbfa845f29c393a59daabf1c7172ae diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc7.tar.gz/md5 deleted file mode 100644 index 9ae1af65ad0e7..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc7.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -d0e5df7f099640b46be8ff5a66e4b6e1 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc7.tar.gz/sha512 deleted file mode 100644 index 5e1332f5eb29e..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc7.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -e3fd81ca6a5c9b5fc23700c59ef610bf649213bda34940d7e625f90cfbf09dd6f086fb4614d02c022cf9c4eb912e7487219badc2e30eef058b60d8ecdb2219ed diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc8.tar.gz/md5 deleted file mode 100644 index a8eb898b63d95..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -c07804931969bb9e569468416b60db6c diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc8.tar.gz/sha512 deleted file mode 100644 index 2416453ff120b..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-linux-musl-gcc8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -9ccd6bc350e406e782d52d821c1b9ded8adf67c332d90072657b3c0a8d3c42b65f48d891efe855a2b5067467fff542a86cf564675cd73defed828a07814365eb diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 deleted file mode 100644 index 087c9fe859f92..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -adf71aff58ce0f5ad224cca2d9c849d1 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 deleted file mode 100644 index bb5bbd89ebf7f..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -52cea3cfd56099777f35c2d04e4313c084f348598502d1c9c045a941bb8ec2767de81dd88abc181883c7a2fb0ec916e651a042a9666b89712162ef3b33ac6c9f diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 deleted file mode 100644 index 6447b83bf93aa..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -8f7ea4ba9465fdfe5a1fb182db51f1c6 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 deleted file mode 100644 index 51936f391f5f1..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -a1e0fdf3a92dae0a2c0bcc81d278003453521bc7b636e0e62565152a600996b1e24720669ee0e9d8fb65e19f8c0c53865168dd5904368e2ac0e1fcbca4eb8903 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 deleted file mode 100644 index 1ec02db3e4d86..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -354fe85d08d3d142404dbbdc172ce655 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 deleted file mode 100644 index 37941f31bbaf8..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -b4844ef9c3c0c8be95c2fe07de9a4c2afbcf8e100699d38cf8f6caa38ad6c804812f2063faf1c669d680355c3d6e16b115618b8984313c2aee2cd37f4a6796de diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc4.tar.gz/md5 deleted file mode 100644 index a13acb3425080..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc4.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -09e636495833682b7b636a379ece3835 diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc4.tar.gz/sha512 deleted file mode 100644 index 6eade83fd4986..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc4.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -0deeb5229bb55b010212feb80c79d39804a776eec988fc877636df9541ac1135c7114d0814d19a02ced312c42bd84683a8886c50e680e4a6c71582521dca42bd diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc7.tar.gz/md5 deleted file mode 100644 index f280864ddee48..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc7.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -40041e58babe9172b34716829867763a diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc7.tar.gz/sha512 deleted file mode 100644 index a24f38aeb2ee3..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc7.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -e6acd7f2baab220768df9b21374c0548d20eef6e93ffba613e286d3cd0f31464a3fd2d14f65089460113f9603d3852a40a18331bddfee855b24986ab6720c29f diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc8.tar.gz/md5 deleted file mode 100644 index 099c9938d2c8c..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc8.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -bd952c914461b396924e48a67765445e diff --git a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc8.tar.gz/sha512 deleted file mode 100644 index 7d8531661e07a..0000000000000 --- a/deps/checksums/LLVM.v6.0.1-7+nowasm.x86_64-w64-mingw32-gcc8.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -2c51c8ffd9d1c936fdbab2395cf29bc83f416473f3444ee5613a9f1b261c8693c7102bc2c2b53d2c2b5e473ffd94bd0db2d7cfc2e20d32c11129d1c3884b6214 diff --git a/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000000000..1b90b84308e74 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +deb53c9204e8f79cc083acba32c9b610 diff --git a/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000000000..cdf87367c19d7 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +c580545984d40e5c87013a63b73843cf897635015471a3c7875b2fc6874accdd66689df61b101b3666add570de775d48a6d6d4845b02add0d6c38e60bccf31b9 diff --git a/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000000000..b56fef50e07f1 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +24659fa5ad25d422f833c8ac13284aaa diff --git a/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000000000..64c841d92012b --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +38949fa25fbb823f18817f3febcf4fc7a4872a70f4a75c6caeb978800bc2f0fe47d6a95dad350b0e5baf68ea5ae72d73c595e4c244f004e8976fda7a6355e1be diff --git a/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000000000..fc1ecac2085a5 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +034e0213f230e4d5af532be98c2f9e68 diff --git a/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000000000..09ba6948e7eb4 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.aarch64-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +cb2b3a48b3f7a277efb1475c53b2a61259f4fe4d290ffba7adb227aeecac03c5f0bfebc97444c8d2771ad674345e21c1bcc02bc6a7feceb9b3ae731d36d26bb7 diff --git a/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc4.tar.gz/md5 new file mode 100644 index 0000000000000..163caed6254be --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +0dfb0a0767512f4797f9bdcd2d197183 diff --git a/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc4.tar.gz/sha512 new file mode 100644 index 0000000000000..02128b56ded58 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +f17c8d4a7f349c4b4295562ef123229f5f1d311b49a5d75106b48b925b682fde293b492a37a428f072a9c8186f8cee92e44723e40e6e277eac145f2efb748632 diff --git a/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc7.tar.gz/md5 new file mode 100644 index 0000000000000..cd83b7da108be --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +022871145bdc6ac96c1a9cda041175c6 diff --git a/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc7.tar.gz/sha512 new file mode 100644 index 0000000000000..366bf882ffe4a --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +9ae68f0a154d256335a43b3a959258f2560be8c9c36abd253b00a3259f2197bedd3daa31b6ab670cfd114526a427715d9f37533adf8d2308e5794b40cfc454e1 diff --git a/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc8.tar.gz/md5 new file mode 100644 index 0000000000000..2d714a7bd33b6 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +4e337773ca1c2446f9470c04239bb149 diff --git a/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc8.tar.gz/sha512 new file mode 100644 index 0000000000000..7fb38f22d9328 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.arm-linux-gnueabihf-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +476a6f447c62b577956f76c398952c6705897e17232d92d7fd1eb79984198e576dd0707d760c2eefbcae5986abe41cee8fa3d10457b853dc51c7d4000637e572 diff --git a/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000000000..9852f7cba0adf --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +3a7f0f18cf125d4dd95caa6cbda280b5 diff --git a/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000000000..4a78ddc90ba18 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +19fe933f37cb7248ef92f6db98aa7dda37fe747f31c496825ea5f376844c3849342e561f8032dbe6ca2b4e550d140d1b3024bdfc68c574e1df716a9bce6f1aa5 diff --git a/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000000000..f821e20b31a82 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +e0945d3933d05e804939487554aa4b9c diff --git a/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000000000..278425fcb327c --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +4ee1bef3c3f9ea3313df6500f62f24dfa62df1334ff2c50b04c7b9b5f560101e1eb85c473f965753a705bdb739b59e1d701965e90e3d48c4ab2c7737ce23cc6f diff --git a/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000000000..9fb3b98b869e0 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +460f4d33fece31578e22e381ccb1d55f diff --git a/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000000000..ac6a4666cf2cf --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.i686-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +49ed6d9d96a337a2562223841dd9e820a60709c54ede53055eea258f5345481f880724721d63bcefaf4e24da16a0a5627f68870b8c3882ada15877bbab1771fb diff --git a/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc4.tar.gz/md5 new file mode 100644 index 0000000000000..ba4bbd946f815 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +e4053a2f5fe5af836ed6d7525d0285b5 diff --git a/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc4.tar.gz/sha512 new file mode 100644 index 0000000000000..7b524cc89f753 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +d04366d78a4ad9f52ddf44acff1336f8aa05cce9ade38bc73884680e5315e3dff1bc3f0a4f65e79333f8d649ec3ec792bcc56bbf50de0b3952ce9957b524461a diff --git a/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc7.tar.gz/md5 new file mode 100644 index 0000000000000..a59cbe51bd2ea --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +b2b96e7adbc26a26766fab9e93e718bc diff --git a/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc7.tar.gz/sha512 new file mode 100644 index 0000000000000..942c3daf5c83d --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +228d7e07d639617d206664cafeea6e89d0cf8fbedc4e8b2d014c743fb890a220f9c9c90d33efc325d5709314a706104172e45c6c714e07527c9966331b2aa63e diff --git a/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc8.tar.gz/md5 new file mode 100644 index 0000000000000..500051e84d979 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +b23e00c7069221f6bb12cd425becf521 diff --git a/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc8.tar.gz/sha512 new file mode 100644 index 0000000000000..259277628a23a --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.i686-w64-mingw32-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +52777bfa56a6e050e73d9a2d4267773e8333962cbc44d203ab1bc9ddeb1aa62f87ea438e4992ace8d8d2006c769657ebf2182a3c4c6d4f073974ab1bd02ece8e diff --git a/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000000000..726942c32c1d2 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +1ab19324cb35af10455ab7ce6b23c485 diff --git a/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000000000..c4a818a3d4bbb --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +0609b6db180c5e4f8468566e3261e29774541783888ecb9a1e8db1daecb576072bfd31436f86f4eb6a99fd60758e02eb591bd7da750ce40218233bea1d296dc0 diff --git a/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000000000..9c02dc7f63078 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +7f5e9702ecca0a7fb0b453a5b4e23d76 diff --git a/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000000000..63d0447a78616 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +3607a06016259d97fa49f753ba7aac66a0fc25d38ff78ccca291889b4e6c8c91e57da597f33db7304f604c597518a17f03464905a6c3d8c4904399a43a235d24 diff --git a/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000000000..8084e1f82e6ee --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +d539a1a160ca69859262e533bee7c0b0 diff --git a/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000000000..5441a543274e7 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.powerpc64le-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +34bbcfb23ede7fcfe6a3c2c03671ec0e6b2a7d3e6f24343955f18b2c2772ece08710dcfaf0023d8821b4f15a3e51e167081956cd273a8a3db56c0f1dabc1af3c diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc4.tar.gz/md5 new file mode 100644 index 0000000000000..8ec5a6aa3b584 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +8ac2c9738387616d883ee7eef7ae9168 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc4.tar.gz/sha512 new file mode 100644 index 0000000000000..a245e3d9a6e97 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +256291a9063cb11ec9ce6545773c50c664a9b2691893af91e2a902ea7769823a5b98c7affb4504b356b37950de8b3e53d9cffe1a0081e3417313f7d3e9b32b1e diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc7.tar.gz/md5 new file mode 100644 index 0000000000000..6d80a3f93a155 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +519cf144413c31fb5cfe448e4df125bc diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc7.tar.gz/sha512 new file mode 100644 index 0000000000000..ae8e7e125e684 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +bc7896e6276205673f0cea7a0d213ebe5fe8c177e81df654afabbef7e6a2ab98e635f3f5ee47ab7e11b399522ce22d6c6b8d7610f78bf34dd79d334e8cd9f5e6 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc8.tar.gz/md5 new file mode 100644 index 0000000000000..01f6caa1921c4 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +aa180033e96f7541aed22f24edfc0ca7 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc8.tar.gz/sha512 new file mode 100644 index 0000000000000..ba30ed6d23646 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-apple-darwin14-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +0846f814142145199c302a1054506082d2d192db5ec971b48f8a6b233a726fb51f808d1de8630b066f1688f2912b6ca7736bc29aa7d7f06e06c8e93e8b122d8d diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc4.tar.gz/md5 new file mode 100644 index 0000000000000..e076290d2cf52 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +42131a6b05b120692e882845beb9b683 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc4.tar.gz/sha512 new file mode 100644 index 0000000000000..459007f269f00 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +895eaa8f5b60d0cad6182296aba3d363e7703d80e1cc74f6de88e691a256e795c64966b007c0d8f07bb2f49f300fb6a3d768ea4b142daaefe14b86f22fff809a diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc7.tar.gz/md5 new file mode 100644 index 0000000000000..31bfe2b02699b --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +16f4b7e92c623e92423ef6cf76e99173 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc7.tar.gz/sha512 new file mode 100644 index 0000000000000..18e844a3dbeff --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +e66b2370f12523dbc8e3e6c605213d132cbbd1d227020d67a6c794229e1e92c7bf4da95fbfca357b162c8feca7b851aa0fc9b2fdccb406dd3c2d99421ba22eab diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc8.tar.gz/md5 new file mode 100644 index 0000000000000..d785ade7dd510 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +2b1f7b71ba4da8dc0d2db88f5dece154 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc8.tar.gz/sha512 new file mode 100644 index 0000000000000..dd11dc16074c2 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-gnu-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +45a079b14c9a6c091a21469361d022ad665b6b4cb8b95a9d387a169bee22ac7421eca1120a302fd4df45108559b6c0d5672eb29885c421a4069dc1a3171d165c diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc4.tar.gz/md5 new file mode 100644 index 0000000000000..49fb67b6b31ba --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +4512358528d67393802e2fd93cd3ad10 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc4.tar.gz/sha512 new file mode 100644 index 0000000000000..42a3d6269639c --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +4fd1cd383d0ff7d13487fb41751b2e97af6d860f70f9fc0040446ece91f7c111e8e5b357f8854ed21416e2f063f4bf660bd829c3640666435f42ebbded8d5568 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc7.tar.gz/md5 new file mode 100644 index 0000000000000..fb7626f12ebfb --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +8ab475bf3887144aa30af15fa385aa8f diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc7.tar.gz/sha512 new file mode 100644 index 0000000000000..25bc1112cb195 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +c12d7497ee782c02bd59a888433b41ff0630485182212d6eeda267d1b3c0fad695ea6b6d81d2a1b778ec8bb031a53663d0f7e7b55d90e7cffc96b1eaf84cd576 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc8.tar.gz/md5 new file mode 100644 index 0000000000000..550ef75ab9646 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +033bf07b217d2f88db79b91de7be045e diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc8.tar.gz/sha512 new file mode 100644 index 0000000000000..e0390fda3ef5e --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-linux-musl-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +ff17f668f1e7a6080ea384c93ea7ce64028b725f3a4cc72a16864af86f171c6a05ff6e09c67dcfde97193c1d1adfc07aefa3cd2a3dbc24fb29accfa1e4970f11 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 new file mode 100644 index 0000000000000..8abd961907e0d --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +0961cab6b180976083a55b2d108a7678 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 new file mode 100644 index 0000000000000..0bf14609632d0 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +a2671d2fe26de3a76f13c874cfab18e5f30041e252a4fcd7d8eb59aef5972a97dca40d428fe8e9b639aa625a2a8b2b3193629ca01414d01cb9eb776a61f9d14f diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 new file mode 100644 index 0000000000000..9629e63563814 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +6f76652a553f09af2ed864b9a92e7929 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 new file mode 100644 index 0000000000000..8b67c71dc6f0f --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +e0075565f793dc90966f9969cf920b84c35e5977afce01e63d33280aad83845672edd6e5d2fb91342ad963be14775e24d7a01d560b5cbd641427e1332b579177 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 new file mode 100644 index 0000000000000..09d529624aa5f --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +7fda5ee74199ec605e51090c075db862 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 new file mode 100644 index 0000000000000..a7d7c5e162e0c --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-unknown-freebsd11.1-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +9c179b9e19960fd998ce27fa4b71759cb4333d2c425980b4498df4000c7e9dde636a27fc9fa33cee1137c9335c5a4b39804f2f2f235ea5952d4b7fa8183634b1 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc4.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc4.tar.gz/md5 new file mode 100644 index 0000000000000..914511fb81eec --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc4.tar.gz/md5 @@ -0,0 +1 @@ +8f7e45416c1346ca105579693194eac9 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc4.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc4.tar.gz/sha512 new file mode 100644 index 0000000000000..c8c30b2b6111a --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc4.tar.gz/sha512 @@ -0,0 +1 @@ +b0b4efc9a9ff4979126d3500b7fbec66409c81eed9bdf8cdbcf702020b7c0198079068e71210b0544d8ee5ef86690127a7cfb4d10a5198ab1ab11fbb12fe9aef diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc7.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc7.tar.gz/md5 new file mode 100644 index 0000000000000..643968a76b490 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc7.tar.gz/md5 @@ -0,0 +1 @@ +120c39da009b1ca681c4219579b5b58c diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc7.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc7.tar.gz/sha512 new file mode 100644 index 0000000000000..7463a39d2a6f7 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc7.tar.gz/sha512 @@ -0,0 +1 @@ +30fd877c00f6c87b3afc895d3c3a530152c5d87b28485f5fe4a4bde6ecf842be48e4c2574aa986ab496833adc526915533cdcef34e6a7b65a86774ceb090e0f1 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc8.tar.gz/md5 b/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc8.tar.gz/md5 new file mode 100644 index 0000000000000..12f72fcb9c07c --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc8.tar.gz/md5 @@ -0,0 +1 @@ +047afbc3576217fa486a2e7651e63fb2 diff --git a/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc8.tar.gz/sha512 b/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc8.tar.gz/sha512 new file mode 100644 index 0000000000000..11c6d0298e157 --- /dev/null +++ b/deps/checksums/LLVM.v8.0.1-3.x86_64-w64-mingw32-gcc8.tar.gz/sha512 @@ -0,0 +1 @@ +485cdb3d66d3f19631360bf20d528405112e82dfe4a023d8409e3309283e44c5cf239cfa5898cea1042e6464570e49934617fd35428d779d8c9f915c4e328628 diff --git a/deps/checksums/Pkg-04ab197340696404272c691135a7fba38b64f0e6.tar.gz/md5 b/deps/checksums/Pkg-04ab197340696404272c691135a7fba38b64f0e6.tar.gz/md5 deleted file mode 100644 index c1eb4e15504a9..0000000000000 --- a/deps/checksums/Pkg-04ab197340696404272c691135a7fba38b64f0e6.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -10c02e0018af6a4d7a668e40e5c02fb2 diff --git a/deps/checksums/Pkg-04ab197340696404272c691135a7fba38b64f0e6.tar.gz/sha512 b/deps/checksums/Pkg-04ab197340696404272c691135a7fba38b64f0e6.tar.gz/sha512 deleted file mode 100644 index 160dfbf68b108..0000000000000 --- a/deps/checksums/Pkg-04ab197340696404272c691135a7fba38b64f0e6.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -74ce17083cc4b88c37d62d88ca6ca496ae8a14c3e60454937d289f2df8259e06e0b0960cae9f1eb88a1f824245b55b95f801826817341fbec4c48603a4f227ea diff --git a/deps/checksums/Pkg-0c2dddd40e4d7492d2a7337be54c345011e5f1e1.tar.gz/md5 b/deps/checksums/Pkg-0c2dddd40e4d7492d2a7337be54c345011e5f1e1.tar.gz/md5 new file mode 100644 index 0000000000000..7247d735d18aa --- /dev/null +++ b/deps/checksums/Pkg-0c2dddd40e4d7492d2a7337be54c345011e5f1e1.tar.gz/md5 @@ -0,0 +1 @@ +490cd00c7c114e4f8561c18910770a64 diff --git a/deps/checksums/Pkg-0c2dddd40e4d7492d2a7337be54c345011e5f1e1.tar.gz/sha512 b/deps/checksums/Pkg-0c2dddd40e4d7492d2a7337be54c345011e5f1e1.tar.gz/sha512 new file mode 100644 index 0000000000000..48782a423e6ea --- /dev/null +++ b/deps/checksums/Pkg-0c2dddd40e4d7492d2a7337be54c345011e5f1e1.tar.gz/sha512 @@ -0,0 +1 @@ +d0aba2cc71ad036207e72b7491120e17633726e30128ab506961973def7f02a22a47e5664170aad3d8016e3019c0ed2195b78375b56abed80fba8cab8efac6fc diff --git a/deps/checksums/Statistics-a2203d3b67f7413701be5de251622cb85c9cc69d.tar.gz/md5 b/deps/checksums/Statistics-a2203d3b67f7413701be5de251622cb85c9cc69d.tar.gz/md5 new file mode 100644 index 0000000000000..be4e3dff40ea9 --- /dev/null +++ b/deps/checksums/Statistics-a2203d3b67f7413701be5de251622cb85c9cc69d.tar.gz/md5 @@ -0,0 +1 @@ +dcb3084255e7072157f8efb820988bb9 diff --git a/deps/checksums/Statistics-a2203d3b67f7413701be5de251622cb85c9cc69d.tar.gz/sha512 b/deps/checksums/Statistics-a2203d3b67f7413701be5de251622cb85c9cc69d.tar.gz/sha512 new file mode 100644 index 0000000000000..d629a20e5e56e --- /dev/null +++ b/deps/checksums/Statistics-a2203d3b67f7413701be5de251622cb85c9cc69d.tar.gz/sha512 @@ -0,0 +1 @@ +5f4d0a25915f066bed0c78270af61d63ec60514d5a3b9b08a2250fbbe2aea7046c730263e28dd257b9686d930d6dee814c0aa141b3d532a25a78fd7c58c0587f diff --git a/deps/checksums/Zlib.v1.2.11-3.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-3.aarch64-linux-gnu.tar.gz/md5 deleted file mode 100644 index 70d1c3dddc732..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.aarch64-linux-gnu.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -489c5ebbca174fdbfae86846e270e112 diff --git a/deps/checksums/Zlib.v1.2.11-3.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-3.aarch64-linux-gnu.tar.gz/sha512 deleted file mode 100644 index ea21f066afa19..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.aarch64-linux-gnu.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -da1bac181754711c50909492127f855c9acc2928951ce95310a6d32c03bb731d82fe2f0f97ae482c0da319b09d74b91aa143bd90b992489d386beee492d30e6f diff --git a/deps/checksums/Zlib.v1.2.11-3.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-3.aarch64-linux-musl.tar.gz/md5 deleted file mode 100644 index 66f7268085977..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.aarch64-linux-musl.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -e132449b6a56b91aa6db9e9b1c386517 diff --git a/deps/checksums/Zlib.v1.2.11-3.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-3.aarch64-linux-musl.tar.gz/sha512 deleted file mode 100644 index bdbb9b1f43896..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.aarch64-linux-musl.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -7faf95166293b65f0c348417520b590b89cffcce6f2be19684398c9d313393a8405aeabbde0855e770a06a858c8684f298ec0123b60c75ff187dbf735be871b1 diff --git a/deps/checksums/Zlib.v1.2.11-3.arm-linux-gnueabihf.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-3.arm-linux-gnueabihf.tar.gz/md5 deleted file mode 100644 index b7286d899a475..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.arm-linux-gnueabihf.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -d570463290c9ad88b480a88d593ec430 diff --git a/deps/checksums/Zlib.v1.2.11-3.arm-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-3.arm-linux-gnueabihf.tar.gz/sha512 deleted file mode 100644 index 7f7366e3a8a27..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.arm-linux-gnueabihf.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -d66c7b423723ecef0cd281ba143fb8a67b8730d30db25df8f9f1e4dc7e615bd0ea590b04e89c94fb6d5be9cc5017e537b03e3888d5a5f327d7e35694992669ed diff --git a/deps/checksums/Zlib.v1.2.11-3.arm-linux-musleabihf.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-3.arm-linux-musleabihf.tar.gz/md5 deleted file mode 100644 index 9ccaa87c29c20..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.arm-linux-musleabihf.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -11f531921e5311f55d98b7088ab1914a diff --git a/deps/checksums/Zlib.v1.2.11-3.arm-linux-musleabihf.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-3.arm-linux-musleabihf.tar.gz/sha512 deleted file mode 100644 index 28172a71a2cd1..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.arm-linux-musleabihf.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -ab871eaa4eb1f54d5ecf6675e8bef11976fe7fe2eb93a5ad3c918fa401f7fd1562ca09b592a86c4919627f9cf10650b40fb80d706a43d3e212c39985e819b862 diff --git a/deps/checksums/Zlib.v1.2.11-3.i686-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-3.i686-linux-gnu.tar.gz/md5 deleted file mode 100644 index f7133cba82080..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.i686-linux-gnu.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -83e87e8f40bf6ed602bcc47574cb25d6 diff --git a/deps/checksums/Zlib.v1.2.11-3.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-3.i686-linux-gnu.tar.gz/sha512 deleted file mode 100644 index 6d1a35d4649c1..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.i686-linux-gnu.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -801c1795ddadd8c6fd340a0854c3245e68cabb39225a75b747f674e466b70fa44afbdb9db01f45f165b56e62f9869dfd28072c4b8530f9c4eaa664baddb8b813 diff --git a/deps/checksums/Zlib.v1.2.11-3.i686-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-3.i686-linux-musl.tar.gz/md5 deleted file mode 100644 index e130f48be335b..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.i686-linux-musl.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -bdc858aa86b135f1bb25466a1d725a1f diff --git a/deps/checksums/Zlib.v1.2.11-3.i686-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-3.i686-linux-musl.tar.gz/sha512 deleted file mode 100644 index a185935190efb..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.i686-linux-musl.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -c1c6585f4e5a042de0d7d0de6d46149a4befd861c9664abdb049220d882c5e894b7a63483598656c5b9564bfccf01063d8689bda230f251e0dd519ab82494392 diff --git a/deps/checksums/Zlib.v1.2.11-3.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-3.i686-w64-mingw32.tar.gz/md5 deleted file mode 100644 index 9da96af35f2f1..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.i686-w64-mingw32.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -5c8b8ae739dfe3bb6e5c07908e760924 diff --git a/deps/checksums/Zlib.v1.2.11-3.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-3.i686-w64-mingw32.tar.gz/sha512 deleted file mode 100644 index 4379c979c43b1..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.i686-w64-mingw32.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -4e3c336333413a1916a281a107ab8d252837a6e3b80cdcbc855c1ef2400576a80cbc66bf6f58ca45209926a1347db9854928e161006557239d080496089a0e15 diff --git a/deps/checksums/Zlib.v1.2.11-3.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-3.powerpc64le-linux-gnu.tar.gz/md5 deleted file mode 100644 index 1f3e0a1cb9700..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.powerpc64le-linux-gnu.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -dcf64207e69f152de7ed27ff17990e92 diff --git a/deps/checksums/Zlib.v1.2.11-3.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-3.powerpc64le-linux-gnu.tar.gz/sha512 deleted file mode 100644 index f4fec87e67917..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.powerpc64le-linux-gnu.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -505d2a54063325ea88cd14c85f6caaa18c4f2bfa91ea4dbf8f4b461cdfca4a75424d198b1d54ae4e45505fec7ae20128c054dcef8a263c852ed6e3d0eebf62fb diff --git a/deps/checksums/Zlib.v1.2.11-3.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-3.x86_64-apple-darwin14.tar.gz/md5 deleted file mode 100644 index 3e32a268cf22f..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.x86_64-apple-darwin14.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -4bac35015bfc195e0a7e55baf40e8baa diff --git a/deps/checksums/Zlib.v1.2.11-3.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-3.x86_64-apple-darwin14.tar.gz/sha512 deleted file mode 100644 index 3bfd4dcb518c3..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.x86_64-apple-darwin14.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -60f959b1f77eeb5f8ed9e94e3ec300b0e23ea90feb67255956257aaddcb1b0ed65ed6d26deacb9b4b5337a757f5e4e2a7ef37d5534432975dbdcac36e63e33a9 diff --git a/deps/checksums/Zlib.v1.2.11-3.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-3.x86_64-linux-gnu.tar.gz/md5 deleted file mode 100644 index 54539bb945594..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.x86_64-linux-gnu.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -5e584cf6cbeac6c4c3d9157fb9cf1c8a diff --git a/deps/checksums/Zlib.v1.2.11-3.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-3.x86_64-linux-gnu.tar.gz/sha512 deleted file mode 100644 index a9c770c63a34d..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.x86_64-linux-gnu.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -2498c7dae82060c9c90dbd4e2d213ff49996d438abd1d47349ff5154fa1917cb082e10481e8eae61b047d29d014052b6fa7a647990eb16dc6a5111afd83bb155 diff --git a/deps/checksums/Zlib.v1.2.11-3.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-3.x86_64-linux-musl.tar.gz/md5 deleted file mode 100644 index 77b2a7f5a429d..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.x86_64-linux-musl.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -61d1dafed4ea486f317e332e84059b68 diff --git a/deps/checksums/Zlib.v1.2.11-3.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-3.x86_64-linux-musl.tar.gz/sha512 deleted file mode 100644 index aeb4cc492cac6..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.x86_64-linux-musl.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -3761830c2c67b2e30c484dba487fb7a35d2a8eb0cd64da726f2a73bb2ab789215db02c292b43832d4ad4cd22165a9e63a11b3c9c7710188faf5c3b6a0ee9ec09 diff --git a/deps/checksums/Zlib.v1.2.11-3.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-3.x86_64-unknown-freebsd11.1.tar.gz/md5 deleted file mode 100644 index 3245187096d8b..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.x86_64-unknown-freebsd11.1.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -3a88149d4bca1386d467e2317d483901 diff --git a/deps/checksums/Zlib.v1.2.11-3.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-3.x86_64-unknown-freebsd11.1.tar.gz/sha512 deleted file mode 100644 index 91d906ce541ac..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.x86_64-unknown-freebsd11.1.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -83d65ec48ed89dcdbc3fa3a70c3f859619ae980cdd457408fd4b9f01fbbecc6987a0156a2ab6dfe25c5a41a7b2a5647baa79a69d0b29ca50d0906270dd1effb6 diff --git a/deps/checksums/Zlib.v1.2.11-3.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-3.x86_64-w64-mingw32.tar.gz/md5 deleted file mode 100644 index e1241695979d2..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.x86_64-w64-mingw32.tar.gz/md5 +++ /dev/null @@ -1 +0,0 @@ -f4ace8b649c3fc186421c5fd67503e39 diff --git a/deps/checksums/Zlib.v1.2.11-3.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-3.x86_64-w64-mingw32.tar.gz/sha512 deleted file mode 100644 index b27f621fa870f..0000000000000 --- a/deps/checksums/Zlib.v1.2.11-3.x86_64-w64-mingw32.tar.gz/sha512 +++ /dev/null @@ -1 +0,0 @@ -04eefb7fe95725672db39e37a7b0c791456e0df6c58ac9e291bf9f02f50aac103e43879b1befe6ba3bf8ca11a1ff1ec4647ab40105594b17a0a5d67059b46670 diff --git a/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000000000..00da64ec9a6a0 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +9f518cd093311ea2c8ff624b06aaa4cd diff --git a/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000000000..e8006fef420c6 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +8ca001e4cdc0de7c3224b92b6e454102c511225757b1c54f32734c65eb11cd57cb2fffc50b7e4d12c9b9cb243bdf87f3b215a2fa3ce538336d1852820b241ebf diff --git a/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000000000..216075ed1144e --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +5157dfaf0a62241553783d38d6fa565c diff --git a/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000000000..631e470856d08 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.aarch64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +30199abf69bcee900087ae548de0303ae3ac5fa1fba24436ef406ad81675407eefb7157922e4701b2297d139eb8b4e3630a92c01b309de6b0205a98d60f2d63b diff --git a/deps/checksums/Zlib.v1.2.11-6.arm-linux-gnueabihf.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.arm-linux-gnueabihf.tar.gz/md5 new file mode 100644 index 0000000000000..4485a55d43f41 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.arm-linux-gnueabihf.tar.gz/md5 @@ -0,0 +1 @@ +2d42d6a3899b974f9c56c4fabfa34459 diff --git a/deps/checksums/Zlib.v1.2.11-6.arm-linux-gnueabihf.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.arm-linux-gnueabihf.tar.gz/sha512 new file mode 100644 index 0000000000000..31a1f61e0ff23 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.arm-linux-gnueabihf.tar.gz/sha512 @@ -0,0 +1 @@ +138254be838d7a9e356632785e5af30a562b498605e366e932011d9a41778e5a81c9073aa698529f95aa1101f2a7642e7ae81449f734d5e7bf0e3b4f26fedcae diff --git a/deps/checksums/Zlib.v1.2.11-6.arm-linux-musleabihf.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.arm-linux-musleabihf.tar.gz/md5 new file mode 100644 index 0000000000000..4dbcf6944a4a2 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.arm-linux-musleabihf.tar.gz/md5 @@ -0,0 +1 @@ +5db5f13979777caac31c830b66e75e8f diff --git a/deps/checksums/Zlib.v1.2.11-6.arm-linux-musleabihf.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.arm-linux-musleabihf.tar.gz/sha512 new file mode 100644 index 0000000000000..c47463b86564c --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.arm-linux-musleabihf.tar.gz/sha512 @@ -0,0 +1 @@ +6088b1a14ff372a2b7d28187edcfe85b613242b812ae23cbaea88da1e580105f38f65f70b2e80833711e2e99ac1683bdf451d55e5dc9283bb7e8dd582680acfb diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000000000..b5d6d15e0769a --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +46d03f5f9190cb787e867c3eb7edea78 diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000000000..008795089e144 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.i686-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +ca08a089148260c2b6d81a87a1e00a4e3a881c6d8bdad0d00b3e42a56cc91a7b72b542a748fc01a49ce207785053636587417916c6ec0d112fae49c55f34fb65 diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/md5 new file mode 100644 index 0000000000000..811bce75bde6b --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +34725ad0598db3cd7817e3578453f1d8 diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000000000..1936e8f8500e4 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.i686-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +b2706f68cecc21ab5013b07c226eda8fd1a68101bd17fcd79b991ac3b74d9b9e63e54f804a48a86ea448c5695c3284ac11c07592ae76a10b4fd6f5881c3ee277 diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000000000..2074ae57eb49f --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +89611cfd8f7c987de8b8c8cfd9eaeae6 diff --git a/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000000000..482a799d0b6a9 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.i686-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +128f0a2db6045c7e886dc283c93a3bff3f7bd64d2b2bcea0e4e67aac6c8e76ef9abcdac10cc56f5880ae32160ffe5ee4b42fdc9bc1fd3e07104776e16dc05aa0 diff --git a/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000000000..e7b88b4458ee3 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +1c32a314230b46db6eb02e72bd8b8b47 diff --git a/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000000000..9be88af9676b5 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.powerpc64le-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +69e785a356eec5c12cb4fe2d3b2a5b6ced16152d51e28c6e4eb646cad07a951d73cfcfd015c233f0164ed28d9ad772403230dcee581526fa4d34fefb95c2bb13 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/md5 new file mode 100644 index 0000000000000..5316272ba1928 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/md5 @@ -0,0 +1 @@ +0e14dd815045e47dd722ff55434428d1 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/sha512 new file mode 100644 index 0000000000000..7b21de4c41657 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-apple-darwin14.tar.gz/sha512 @@ -0,0 +1 @@ +4111e99386cab30de2f1c625079991576e97446c8a85bc37c05629ddf7b867bc32a74b94ed5552c2b057c4cf14f85cbc06b62459ed800cd9faa5a40382a6e958 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/md5 new file mode 100644 index 0000000000000..77123e338d051 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/md5 @@ -0,0 +1 @@ +9fcc23ef8cf1740418a147905f7aa45c diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/sha512 new file mode 100644 index 0000000000000..089693b3315bd --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-gnu.tar.gz/sha512 @@ -0,0 +1 @@ +2da223cb96bcd1845c8b84b44f779c8d464fd8dc21a8c8d7b065da07e7d995db627b9a2df1dc5247f70d1cd9e23f31c118288a0b3047884b19edd886e1dd6f2b diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/md5 new file mode 100644 index 0000000000000..538081546aba3 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/md5 @@ -0,0 +1 @@ +b3e690fe03ab5f57118ae2cff0e82f35 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/sha512 new file mode 100644 index 0000000000000..74786b8dcd156 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-linux-musl.tar.gz/sha512 @@ -0,0 +1 @@ +cea679067b995a5503922e81c50dda603b0b9634bb07b54073f74e2d0ccd638ec6c10ab26ae09c5a3566c44d4ee78419abc99f22da55c8131901aa72f790cc5c diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/md5 new file mode 100644 index 0000000000000..85a8c96a236b6 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/md5 @@ -0,0 +1 @@ +34a32be8fed7b26b3cef2f7c367241b8 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/sha512 new file mode 100644 index 0000000000000..5b033470ec838 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-unknown-freebsd11.1.tar.gz/sha512 @@ -0,0 +1 @@ +ba351d5415c86457b487e0078b8a86f2429e8c2aef8f3edb48647de8a2b7d05accf9582e8b58f49b46a67684b59c1bfa9ebda6ec93724747128c93f9efd69fa1 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/md5 b/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/md5 new file mode 100644 index 0000000000000..011042642da29 --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/md5 @@ -0,0 +1 @@ +66292bfea89453db4945639d454fa739 diff --git a/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/sha512 b/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/sha512 new file mode 100644 index 0000000000000..a9cdbe9cd278d --- /dev/null +++ b/deps/checksums/Zlib.v1.2.11-6.x86_64-w64-mingw32.tar.gz/sha512 @@ -0,0 +1 @@ +2f2ec74977e5a713c2336a0ef5f2553ec5393285ae3a6914ab8d9b535337ad36d9f07fc180df10db302d96dc6b9ca2d7d178a9f42b906276b97b597953dc4782 diff --git a/deps/checksums/cacert-2019-05-15.pem/md5 b/deps/checksums/cacert-2019-05-15.pem/md5 deleted file mode 100644 index 71ac52c69f597..0000000000000 --- a/deps/checksums/cacert-2019-05-15.pem/md5 +++ /dev/null @@ -1 +0,0 @@ -f4a13fcdd32ca18c3e62c7a728ebb378 diff --git a/deps/checksums/cacert-2019-05-15.pem/sha512 b/deps/checksums/cacert-2019-05-15.pem/sha512 deleted file mode 100644 index ea95dce0719bd..0000000000000 --- a/deps/checksums/cacert-2019-05-15.pem/sha512 +++ /dev/null @@ -1 +0,0 @@ -6dff0130bdc7c9b211d437598d6caf1b5bb7f7268ce66713e9701890f8924c98ab5a4c0df28dac4fdfea439ad61b46861d0c5b2986ac8c8b4a47218a2b9ba02f diff --git a/deps/checksums/cacert-2019-08-28.pem/md5 b/deps/checksums/cacert-2019-08-28.pem/md5 new file mode 100644 index 0000000000000..c2b816d1ecd7f --- /dev/null +++ b/deps/checksums/cacert-2019-08-28.pem/md5 @@ -0,0 +1 @@ +6c8779e5755d9dddf677bf7a52d035ce diff --git a/deps/checksums/cacert-2019-08-28.pem/sha512 b/deps/checksums/cacert-2019-08-28.pem/sha512 new file mode 100644 index 0000000000000..97b5e2963b7b4 --- /dev/null +++ b/deps/checksums/cacert-2019-08-28.pem/sha512 @@ -0,0 +1 @@ +527e23d1e83381583cc2efe4625b01a00baa990afc877bb617727e8bffab17a0dc4f36b60162bad375b576ff09bc27acc7e0f095a34150f23d61825b8a7fb81f diff --git a/deps/checksums/cacert-2019-10-16.pem/md5 b/deps/checksums/cacert-2019-10-16.pem/md5 new file mode 100644 index 0000000000000..5286d34293fa5 --- /dev/null +++ b/deps/checksums/cacert-2019-10-16.pem/md5 @@ -0,0 +1 @@ +5805059ab9e4646e4803ce1e007eb8ba diff --git a/deps/checksums/cacert-2019-10-16.pem/sha512 b/deps/checksums/cacert-2019-10-16.pem/sha512 new file mode 100644 index 0000000000000..e017d0fe86c31 --- /dev/null +++ b/deps/checksums/cacert-2019-10-16.pem/sha512 @@ -0,0 +1 @@ +49778472e46ce3b86b3930f4df5731ac86daf4d8602d418af1c89dc35df5f98c4557aa6c6eb280558c61139ead4b96cbb457a259f72640452f28a2fecd4ccb89 diff --git a/deps/checksums/llvm-8.0.1.src.tar.xz/md5 b/deps/checksums/llvm-8.0.1.src.tar.xz/md5 new file mode 100644 index 0000000000000..b76ea8119cb5e --- /dev/null +++ b/deps/checksums/llvm-8.0.1.src.tar.xz/md5 @@ -0,0 +1 @@ +9a3b63df01c52556f7afb5617934e79e diff --git a/deps/checksums/llvm-8.0.1.src.tar.xz/sha512 b/deps/checksums/llvm-8.0.1.src.tar.xz/sha512 new file mode 100644 index 0000000000000..bdc9a5e10793e --- /dev/null +++ b/deps/checksums/llvm-8.0.1.src.tar.xz/sha512 @@ -0,0 +1 @@ +82e120be5cabdfd5111aebbea68a663fe229c8861d73802d6ab09a3bf48f60de333e07e61f8fb61beaa14ac2bea24fcd74fa6f761acaf62469f536b79fcb1e16 diff --git a/deps/llvm-options.mk b/deps/llvm-options.mk index 14ae8208943ce..20e3114818b0e 100644 --- a/deps/llvm-options.mk +++ b/deps/llvm-options.mk @@ -20,6 +20,15 @@ LLVM_BUILDTYPE := $(LLVM_BUILDTYPE)+ASAN endif endif + +ifeq ($(LLVM_VER),svn) +LLVM_MONOSRC_DIR:=$(SRCCACHE)/llvm-project-$(LLVM_VER) +LLVM_SRC_DIR:=$(LLVM_MONOSRC_DIR)/llvm +LIBCXX_ROOT_DIR:=$(LLVM_MONOSRC_DIR) +else +LLVM_MONOSRC_DIR:= LLVM_SRC_DIR:=$(SRCCACHE)/llvm-$(LLVM_VER) +LIBCXX_ROOT_DIR:=$(LLVM_SRC_DIR)/projects +endif LLVM_BUILD_DIR:=$(BUILDDIR)/llvm-$(LLVM_VER) LLVM_BUILDDIR_withtype := $(LLVM_BUILD_DIR)/build_$(LLVM_BUILDTYPE) diff --git a/deps/llvm.mk b/deps/llvm.mk index 6d67d32ff40a0..186d0c7a91413 100644 --- a/deps/llvm.mk +++ b/deps/llvm.mk @@ -2,14 +2,7 @@ include $(SRCDIR)/llvm-ver.make ifneq ($(USE_BINARYBUILDER_LLVM), 1) -LLVM_GIT_URL_BASE ?= http://llvm.org/git -LLVM_GIT_URL_LLVM ?= $(LLVM_GIT_URL_BASE)/llvm.git -LLVM_GIT_URL_CLANG ?= $(LLVM_GIT_URL_BASE)/clang.git -LLVM_GIT_URL_COMPILER_RT ?= $(LLVM_GIT_URL_BASE)/compiler-rt.git -LLVM_GIT_URL_LLDB ?= $(LLVM_GIT_URL_BASE)/lldb.git -LLVM_GIT_URL_LIBCXX ?= $(LLVM_GIT_URL_BASE)/libcxx.git -LLVM_GIT_URL_LIBCXXABI ?= $(LLVM_GIT_URL_BASE)/libcxxabi.git -LLVM_GIT_URL_POLLY ?= $(LLVM_GIT_URL_BASE)/polly.git +LLVM_GIT_URL ?= https://github.com/llvm/llvm-project.git ifeq ($(BUILD_LLDB), 1) BUILD_LLVM_CLANG := 1 @@ -24,6 +17,18 @@ endif endif endif +# for Monorepo +LLVM_ENABLE_PROJECTS := +ifeq ($(BUILD_LLVM_CLANG), 1) +LLVM_ENABLE_PROJECTS := $(LLVM_ENABLE_PROJECTS);clang;compiler-rt +endif +ifeq ($(USE_POLLY), 1) +LLVM_ENABLE_PROJECTS := $(LLVM_ENABLE_PROJECTS);polly +endif +ifeq ($(BUILD_LLDB), 1) +LLVM_ENABLE_PROJECTS := $(LLVM_ENABLE_PROJECTS);lldb +endif + include $(SRCDIR)/llvm-options.mk LLVM_LIB_FILE := libLLVMCodeGen.a @@ -60,6 +65,11 @@ LLVM_CPPFLAGS := LLVM_LDFLAGS := LLVM_CMAKE := +# MONOREPO +ifeq ($(LLVM_VER),svn) +LLVM_CMAKE += -DLLVM_ENABLE_PROJECTS="$(LLVM_ENABLE_PROJECTS)" +endif + # Allow adding LLVM specific flags LLVM_CFLAGS += $(CFLAGS) LLVM_CXXFLAGS += $(CXXFLAGS) @@ -244,27 +254,17 @@ else BUILT_UNWIND := endif # Building libunwind -$(LLVM_SRC_DIR)/projects/libcxx: $(LLVM_LIBCXX_TAR) | $(LLVM_SRC_DIR)/source-extracted - ([ ! -d $@ ] && \ - git clone $(LLVM_GIT_URL_LIBCXX) $@ ) || \ - (cd $@ && \ - git pull --ff-only) -$(LLVM_SRC_DIR)/projects/libcxx/.git/HEAD: | $(LLVM_SRC_DIR)/projects/libcxx -$(LLVM_SRC_DIR)/projects/libcxxabi: $(LLVM_LIBCXXABI_TAR) | $(LLVM_SRC_DIR)/source-extracted - ([ ! -d $@ ] && \ - git clone $(LLVM_GIT_URL_LIBCXXABI) $@ ) || \ - (cd $@ && \ - git pull --ff-only) -$(LLVM_SRC_DIR)/projects/libcxxabi/.git/HEAD: | $(LLVM_SRC_DIR)/projects/libcxxabi -$(LLVM_BUILD_DIR)/libcxx-build/Makefile: | $(LLVM_SRC_DIR)/projects/libcxx $(LLVM_SRC_DIR)/projects/libcxxabi $(BUILT_UNWIND) +$(LIBCXX_ROOT_DIR)/libcxx: $(LLVM_LIBCXX_TAR) | $(LLVM_SRC_DIR)/source-extracted +$(LIBCXX_ROOT_DIR)/libcxxabi: $(LLVM_LIBCXXABI_TAR) | $(LLVM_SRC_DIR)/source-extracted +$(LLVM_BUILD_DIR)/libcxx-build/Makefile: | $(LIBCXX_ROOT_DIR)/libcxx $(LIBCXX_ROOT_DIR)/libcxxabi $(BUILT_UNWIND) mkdir -p $(dir $@) cd $(dir $@) && \ - $(CMAKE) -G "Unix Makefiles" $(CMAKE_COMMON) $(LLVM_CMAKE_LIBCXX) -DLIBCXX_CXX_ABI=libcxxabi -DLIBCXX_CXX_ABI_INCLUDE_PATHS="$(LLVM_SRC_DIR)/projects/libcxxabi/include" $(LLVM_SRC_DIR)/projects/libcxx -DCMAKE_SHARED_LINKER_FLAGS="$(LDFLAGS) -L$(build_libdir) $(LIBCXX_EXTRA_FLAGS)" -$(LLVM_BUILD_DIR)/libcxxabi-build/Makefile: | $(LLVM_SRC_DIR)/projects/libcxxabi $(LLVM_SRC_DIR)/projects/libcxx $(BUILT_UNWIND) + $(CMAKE) -G "Unix Makefiles" $(CMAKE_COMMON) $(LLVM_CMAKE_LIBCXX) -DLIBCXX_CXX_ABI=libcxxabi -DLIBCXX_CXX_ABI_INCLUDE_PATHS="$(LIBCXX_ROOT_DIR)/libcxxabi/include" $(LIBCXX_ROOT_DIR)/libcxx -DCMAKE_SHARED_LINKER_FLAGS="$(LDFLAGS) -L$(build_libdir) $(LIBCXX_EXTRA_FLAGS)" +$(LLVM_BUILD_DIR)/libcxxabi-build/Makefile: | $(LIBCXX_ROOT_DIR)/libcxxabi $(LIBCXX_ROOT_DIR)/libcxx $(BUILT_UNWIND) mkdir -p $(dir $@) cd $(dir $@) && \ - $(CMAKE) -G "Unix Makefiles" $(CMAKE_COMMON) $(LLVM_CMAKE_LIBCXX) -DLLVM_ABI_BREAKING_CHECKS="WITH_ASSERTS" -DLLVM_PATH="$(LLVM_SRC_DIR)" $(LLVM_SRC_DIR)/projects/libcxxabi -DLIBCXXABI_CXX_ABI_LIBRARIES="$(LIBCXX_EXTRA_FLAGS)" -DCMAKE_CXX_FLAGS="$(LLVM_CPPFLAGS) $(LLVM_CXXFLAGS) -std=c++11" -$(LLVM_BUILD_DIR)/libcxxabi-build/lib/libc++abi.so.1.0: $(LLVM_BUILD_DIR)/libcxxabi-build/Makefile $(LLVM_SRC_DIR)/projects/libcxxabi/.git/HEAD + $(CMAKE) -G "Unix Makefiles" $(CMAKE_COMMON) $(LLVM_CMAKE_LIBCXX) -DLLVM_ABI_BREAKING_CHECKS="WITH_ASSERTS" -DLLVM_PATH="$(LLVM_SRC_DIR)" $(LIBCXX_ROOT_DIR)/libcxxabi -DLIBCXXABI_CXX_ABI_LIBRARIES="$(LIBCXX_EXTRA_FLAGS)" -DCMAKE_CXX_FLAGS="$(LLVM_CPPFLAGS) $(LLVM_CXXFLAGS) -std=c++11" +$(LLVM_BUILD_DIR)/libcxxabi-build/lib/libc++abi.so.1.0: $(LLVM_BUILD_DIR)/libcxxabi-build/Makefile $(LIBCXX_ROOT_DIR)/libcxxabi/.git/HEAD $(MAKE) -C $(LLVM_BUILD_DIR)/libcxxabi-build touch -c $@ $(build_libdir)/libc++abi.so.1.0: $(LLVM_BUILD_DIR)/libcxxabi-build/lib/libc++abi.so.1.0 @@ -272,21 +272,22 @@ $(build_libdir)/libc++abi.so.1.0: $(LLVM_BUILD_DIR)/libcxxabi-build/lib/libc++ab touch -c $@ # Building this library installs these headers, which breaks other dependencies -rm -rf $(build_includedir)/c++ -$(LLVM_BUILD_DIR)/libcxx-build/lib/libc++.so.1.0: $(build_libdir)/libc++abi.so.1.0 $(LLVM_BUILD_DIR)/libcxx-build/Makefile $(LLVM_SRC_DIR)/projects/libcxx/.git/HEAD +$(LLVM_BUILD_DIR)/libcxx-build/lib/libc++.so.1.0: $(build_libdir)/libc++abi.so.1.0 $(LLVM_BUILD_DIR)/libcxx-build/Makefile $(LIBCXX_ROOT_DIR)/libcxx/.git/HEAD $(MAKE) -C $(LLVM_BUILD_DIR)/libcxx-build $(build_libdir)/libc++.so.1.0: $(LLVM_BUILD_DIR)/libcxx-build/lib/libc++.so.1.0 $(MAKE) -C $(LLVM_BUILD_DIR)/libcxx-build install touch -c $@ # Building this library installs these headers, which breaks other dependencies -rm -rf $(build_includedir)/c++ -get-libcxx: $(LLVM_SRC_DIR)/projects/libcxx -get-libcxxabi: $(LLVM_SRC_DIR)/projects/libcxxabi +get-libcxx: $(LIBCXX_ROOT_DIR)/libcxx +get-libcxxabi: $(LIBCXX_ROOT_DIR)/libcxxabi install-libcxxabi: $(build_libdir)/libc++abi.so.1.0 install-libcxx: $(build_libdir)/libc++.so.1.0 endif # BUILD_CUSTOM_LIBCXX # We want to be able to clean without having to pass BUILD_CUSTOM_LIBCXX=1, so define these -# outside of the conditional above +# outside of the conditional above, can't use `LIBCXX_ROOT_DIR` since that might come from +# the monorepo. clean-libcxx: -$(MAKE) -C $(LLVM_BUILD_DIR)/libcxx-build clean clean-libcxxabi: @@ -332,12 +333,12 @@ ifneq ($(LLVM_VER),svn) mkdir -p $(LLVM_SRC_DIR) $(TAR) -C $(LLVM_SRC_DIR) --strip-components 1 -xf $(LLVM_TAR) else - ([ ! -d $(LLVM_SRC_DIR) ] && \ - git clone $(LLVM_GIT_URL_LLVM) $(LLVM_SRC_DIR) ) || \ - (cd $(LLVM_SRC_DIR) && \ + ([ ! -d $(LLVM_MONOSRC_DIR) ] && \ + git clone $(LLVM_GIT_URL) $(LLVM_MONOSRC_DIR) ) || \ + (cd $(LLVM_MONOSRC_DIR) && \ git pull --ff-only) ifneq ($(LLVM_GIT_VER),) - (cd $(LLVM_SRC_DIR) && \ + (cd $(LLVM_MONOSRC_DIR) && \ git checkout $(LLVM_GIT_VER)) endif # LLVM_GIT_VER # Debug output only. Disable pager and ignore error. @@ -357,41 +358,6 @@ ifneq ($(LLVM_LLDB_TAR),) mkdir -p $(LLVM_SRC_DIR)/tools/lldb $(TAR) -C $(LLVM_SRC_DIR)/tools/lldb --strip-components 1 -xf $(LLVM_LLDB_TAR) endif # LLVM_LLDB_TAR -else # LLVM_VER -ifeq ($(BUILD_LLVM_CLANG),1) - ([ ! -d $(LLVM_SRC_DIR)/tools/clang ] && \ - git clone $(LLVM_GIT_URL_CLANG) $(LLVM_SRC_DIR)/tools/clang ) || \ - (cd $(LLVM_SRC_DIR)/tools/clang && \ - git pull --ff-only) - ([ ! -d $(LLVM_SRC_DIR)/projects/compiler-rt ] && \ - git clone $(LLVM_GIT_URL_COMPILER_RT) $(LLVM_SRC_DIR)/projects/compiler-rt ) || \ - (cd $(LLVM_SRC_DIR)/projects/compiler-rt && \ - git pull --ff-only) -ifneq ($(LLVM_GIT_VER_CLANG),) - (cd $(LLVM_SRC_DIR)/tools/clang && \ - git checkout $(LLVM_GIT_VER_CLANG)) -endif # LLVM_GIT_VER_CLANG -endif # BUILD_LLVM_CLANG -ifeq ($(BUILD_LLDB),1) - ([ ! -d $(LLVM_SRC_DIR)/tools/lldb ] && \ - git clone $(LLVM_GIT_URL_LLDB) $(LLVM_SRC_DIR)/tools/lldb ) || \ - (cd $(LLVM_SRC_DIR)/tools/lldb && \ - git pull --ff-only) -ifneq ($(LLVM_GIT_VER_LLDB),) - (cd $(LLVM_SRC_DIR)/tools/lldb && \ - git checkout $(LLVM_GIT_VER_LLDB)) -endif # LLVM_GIT_VER_CLANG -endif # BUILD_LLDB -ifeq ($(USE_POLLY),1) - ([ ! -d $(LLVM_SRC_DIR)/tools/polly ] && \ - git clone $(LLVM_GIT_URL_POLLY) $(LLVM_SRC_DIR)/tools/polly ) || \ - (cd $(LLVM_SRC_DIR)/tools/polly && \ - git pull --ff-only) -ifneq ($(LLVM_GIT_VER_POLLY),) - (cd $(LLVM_SRC_DIR)/tools/polly && \ - git checkout $(LLVM_GIT_VER_POLLY)) -endif # LLVM_GIT_VER_POLLY -endif # USE_POLLY endif # LLVM_VER # touch some extra files to ensure bisect works pretty well touch -c $(LLVM_SRC_DIR).extracted @@ -588,16 +554,11 @@ check-llvm: $(LLVM_BUILDDIR_withtype)/build-checked ifeq ($(LLVM_VER),svn) update-llvm: - (cd $(LLVM_SRC_DIR); git pull --ff-only) - ([ -d "$(LLVM_SRC_DIR)/tools/clang" ] || exit 0; cd $(LLVM_SRC_DIR)/tools/clang; git pull --ff-only) - ([ -d "$(LLVM_SRC_DIR)/projects/compiler-rt" ] || exit 0; cd $(LLVM_SRC_DIR)/projects/compiler-rt; git pull --ff-only) - ([ -d "$(LLVM_SRC_DIR)/tools/lldb" ] || exit 0; cd $(LLVM_SRC_DIR)/tools/lldb; git pull --ff-only) -ifeq ($(USE_POLLY),1) - ([ -d "$(LLVM_SRC_DIR)/tools/polly" ] || exit 0; cd $(LLVM_SRC_DIR)/tools/polly; git pull --ff-only) -endif + cd $(LLVM_MONOSRC_DIR) && \ + git pull --ff-only endif else # USE_BINARYBUILDER_LLVM -LLVM_BB_URL_BASE := https://github.com/JuliaPackaging/Yggdrasil/releases/download/LLVM-v$(LLVM_VER)-$(LLVM_BB_REL) +LLVM_BB_URL_BASE := https://github.com/staticfloat/LLVMBuilder/releases/download/v$(LLVM_VER)+$(LLVM_BB_REL) ifneq ($(BINARYBUILDER_LLVM_ASSERTS), 1) LLVM_BB_NAME := LLVM.v$(LLVM_VER) else diff --git a/deps/tools/bb-install.mk b/deps/tools/bb-install.mk index 4db7c5d029691..4b6ceb064fc34 100644 --- a/deps/tools/bb-install.mk +++ b/deps/tools/bb-install.mk @@ -1,3 +1,9 @@ +#$(call bb-install, \ +# 1 target, \ # name (lowercase) +# 2 variable, \ # name (uppercase) +# 3 gfortran, \ # signifies a GCC ABI (e.g. libgfortran version) dependency +# 4 cxx11) # signifies a cxx11 ABI dependency + # Auto-detect triplet once, create different versions that we use as defaults below for each BB install target # This is much more efficient than launching `gcc` and `python` once for each BB install target. BB_TRIPLET_GCCABI_CXXABI := $(shell python $(JULIAHOME)/contrib/normalize_triplet.py $(or $(XC_HOST),$(XC_HOST),$(BUILD_MACHINE)) "$(shell $(FC) --version | head -1)" "$(shell echo '\#include ' | $(CXX) $(CXXFLAGS) -x c++ -dM -E - | grep _GLIBCXX_USE_CXX11_ABI | awk '{ print $$3 }' )") @@ -6,7 +12,6 @@ BB_TRIPLET_CXXABI := $(subst $(SPACE),-,$(filter-out gcc%,$(subst -,$(SPACE),$(B BB_TRIPLET := $(subst $(SPACE),-,$(filter-out cxx%,$(filter-out gcc%,$(subst -,$(SPACE),$(BB_TRIPLET_GCCABI_CXXABI))))) define bb-install -# $(3) signifies a GCC ABI (e.g. libgfortran version) dependency, $(4) signifies a cxx11 ABI dependency TRIPLET_VAR := BB_TRIPLET ifeq ($(3),true) TRIPLET_VAR := $$(TRIPLET_VAR)_GCCABI @@ -26,20 +31,20 @@ $$(SRCCACHE)/$$($(2)_BB_BASENAME): | $$(SRCCACHE) stage-$(strip $1): $$(SRCCACHE)/$$($(2)_BB_BASENAME) install-$(strip $1): $$(build_prefix)/manifest/$(strip $1) -uninstall-$(strip $1): - -rm $$(build_prefix)/manifest/$(strip $1) - -cd $$(build_prefix) && rm -dv -- $$$$($(TAR) -tzf $$(SRCCACHE)/$$($(2)_BB_BASENAME) --exclude './$$$$') reinstall-$(strip $1): +$$(MAKE) uninstall-$(strip $1) +$$(MAKE) stage-$(strip $1) +$$(MAKE) install-$(strip $1) +UNINSTALL_$(strip $1) := $$($(2)_BB_BASENAME:.tar.gz=) bb-uninstaller + $$(build_prefix)/manifest/$(strip $1): $$(SRCCACHE)/$$($(2)_BB_BASENAME) | $(build_prefix)/manifest + -+[ ! -e $$@ ] || $$(MAKE) uninstall-$(strip $1) $$(JLCHECKSUM) $$< mkdir -p $$(build_prefix) $(UNTAR) $$< -C $$(build_prefix) - echo $2 > $$@ + echo '$$(UNINSTALL_$(strip $1))' > $$@ clean-bb-download-$(1): rm -f $$(SRCCACHE)/$$($(2)_BB_BASENAME) @@ -62,3 +67,9 @@ $$(error Attempting to use gcc4 $(2) tarball, but compiling with cxx11 string AB endif endif endef + +define bb-uninstaller +uninstall-$(strip $1): + -cd $$(build_prefix) && rm -fdv -- $$$$($$(TAR) -tzf $$(SRCCACHE)/$2.tar.gz --exclude './$$$$') + -rm $$(build_prefix)/manifest/$(strip $1) +endef diff --git a/deps/tools/common.mk b/deps/tools/common.mk index 87e3f31cacd3f..9af4966733df2 100644 --- a/deps/tools/common.mk +++ b/deps/tools/common.mk @@ -144,9 +144,6 @@ endef define staged-install stage-$(strip $1): $$(build_staging)/$2.tgz install-$(strip $1): $$(build_prefix)/manifest/$(strip $1) -uninstall-$(strip $1): - -rm $$(build_prefix)/manifest/$(strip $1) - -cd $$(build_prefix) && rm -dv -- $$$$($(TAR) -tzf $$(build_staging)/$2.tgz --exclude './$$$$') ifeq (exists, $$(shell [ -e $$(build_staging)/$2.tgz ] && echo exists )) # clean depends on uninstall only if the staged file exists @@ -171,11 +168,20 @@ $$(build_staging)/$2.tgz: $$(BUILDDIR)/$2/build-compiled rm -rf $$(build_staging)/$2 mv $$@.tmp $$@ +UNINSTALL_$(strip $1) := $2 staged-uninstaller + $$(build_prefix)/manifest/$(strip $1): $$(build_staging)/$2.tgz | $(build_prefix)/manifest + -+[ ! -e $$@ ] || $$(MAKE) uninstall-$(strip $1) mkdir -p $$(build_prefix) $(UNTAR) $$< -C $$(build_prefix) $6 - echo $2 > $$@ + echo '$$(UNINSTALL_$(strip $1))' > $$@ +endef + +define staged-uninstaller +uninstall-$(strip $1): + -cd $$(build_prefix) && rm -fdv -- $$$$($$(TAR) -tzf $$(build_staging)/$2.tgz --exclude './$$$$') + -rm $$(build_prefix)/manifest/$(strip $1) endef @@ -189,16 +195,11 @@ define symlink_install # (target-name, rel-from, abs-to) clean-$1: uninstall-$1 install-$1: $$(build_prefix)/manifest/$1 reinstall-$1: install-$1 -uninstall-$1: -ifeq ($$(BUILD_OS), WINNT) - -cmd //C rmdir $$(call mingw_to_dos,$3/$1,cd $3 &&) -else - -rm -r $3/$1 -endif - -rm $$(build_prefix)/manifest/$1 + +UNINSTALL_$(strip $1) := $2 symlink-uninstaller $3 $$(build_prefix)/manifest/$1: $$(BUILDDIR)/$2/build-compiled | $3 $$(build_prefix)/manifest - +[ ! \( -e $3/$1 -o -h $3/$1 \) ] || $$(MAKE) uninstall-$1 + -+[ ! \( -e $3/$1 -o -h $3/$1 \) ] || $$(MAKE) uninstall-$1 ifeq ($$(BUILD_OS), WINNT) cmd //C mklink //J $$(call mingw_to_dos,$3/$1,cd $3 &&) $$(call mingw_to_dos,$$(BUILDDIR)/$2,) else ifneq (,$$(findstring CYGWIN,$$(BUILD_OS))) @@ -208,7 +209,17 @@ else ifdef JULIA_VAGRANT_BUILD else ln -sf $$(abspath $$(BUILDDIR)/$2) $3/$1 endif - echo $2 > $$@ + echo '$$(UNINSTALL_$(strip $1))' > $$@ +endef + +define symlink-uninstaller +uninstall-$1: +ifeq ($$(BUILD_OS), WINNT) + -cmd //C rmdir $$(call mingw_to_dos,$3/$1,cd $3 &&) +else + -rm -r $3/$1 +endif + -rm $$(build_prefix)/manifest/$1 endef diff --git a/deps/tools/uninstallers.mk b/deps/tools/uninstallers.mk new file mode 100644 index 0000000000000..421626f3e6559 --- /dev/null +++ b/deps/tools/uninstallers.mk @@ -0,0 +1,31 @@ +# include after all other files: +# defines uninstallers and version-checks +# based on the contents of the UNINSTALL_* variables and the manifest files + +install: version-check +version-check: $(addprefix version-check-, $(DEP_LIBS_STAGED)) +uninstall: $(addprefix uninstall-, $(DEP_LIBS_STAGED)) + +## read 'uninstall-*' definition from either the manifest or the current session +define define-uninstaller +MANIFEST_$1 := $$(shell [ -e $$(build_prefix)/manifest/$1 ] && cat $$(build_prefix)/manifest/$1) +ifeq (undefined,$$(flavor $$(word 2,$$(MANIFEST_$1)))) +MANIFEST_$1 := $$(UNINSTALL_$1) +endif +UNINST_HOW_$1 := $$(word 2,$$(MANIFEST_$1)) +ifneq ($$(UNINST_HOW_$1),) +UNINST_WHO_$1 := $$(firstword $$(MANIFEST_$1)) +UNINST_WHERE_$1 := $$(wordlist 3,99,$$(MANIFEST_$1)) +$$(eval $$(call $$(UNINST_HOW_$1),$1,$$(UNINST_WHO_$1),$$(UNINST_WHERE_$1))) +endif +endef +$(foreach dep,$(DEP_LIBS_STAGED),$(eval $(call define-uninstaller,$(dep)))) + +# for each subproject with a manifest, keep the user aware if something is not the expected version +$(addprefix version-check-,$(DEP_LIBS_STAGED)) : version-check-% : install-% + @if [ ! -e $(build_prefix)/manifest/$* ] || ( \ + [ "1" != "`wc -w $(build_prefix)/manifest/$* | cut -f 1 -d ' '`" ] && \ + [ "$(UNINSTALL_$*)" != "`cat $(build_prefix)/manifest/$*`" ]) ; then \ + echo "WARNING: using mismatched version for $$(cat $(build_prefix)/manifest/$*):" ; \ + echo " want $(UNINSTALL_$*)" ; \ + fi diff --git a/doc/Manifest.toml b/doc/Manifest.toml index b392be3de1666..02381c6737de8 100644 --- a/doc/Manifest.toml +++ b/doc/Manifest.toml @@ -13,15 +13,15 @@ uuid = "8ba89e20-285c-5b6f-9357-94700520ee1b" [[DocStringExtensions]] deps = ["LibGit2", "Markdown", "Pkg", "Test"] -git-tree-sha1 = "0513f1a8991e9d83255e0140aace0d0fc4486600" +git-tree-sha1 = "88bb0edb352b16608036faadcc071adda068582a" uuid = "ffbed154-4ef7-542d-bbb7-c09d3a79fcae" -version = "0.8.0" +version = "0.8.1" [[Documenter]] -deps = ["Base64", "DocStringExtensions", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "REPL", "Test", "Unicode"] -git-tree-sha1 = "1dba3854d6b0e35b3ed77f84419efbaf81f28886" +deps = ["Base64", "Dates", "DocStringExtensions", "InteractiveUtils", "JSON", "LibGit2", "Logging", "Markdown", "REPL", "Test", "Unicode"] +git-tree-sha1 = "7f6ad432deb42aa108bc79205ee96d28724b084f" uuid = "e30172f5-a6a5-5a46-863b-614d45cd2de4" -version = "0.23.1" +version = "0.24.1" [[DocumenterLaTeX]] deps = ["Documenter", "Test"] @@ -54,9 +54,9 @@ uuid = "a63ad114-7e13-5084-954f-fe012c677804" [[Parsers]] deps = ["Dates", "Test"] -git-tree-sha1 = "db2b35dedab3c0e46dc15996d170af07a5ab91c9" +git-tree-sha1 = "0139ba59ce9bc680e2925aec5b7db79065d60556" uuid = "69de0a69-1ddd-5017-9359-2bf0b02dc9f0" -version = "0.3.6" +version = "0.3.10" [[Pkg]] deps = ["Dates", "LibGit2", "Markdown", "Printf", "REPL", "Random", "SHA", "UUIDs"] diff --git a/doc/README.md b/doc/README.md index 421bd6dd0ee75..c1541ddc7b747 100644 --- a/doc/README.md +++ b/doc/README.md @@ -1,6 +1,6 @@ # Julia Documentation README -Julia's documentation is written in Markdown. A reference of all supported syntax can be found in the [manual](https://docs.julialang.org/en/latest/manual/documentation/#Markdown-syntax-1). All documentation can be found in the Markdown files in `doc/src/` and the docstrings in Julia source files in `base/`. +Julia's documentation is written in Markdown. A reference of all supported syntax can be found in the [manual](https://docs.julialang.org/en/latest/stdlib/Markdown/). All documentation can be found in the Markdown files in `doc/src/` and the docstrings in Julia source files in `base/`. ## Requirements @@ -27,4 +27,3 @@ $ make -C doc doctest=true ``` from the root directory. - diff --git a/doc/make.jl b/doc/make.jl index a4d5e6793ca67..8ec30a198f518 100644 --- a/doc/make.jl +++ b/doc/make.jl @@ -172,6 +172,8 @@ else canonical = ("deploy" in ARGS) ? "https://docs.julialang.org/en/v1/" : nothing, assets = ["assets/julia-manual.css", ], analytics = "UA-28835595-6", + collapselevel = 1, + sidebar_sitename = false, ) end @@ -190,28 +192,23 @@ makedocs( pages = PAGES, ) -# Only deploy docs from 64bit Linux to avoid committing multiple versions of the same -# docs from different workers. -if "deploy" in ARGS && Sys.ARCH === :x86_64 && Sys.KERNEL === :Linux - -# Override a few environment variables to deploy to the appropriate repository, -# encode things like branch, whether it's built on a tag, etc.... -env_mappings = [ - "TRAVIS_REPO_SLUG" => "JuliaLang/docs.julialang.org", - "TRAVIS_BRANCH" => Base.GIT_VERSION_INFO.branch, -] - -if Base.GIT_VERSION_INFO.tagged_commit - push!(env_mappings, "TRAVIS_TAG" => "v$(Base.VERSION)") +# Define our own DeployConfig +struct BuildBotConfig <: Documenter.DeployConfig end +function Documenter.deploy_folder(::BuildBotConfig; devurl, kwargs...) + haskey(ENV, "DOCUMENTER_KEY") || return nothing + if Base.GIT_VERSION_INFO.tagged_commit + return "v$(Base.VERSION)" + elseif Base.GIT_VERSION_INFO.branch == "master" + return devurl + end + return nothing end -withenv(env_mappings...) do - deploydocs( - repo = "github.com/JuliaLang/docs.julialang.org.git", - target = joinpath(buildroot, "doc", "_build", "html", "en"), - dirname = "en", - devurl = "v1.4-dev", - versions = ["v#.#", "v1.4-dev" => "v1.4-dev"] - ) -end -end +deploydocs( + repo = "github.com/JuliaLang/docs.julialang.org.git", + deploy_config = BuildBotConfig(), + target = joinpath(buildroot, "doc", "_build", "html", "en"), + dirname = "en", + devurl = "v1.4-dev", + versions = ["v#.#", "v1.4-dev" => "v1.4-dev"] +) diff --git a/doc/src/assets/logo-dark.svg b/doc/src/assets/logo-dark.svg new file mode 100644 index 0000000000000..0acc3dc3211e1 --- /dev/null +++ b/doc/src/assets/logo-dark.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/doc/src/assets/logo.png b/doc/src/assets/logo.png deleted file mode 100644 index 0e4f86abdddd6..0000000000000 Binary files a/doc/src/assets/logo.png and /dev/null differ diff --git a/doc/src/assets/logo.svg b/doc/src/assets/logo.svg new file mode 100644 index 0000000000000..c608baf051599 --- /dev/null +++ b/doc/src/assets/logo.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/doc/src/base/c.md b/doc/src/base/c.md index df9143b8dc4f6..30f0e49fc8ae2 100644 --- a/doc/src/base/c.md +++ b/doc/src/base/c.md @@ -19,6 +19,7 @@ Base.unsafe_pointer_to_objref Base.disable_sigint Base.reenable_sigint Base.systemerror +Base.windowserror Core.Ptr Core.Ref Base.Cchar diff --git a/doc/src/base/iterators.md b/doc/src/base/iterators.md index 0b63561ebdc32..ccdd23e213e25 100644 --- a/doc/src/base/iterators.md +++ b/doc/src/base/iterators.md @@ -16,4 +16,5 @@ Base.Iterators.partition Base.Iterators.filter Base.Iterators.reverse Base.Iterators.only +Base.Iterators.peel ``` diff --git a/doc/src/base/math.md b/doc/src/base/math.md index 343c71f03c4b4..6947f6ecc5ea3 100644 --- a/doc/src/base/math.md +++ b/doc/src/base/math.md @@ -173,6 +173,33 @@ Base.invmod Base.powermod Base.ndigits Base.widemul +Base.Math.evalpoly Base.Math.@evalpoly Base.FastMath.@fastmath ``` + +## Customizable binary operators + +Some unicode characters can be used to define new binary operators +that support infix notation. +For example +```⊗(x,y) = kron(x,y)``` +defines the `⊗` (otimes) function to be the Kronecker product, +and one can call it as binary operator using infix syntax: +```C = A ⊗ B``` +as well as with the usual prefix syntax +```C = ⊗(A,B)```. + +Other characters that support such extensions include +\odot `⊙` +and +\oplus `⊕` + +The complete list is in the parser code: +https://github.com/JuliaLang/julia/blob/master/src/julia-parser.scm + +Those that are parsed like `*` (in terms of precedence) include +`* / ÷ % & ⋅ ∘ × |\\| ∩ ∧ ⊗ ⊘ ⊙ ⊚ ⊛ ⊠ ⊡ ⊓ ∗ ∙ ∤ ⅋ ≀ ⊼ ⋄ ⋆ ⋇ ⋉ ⋊ ⋋ ⋌ ⋏ ⋒ ⟑ ⦸ ⦼ ⦾ ⦿ ⧶ ⧷ ⨇ ⨰ ⨱ ⨲ ⨳ ⨴ ⨵ ⨶ ⨷ ⨸ ⨻ ⨼ ⨽ ⩀ ⩃ ⩄ ⩋ ⩍ ⩎ ⩑ ⩓ ⩕ ⩘ ⩚ ⩜ ⩞ ⩟ ⩠ ⫛ ⊍ ▷ ⨝ ⟕ ⟖ ⟗` +and those that are parsed like `+` include +`+ - |\|| ⊕ ⊖ ⊞ ⊟ |++| ∪ ∨ ⊔ ± ∓ ∔ ∸ ≏ ⊎ ⊻ ⊽ ⋎ ⋓ ⧺ ⧻ ⨈ ⨢ ⨣ ⨤ ⨥ ⨦ ⨧ ⨨ ⨩ ⨪ ⨫ ⨬ ⨭ ⨮ ⨹ ⨺ ⩁ ⩂ ⩅ ⩊ ⩌ ⩏ ⩐ ⩒ ⩔ ⩖ ⩗ ⩛ ⩝ ⩡ ⩢ ⩣` +There are many others that are related to arrows, comparisons, and powers. diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index 4f4311a9dbbab..da510f8e0da36 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -356,20 +356,20 @@ These symbols appear in the `head` field of [`Expr`](@ref)s in lowered form. * `args[2]` - A `call` expression that creates `SimpleVector` specifying its parameters + A `call` expression that creates a `SimpleVector` specifying its parameters * `args[3]` - A `call` expression that creates `SimpleVector` specifying its fieldnames + A `call` expression that creates a `SimpleVector` specifying its fieldnames * `args[4]` - A `Symbol` or `GlobalRef` specifying the supertype (e.g., `:Integer` or - `GlobalRef(Core, :Any)`) + A `Symbol`, `GlobalRef`, or `Expr` specifying the supertype (e.g., `:Integer`, + `GlobalRef(Core, :Any)`, or `:(Core.apply_type(AbstractArray, T, N))`) * `args[5]` - A `call` expression that creates `SimpleVector` specifying its fieldtypes + A `call` expression that creates a `SimpleVector` specifying its fieldtypes * `args[6]` @@ -503,7 +503,7 @@ These symbols appear in the `head` field of [`Expr`](@ref)s in lowered form. See [Working with LLVM](@ref Working-with-LLVM) for where these are derived from and how they get handled. -### Method +### [Method](@id ast-lowered-method) A unique'd container describing the shared metadata for a single method. diff --git a/doc/src/devdocs/debuggingtips.md b/doc/src/devdocs/debuggingtips.md index a5f4ce9f74806..faf75d38935c8 100644 --- a/doc/src/devdocs/debuggingtips.md +++ b/doc/src/devdocs/debuggingtips.md @@ -240,3 +240,10 @@ deterministically. The replayed execution's address spaces, register contents, are exactly the same in every run. A recent version of rr (3.1.0 or higher) is required. + +### Reproducing concurrency bugs with rr + +rr simulates a single-threaded machine by default. In order to debug concurrent +code you can use `rr record --chaos` which will cause rr to simulate between +one to eight cores, chosen randomly. You might therefore want to set `JULIA_NUM_THREADS=8` +and rerun your code under rr until you have caught your bug. diff --git a/doc/src/devdocs/eval.md b/doc/src/devdocs/eval.md index f91d3790769d4..e04035a3d49e6 100644 --- a/doc/src/devdocs/eval.md +++ b/doc/src/devdocs/eval.md @@ -71,6 +71,14 @@ which handles tokenizing Julia code and turning it into an AST, and [`julia-synt which handles transforming complex AST representations into simpler, "lowered" AST representations which are more suitable for analysis and execution. +If you want to test the parser without re-building Julia in its entirety, you can run the frontend +on its own as follows: + + $ cd src + $ flisp/flisp + > (load "jlfrontend.scm") + > (jl-parse-file "") + ## [Macro Expansion](@id dev-macro-expansion) When [`eval()`](@ref) encounters a macro, it expands that AST node before attempting to evaluate diff --git a/doc/src/devdocs/functions.md b/doc/src/devdocs/functions.md index 0273f229422be..48b0081f9b2b3 100644 --- a/doc/src/devdocs/functions.md +++ b/doc/src/devdocs/functions.md @@ -210,7 +210,7 @@ This function further unpacks each *element* of `other`, expecting each one to c Naturally, a more efficient implementation is available if all splatted arguments are named tuples. Notice that the original `circle` function is passed through, to handle closures. -## Compiler efficiency issues +## [Compiler efficiency issues](@id compiler-efficiency-issues) Generating a new type for every function has potentially serious consequences for compiler resource use when combined with Julia's "specialize on all arguments by default" design. Indeed, the initial diff --git a/doc/src/manual/arrays.md b/doc/src/manual/arrays.md index a369f5b029a11..f63c47551dcdc 100644 --- a/doc/src/manual/arrays.md +++ b/doc/src/manual/arrays.md @@ -66,7 +66,7 @@ omitted it will default to [`Float64`](@ref). | [`reinterpret(T, A)`](@ref) | an array with the same binary data as `A`, but with element type `T` | | [`rand(T, dims...)`](@ref) | an `Array` with random, iid [^1] and uniformly distributed values in the half-open interval ``[0, 1)`` | | [`randn(T, dims...)`](@ref) | an `Array` with random, iid and standard normally distributed values | -| [`Matrix{T}(I, m, n)`](@ref) | `m`-by-`n` identity matrix | +| [`Matrix{T}(I, m, n)`](@ref) | `m`-by-`n` identity matrix (requires `using LinearAlgebra`) | | [`range(start, stop=stop, length=n)`](@ref) | range of `n` linearly spaced elements from `start` to `stop` | | [`fill!(A, x)`](@ref) | fill the array `A` with the value `x` | | [`fill(x, dims...)`](@ref) | an `Array` filled with the value `x` | diff --git a/doc/src/manual/constructors.md b/doc/src/manual/constructors.md index 33f6d8fc44e1b..2a2c8dd4507bd 100644 --- a/doc/src/manual/constructors.md +++ b/doc/src/manual/constructors.md @@ -38,7 +38,7 @@ addresses all of these cases and more. is used to mean "constructor method" rather than "constructor function", especially as it is often used in the sense of singling out a particular method of the constructor from all of the others. -## Outer Constructor Methods +## [Outer Constructor Methods](@id man-outer-constructor-methods) A constructor is just like any other function in Julia in that its overall behavior is defined by the combined behavior of its methods. Accordingly, you can add functionality to a constructor @@ -71,7 +71,7 @@ become clear very shortly, additional constructor methods declared as normal met are called *outer* constructor methods. Outer constructor methods can only ever create a new instance by calling another constructor method, such as the automatically provided default ones. -## Inner Constructor Methods +## [Inner Constructor Methods](@id man-inner-constructor-methods) While outer constructor methods succeed in addressing the problem of providing additional convenience methods for constructing objects, they fail to address the other two use cases mentioned in the diff --git a/doc/src/manual/faq.md b/doc/src/manual/faq.md index 1b86e18986595..e3a94bdf9c1cb 100644 --- a/doc/src/manual/faq.md +++ b/doc/src/manual/faq.md @@ -352,6 +352,45 @@ julia> sqrt(-2.0+0im) 0.0 + 1.4142135623730951im ``` +### How can I constrain or compute type parameters? + +The parameters of a [parametric type](@ref Parametric-Types) can hold either +types or bits values, and the type itself chooses how it makes use of these parameters. +For example, `Array{Float64, 2}` is parameterized by the type `Float64` to express its +element type and the integer value `2` to express its number of dimensions. When +defining your own parametric type, you can use subtype constraints to declare that a +certain parameter must be a subtype ([`<:`](@ref)) of some abstract type or a previous +type parameter. There is not, however, a dedicated syntax to declare that a parameter +must be a _value_ of a given type — that is, you cannot directly declare that a +dimensionality-like parameter [`isa`](@ref) `Int` within the `struct` definition, for +example. Similarly, you cannot do computations (including simple things like addition +or subtraction) on type parameters. Instead, these sorts of constraints and +relationships may be expressed through additional type parameters that are computed +and enforced within the type's [constructors](@ref man-constructors). + +As an example, consider +```julia +struct ConstrainedType{T,N,N+1} # NOTE: INVALID SYNTAX + A::Array{T,N} + B::Array{T,N+1} +end +``` +where the user would like to enforce that the third type parameter is always the second plus one. This can be implemented with an explicit type parameter that is checked by an [inner constructor method](@ref man-inner-constructor-methods) (where it can be combined with other checks): +```julia +struct ConstrainedType{T,N,M} + A::Array{T,N} + B::Array{T,M} + function ConstrainedType(A::Array{T,N}, B::Array{T,M}) where {T,N,M} + N + 1 == M || throw(ArgumentError("second argument should have one more axis" )) + new{T,N,M}(A, B) + end +end +``` +This check is usually *costless*, as the compiler can elide the check for valid concrete types. If the second argument is also computed, it may be advantageous to provide an [outer constructor method](@ref man-outer-constructor-methods) that performs this calculation: +```julia +ConstrainedType(A) = ConstrainedType(A, compute_B(A)) +``` + ### [Why does Julia use native machine integer arithmetic?](@id faq-integer-arithmetic) Julia uses machine arithmetic for integer computations. This means that the range of `Int` values diff --git a/doc/src/manual/functions.md b/doc/src/manual/functions.md index 14879c32250c0..03fbcca918547 100644 --- a/doc/src/manual/functions.md +++ b/doc/src/manual/functions.md @@ -11,6 +11,9 @@ julia> function f(x,y) f (generic function with 1 method) ``` +This function accepts two arguments `x` and `y` and returns the value +of the last expression evaluated, which is `x + y`. + There is a second, more terse syntax for defining a function in Julia. The traditional function declaration syntax demonstrated above is equivalent to the following compact "assignment form": @@ -64,8 +67,9 @@ Python, Ruby and Perl, among other dynamic languages. The value returned by a function is the value of the last expression evaluated, which, by default, is the last expression in the body of the function definition. In the example function, `f`, from -the previous section this is the value of the expression `x + y`. As in C and most other imperative -or functional languages, the `return` keyword causes a function to return immediately, providing +the previous section this is the value of the expression `x + y`. +As an alternative, as in many other languages, +the `return` keyword causes a function to return immediately, providing an expression whose value is returned: ```julia @@ -125,7 +129,9 @@ There are three possible points of return from this function, returning the valu expressions, depending on the values of `x` and `y`. The `return` on the last line could be omitted since it is the last expression. -A return type can also be specified in the function declaration using the `::` operator. This converts +### Return type + +A return type can be specified in the function declaration using the `::` operator. This converts the return value to the specified type. ```jldoctest @@ -140,6 +146,30 @@ Int8 This function will always return an `Int8` regardless of the types of `x` and `y`. See [Type Declarations](@ref) for more on return types. +### Returning nothing + +For functions that do not need to return a value (functions used only for some side effects), +the Julia convention is to return the value [`nothing`](@ref): + +```julia +function printx(x) + println("x = $x") + return nothing +end +``` + +This is a *convention* in the sense that `nothing` is not a Julia keyword +but a only singleton object of type `Nothing`. +Also, you may notice that the `printx` function example above is contrived, +because `println` already returns `nothing`, so that the `return` line is redundant. + +There are two possible shortened forms for the `return nothing` expression. +On the one hand, the `return` keyword implicitly returns `nothing`, so it can be used alone. +On the other hand, since functions implicitly return their last expression evaluated, +`nothing` can be used alone when it's the last expression. +The preference for the expression `return nothing` as opposed to `return` or `nothing` +alone is a matter of coding style. + ## Operators Are Functions In Julia, most operators are just functions with support for special syntax. (The exceptions are diff --git a/doc/src/manual/interfaces.md b/doc/src/manual/interfaces.md index d5a53e93d6843..1232d9912a0ae 100644 --- a/doc/src/manual/interfaces.md +++ b/doc/src/manual/interfaces.md @@ -226,7 +226,7 @@ ourselves, we can officially define it as a subtype of an [`AbstractArray`](@ref | **Optional methods** | **Default definition** | **Brief description** | | `IndexStyle(::Type)` | `IndexCartesian()` | Returns either `IndexLinear()` or `IndexCartesian()`. See the description below. | | `getindex(A, I...)` | defined in terms of scalar `getindex` | [Multidimensional and nonscalar indexing](@ref man-array-indexing) | -| `setindex!(A, I...)` | defined in terms of scalar `setindex!` | [Multidimensional and nonscalar indexed assignment](@ref man-array-indexing) | +| `setindex!(A, X, I...)` | defined in terms of scalar `setindex!` | [Multidimensional and nonscalar indexed assignment](@ref man-array-indexing) | | `iterate` | defined in terms of scalar `getindex` | Iteration | | `length(A)` | `prod(size(A))` | Number of elements | | `similar(A)` | `similar(A, eltype(A), size(A))` | Return a mutable array with the same shape and element type | diff --git a/doc/src/manual/metaprogramming.md b/doc/src/manual/metaprogramming.md index 47f2a13c5e8ac..b16d9677f2e27 100644 --- a/doc/src/manual/metaprogramming.md +++ b/doc/src/manual/metaprogramming.md @@ -261,14 +261,14 @@ julia> x = :(1 + 2); julia> e = quote quote $x end end quote #= none:1 =# - $(Expr(:quote, quote - #= none:1 =# - $(Expr(:$, :x)) -end)) + quote + #= none:1 =# + $x + end end ``` -Notice that the result contains `Expr(:$, :x)`, which means that `x` has not been +Notice that the result contains `$x`, which means that `x` has not been evaluated yet. In other words, the `$` expression "belongs to" the inner quote expression, and so its argument is only evaluated when the inner quote expression is: @@ -289,14 +289,14 @@ This is done with multiple `$`s: julia> e = quote quote $$x end end quote #= none:1 =# - $(Expr(:quote, quote - #= none:1 =# - $(Expr(:$, :(1 + 2))) -end)) + quote + #= none:1 =# + $(1 + 2) + end end ``` -Notice that `:(1 + 2)` now appears in the result instead of the symbol `:x`. +Notice that `(1 + 2)` now appears in the result instead of the symbol `x`. Evaluating this expression yields an interpolated `3`: ```jldoctest interp1 @@ -553,7 +553,7 @@ julia> macro twostep(arg) @twostep (macro with 1 method) julia> ex = macroexpand(Main, :(@twostep :(1, 2, 3)) ); -I execute at parse time. The argument is: $(Expr(:quote, :((1, 2, 3)))) +I execute at parse time. The argument is: :((1, 2, 3)) ``` The first call to [`println`](@ref) is executed when [`macroexpand`](@ref) is called. The diff --git a/doc/src/manual/parallel-computing.md b/doc/src/manual/parallel-computing.md index 608cadc18f209..84279a7b3415a 100644 --- a/doc/src/manual/parallel-computing.md +++ b/doc/src/manual/parallel-computing.md @@ -218,7 +218,7 @@ too. # [Multi-Threading (Experimental)](@id man-multithreading) -In addition to tasks Julia forwards natively supports multi-threading. +In addition to tasks Julia natively supports multi-threading. Note that this section is experimental and the interfaces may change in the future. ## Setup @@ -234,13 +234,24 @@ julia> Threads.nthreads() The number of threads Julia starts up with is controlled by an environment variable called `JULIA_NUM_THREADS`. Now, let's start up Julia with 4 threads: +Bash on Linux/OSX: + ```bash export JULIA_NUM_THREADS=4 ``` -(The above command works on bourne shells on Linux and OSX. Note that if you're using a C shell -on these platforms, you should use the keyword `set` instead of `export`. If you're on Windows, -start up the command line in the location of `julia.exe` and use `set` instead of `export`.) +C shell on Linux/OSX, CMD on Windows: + +```bash +set JULIA_NUM_THREADS=4 +``` + +Powershell on Windows: + +```powershell +$env:JULIA_NUM_THREADS=4 +``` + Let's verify there are 4 threads at our disposal. @@ -758,7 +769,8 @@ It is automatically made available on the worker processes. Note that workers do not run a `~/.julia/config/startup.jl` startup script, nor do they synchronize their global state (such as global variables, new method definitions, and loaded modules) with any -of the other running processes. +of the other running processes. You may use `addprocs(exeflags="--project")` to initialize a worker with +a particular environment, and then `@everywhere using ` or `@everywhere include("file.jl")`. Other types of clusters can be supported by writing your own custom `ClusterManager`, as described below in the [ClusterManagers](@ref) section. @@ -1280,7 +1292,7 @@ julia> addprocs(3) julia> @everywhere using SharedArrays -julia> S = SharedArray{Int,2}((3,4), init = S -> S[localindices(S)] = myid()) +julia> S = SharedArray{Int,2}((3,4), init = S -> S[localindices(S)] = repeat([myid()], length(localindices(S)))) 3×4 SharedArray{Int64,2}: 2 2 3 4 2 3 3 4 @@ -1301,7 +1313,7 @@ convenient for splitting up tasks among processes. You can, of course, divide th you wish: ```julia-repl -julia> S = SharedArray{Int,2}((3,4), init = S -> S[indexpids(S):length(procs(S)):length(S)] = myid()) +julia> S = SharedArray{Int,2}((3,4), init = S -> S[indexpids(S):length(procs(S)):length(S)] = repeat([myid()], length( indexpids(S):length(procs(S)):length(S)))) 3×4 SharedArray{Int64,2}: 2 2 2 2 3 3 3 3 diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index 07c7ad0422d1d..1fdaedb2d2f85 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -481,6 +481,65 @@ c = (b + 1.0f0)::Complex{T} does not hinder performance (but does not help either) since the compiler can determine the type of `c` at the time `k` is compiled. +### Be aware of when Julia avoids specializing + +As a heuristic, Julia avoids automatically specializing on argument type parameters in three +specific cases: `Type`, `Function`, and `Vararg`. Julia will always specialize when the argument is +used within the method, but not if the argument is just passed through to another function. This +usually has no performance impact at runtime and +[improves compiler performance](@ref compiler-efficiency-issues). If you find it does have a +performance impact at runtime in your case, you can trigger specialization by adding a type +parameter to the method declaration. Here are some examples: + +This will not specialize: + +```julia +function f_type(t) # or t::Type + x = ones(t, 10) + return sum(map(sin, x)) +end +``` + +but this will: + +```julia +function g_type(t::Type{T}) where T + x = ones(T, 10) + return sum(map(sin, x)) +end +``` + +These will not specialize: + +```julia +f_func(f, num) = ntuple(f, div(num, 2)) +g_func(g::Function, num) = ntuple(g, div(num, 2)) +``` + +but this will: + +```julia +h_func(h::H, num) where {H} = ntuple(h, div(num, 2)) +``` + +This will not specialize: + +```julia +f_vararg(x::Int...) = tuple(x...) +``` + +but this will: + +```julia +g_vararg(x::Vararg{Int, N}) where {N} = tuple(x...) +``` + +Note that [`@code_typed`](@ref) and friends will always show you specialized code, even if Julia +would not normally specialize that method call. You need to check the +[method internals](@ref ast-lowered-method) if you want to see whether specializations are generated +when argument types are changed, i.e., if `(@which f(...)).specializations` contains specializations +for the argument in question. + ## Break functions into multiple definitions Writing a function as many small definitions allows the compiler to directly call the most applicable diff --git a/doc/src/manual/style-guide.md b/doc/src/manual/style-guide.md index a39697d124456..e9d21a48f7c94 100644 --- a/doc/src/manual/style-guide.md +++ b/doc/src/manual/style-guide.md @@ -176,7 +176,7 @@ as applicable: 7. **Value**. For associative collections, this is the value of the key-value pair(s). - In cases like `fill!(x, v)`, this is `v`. + In cases like [`fill!(x, v)`](@ref fill!), this is `v`. 8. **Everything else**. Any other arguments. diff --git a/src/Makefile b/src/Makefile index d8f185257dcc1..7a9dd723351ac 100644 --- a/src/Makefile +++ b/src/Makefile @@ -44,9 +44,9 @@ RUNTIME_C_SRCS := \ dlload sys init task array dump staticdata toplevel jl_uv datatype \ simplevector runtime_intrinsics precompile \ threading partr stackwalk gc gc-debug gc-pages gc-stacks method \ - jlapi signal-handling safepoint jloptions timing subtype \ + jlapi signal-handling safepoint timing subtype \ crc32c -RUNTIME_SRCS := APInt-C runtime_ccall processor rtutils $(RUNTIME_C_SRCS) +RUNTIME_SRCS := APInt-C runtime_ccall processor rtutils jloptions $(RUNTIME_C_SRCS) SRCS := $(RUNTIME_SRCS) ifeq ($(USEMSVC), 1) @@ -108,6 +108,10 @@ FLAGS += -DLLVM_SHLIB endif # USE_LLVM_SHLIB == 1 endif +CLANGLINK := +CLANGLINK += -lclangAnalysis -lclangStaticAnalyzerCore -lclangASTMatchers -lclangAST -lclangLex -lclangBasic +CLANGLINK += $(LLVMLINK) + COMMON_LIBS := -L$(build_shlibdir) -L$(build_libdir) $(LIBUV) $(LIBUTF8PROC) $(NO_WHOLE_ARCHIVE) $(LLVMLINK) $(OSLIBS) $(LIBUNWIND) DEBUG_LIBS := $(WHOLE_ARCHIVE) $(BUILDDIR)/flisp/libflisp-debug.a $(WHOLE_ARCHIVE) $(BUILDDIR)/support/libsupport-debug.a $(COMMON_LIBS) RELEASE_LIBS := $(WHOLE_ARCHIVE) $(BUILDDIR)/flisp/libflisp.a $(WHOLE_ARCHIVE) $(BUILDDIR)/support/libsupport.a $(COMMON_LIBS) @@ -343,8 +347,8 @@ clean-support: cleanall: clean clean-flisp clean-support clean-analyzegc -$(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT): $(SRCDIR)/clangsa/GCChecker.cpp - @$(call PRINT_CC, $(CXX) -g $(fPIC) -shared -o $@ -DCLANG_PLUGIN -I$(build_includedir) $(shell $(LLVM_CONFIG_HOST) --cxxflags) $(CPPFLAGS) $(CFLAGS) $< $(shell $(LLVM_CONFIG_HOST) --ldflags) $(LDFLAGS) -L$(build_libdir) -lclangAnalysis -lclangStaticAnalyzerCore -lclangASTMatchers -lclangAST -lclangLex -lclangBasic) +$(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT): $(SRCDIR)/clangsa/GCChecker.cpp $(LLVM_CONFIG_ABSOLUTE) + @$(call PRINT_CC, $(CXX) -g $(fPIC) -shared -o $@ -DCLANG_PLUGIN -I$(build_includedir) $(shell $(LLVM_CONFIG_HOST) --cxxflags) $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) $(CXXLDFLAGS) -L$(build_libdir) $< $(CLANGLINK)) # Throw an error if a proper version of `clang` is not available. # Note that for a default install, you will need to have run the following @@ -354,24 +358,22 @@ $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT): $(SRCDIR)/clangsa/GCChecker.c analyzegc-deps-check: $(BUILDDIR)/julia_version.h $(BUILDDIR)/julia_flisp.boot.inc ifeq ($(USE_BINARYBUILDER_LLVM),0) ifneq ($(BUILD_LLVM_CLANG),1) - $(error Clang must be available to use the clang analyzer. Either build it (BUILD_LLVM_CLANG) or use BinaryBuilder) + $(error Clang must be available to use the clang analyzer. Either build it (BUILD_LLVM_CLANG=1) or use BinaryBuilder) endif endif -define CLANG_ANALYZE -clang-sa-$(1): $$(build_shlibdir)/libGCCheckerPlugin.$$(SHLIB_EXT) | analyzegc-deps-check - @$$(call PRINT_ANALYZE, $$(build_depsbindir)/clang --analyze -Xanalyzer -analyzer-werror -Xanalyzer -analyzer-output=text -Xclang -load -Xclang $$(build_shlibdir)/libGCCheckerPlugin.$$(SHLIB_EXT) $$(CPPFLAGS) $$(CFLAGS) $$(DEBUGFLAGS) -Xclang -analyzer-checker=core$$(COMMA)julia.GCChecker --analyzer-no-default-checks -fcolor-diagnostics -Werror -x c $$(SRCDIR)/$(1).c) -.PHONY: clang-sa-$(1) - -# Add this as a target of `analyzegc` -analyzegc: clang-sa-$(1) -endef +CLANGSA_FLAGS := +ifeq ($(OS), Darwin) # on new XCode, the files are hidden +CLANGSA_FLAGS += -isysroot $(shell xcode-select -p)/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk +endif +clang-sa-%: $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) | analyzegc-deps-check + @$(call PRINT_ANALYZE, $(build_depsbindir)/clang --analyze -Xanalyzer -analyzer-werror -Xanalyzer -analyzer-output=text -Xclang -load -Xclang $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) $(CLANGSA_FLAGS) $(CPPFLAGS) $(CFLAGS) $(DEBUGFLAGS) -Xclang -analyzer-checker=core$(COMMA)julia.GCChecker --analyzer-no-default-checks -fcolor-diagnostics -Werror -x c $(SRCDIR)/$*.c) -# Build a Makefile target for each file we want to analyze -$(foreach S,$(RUNTIME_C_SRCS),$(eval $(call CLANG_ANALYZE,$(S)))) +# Add C files as a target of `analyzegc` +analyzegc: $(addprefix clang-sa-,$(RUNTIME_C_SRCS)) clean-analyzegc: rm -f $(build_shlibdir)/libGCCheckerPlugin.$(SHLIB_EXT) -.PHONY: default all debug release clean cleanall clean-* libccalltest libllvmcalltest julia_flisp.boot.inc.phony analyzegc +.PHONY: default all debug release clean cleanall clean-* libccalltest libllvmcalltest julia_flisp.boot.inc.phony analyzegc clang-sa-* diff --git a/src/array.c b/src/array.c index a0627ff24ab8e..c38b9953e0751 100644 --- a/src/array.c +++ b/src/array.c @@ -954,12 +954,12 @@ STATIC_INLINE void jl_array_shrink(jl_array_t *a, size_t dec) char *typetagdata; char *newtypetagdata; if (isbitsunion) { - typetagdata = (char*)malloc(a->nrows); + typetagdata = (char*)malloc_s(a->nrows); memcpy(typetagdata, jl_array_typetagdata(a), a->nrows); } size_t oldoffsnb = a->offset * elsz; - a->data = ((char*) jl_gc_managed_realloc(originalptr, newbytes, oldnbytes, - a->flags.isaligned, (jl_value_t*) a)) + oldoffsnb; + a->data = ((char*)jl_gc_managed_realloc(originalptr, newbytes, oldnbytes, + a->flags.isaligned, (jl_value_t*) a)) + oldoffsnb; a->maxsize -= dec; if (isbitsunion) { newtypetagdata = jl_array_typetagdata(a); diff --git a/src/ast.scm b/src/ast.scm index 997135da7d042..5df41a117b8b3 100644 --- a/src/ast.scm +++ b/src/ast.scm @@ -406,15 +406,17 @@ e) (define (vararg? x) (and (pair? x) (eq? (car x) '...))) -(define (varargexpr? x) (and - (pair? x) - (eq? (car x) '::) - (or - (eq? (caddr x) 'Vararg) - (and - (pair? (caddr x)) - (length> (caddr x) 1) - (eq? (cadr (caddr x)) 'Vararg))))) +(define (vararg-type-expr? x) + (or (eq? x 'Vararg) + (and (length> x 1) + (or (and (eq? (car x) 'curly) + (vararg-type-expr? (cadr x))) + (and (eq? (car x) 'where) + (vararg-type-expr? (cadr x))))))) +(define (varargexpr? x) + (and (pair? x) + (eq? (car x) '::) + (vararg-type-expr? (caddr x)))) (define (linenum? x) (and (pair? x) (eq? (car x) 'line))) (define (make-assignment l r) `(= ,l ,r)) diff --git a/src/builtin_proto.h b/src/builtin_proto.h index ca3096d3a57e0..85762ce2f9ffa 100644 --- a/src/builtin_proto.h +++ b/src/builtin_proto.h @@ -23,7 +23,7 @@ DECLARE_BUILTIN(throw); DECLARE_BUILTIN(is); DECLARE_BUILTIN(typeof); DECLARE_BUILTIN(sizeof); DECLARE_BUILTIN(issubtype); DECLARE_BUILTIN(isa); DECLARE_BUILTIN(_apply); DECLARE_BUILTIN(_apply_pure); -DECLARE_BUILTIN(_apply_latest); +DECLARE_BUILTIN(_apply_latest); DECLARE_BUILTIN(_apply_iterate); DECLARE_BUILTIN(isdefined); DECLARE_BUILTIN(nfields); DECLARE_BUILTIN(tuple); DECLARE_BUILTIN(svec); DECLARE_BUILTIN(getfield); DECLARE_BUILTIN(setfield); diff --git a/src/builtins.c b/src/builtins.c index 69ce064badd0e..2deab966aca08 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -474,9 +474,8 @@ void STATIC_INLINE _grow_to(jl_value_t **root, jl_value_t ***oldargs, jl_svec_t static jl_function_t *jl_iterate_func JL_GLOBALLY_ROOTED; -JL_CALLABLE(jl_f__apply) +static jl_value_t *do_apply(jl_value_t *F, jl_value_t **args, uint32_t nargs, jl_value_t *iterate) { - JL_NARGSV(apply, 1); jl_function_t *f = args[0]; if (nargs == 2) { // some common simple cases @@ -516,10 +515,13 @@ JL_CALLABLE(jl_f__apply) extra += 1; } } - if (extra && jl_iterate_func == NULL) { - jl_iterate_func = jl_get_function(jl_top_module, "iterate"); - if (jl_iterate_func == NULL) - jl_undefined_var_error(jl_symbol("iterate")); + if (extra && iterate == NULL) { + if (jl_iterate_func == NULL) { + jl_iterate_func = jl_get_function(jl_top_module, "iterate"); + if (jl_iterate_func == NULL) + jl_undefined_var_error(jl_symbol("iterate")); + } + iterate = jl_iterate_func; } // allocate space for the argument array and gc roots for it // based on our previous estimates @@ -531,7 +533,7 @@ JL_CALLABLE(jl_f__apply) size_t n_alloc; jl_value_t **roots; JL_GC_PUSHARGS(roots, stackalloc + (extra ? 2 : 0)); - jl_value_t **newargs = NULL; + jl_value_t **newargs; jl_svec_t *arg_heap = NULL; if (onstack) { newargs = roots; @@ -539,7 +541,9 @@ JL_CALLABLE(jl_f__apply) } else { // put arguments on the heap if there are too many + newargs = NULL; n_alloc = 0; + assert(precount > 0); // let optimizer know this won't overflow _grow_to(&roots[0], &newargs, &arg_heap, &n_alloc, precount, extra); } newargs[0] = f; @@ -552,6 +556,7 @@ JL_CALLABLE(jl_f__apply) size_t j, al = jl_svec_len(t); precount = (precount > al) ? precount - al : 0; _grow_to(&roots[0], &newargs, &arg_heap, &n_alloc, n + precount + al, extra); + assert(newargs != NULL); // inform GCChecker that we didn't write a NULL here for (j = 0; j < al; j++) { newargs[n++] = jl_svecref(t, j); // GC Note: here we assume that the return value of `jl_svecref` @@ -564,6 +569,7 @@ JL_CALLABLE(jl_f__apply) size_t j, al = jl_nfields(ai); precount = (precount > al) ? precount - al : 0; _grow_to(&roots[0], &newargs, &arg_heap, &n_alloc, n + precount + al, extra); + assert(newargs != NULL); // inform GCChecker that we didn't write a NULL here for (j = 0; j < al; j++) { // jl_fieldref may allocate. newargs[n++] = jl_fieldref(ai, j); @@ -576,6 +582,7 @@ JL_CALLABLE(jl_f__apply) size_t j, al = jl_array_len(aai); precount = (precount > al) ? precount - al : 0; _grow_to(&roots[0], &newargs, &arg_heap, &n_alloc, n + precount + al, extra); + assert(newargs != NULL); // inform GCChecker that we didn't write a NULL here if (aai->flags.ptrarray) { for (j = 0; j < al; j++) { jl_value_t *arg = jl_array_ptr_ref(aai, j); @@ -599,7 +606,7 @@ JL_CALLABLE(jl_f__apply) assert(extra > 0); jl_value_t *args[2]; args[0] = ai; - jl_value_t *next = jl_apply_generic(jl_iterate_func, args, 1); + jl_value_t *next = jl_apply_generic(iterate, args, 1); while (next != jl_nothing) { roots[stackalloc] = next; jl_value_t *value = jl_get_nth_field_checked(next, 0); @@ -614,7 +621,7 @@ JL_CALLABLE(jl_f__apply) roots[stackalloc + 1] = NULL; JL_GC_ASSERT_LIVE(state); args[1] = state; - next = jl_apply_generic(jl_iterate_func, args, 2); + next = jl_apply_generic(iterate, args, 2); } roots[stackalloc] = NULL; extra -= 1; @@ -629,6 +636,18 @@ JL_CALLABLE(jl_f__apply) return result; } +JL_CALLABLE(jl_f__apply_iterate) +{ + JL_NARGSV(_apply_iterate, 2); + return do_apply(F, args+1, nargs-1, args[0]); +} + +JL_CALLABLE(jl_f__apply) +{ + JL_NARGSV(_apply, 1); + return do_apply(F, args, nargs, NULL); +} + // this is like `_apply`, but with quasi-exact checks to make sure it is pure JL_CALLABLE(jl_f__apply_pure) { @@ -1301,6 +1320,7 @@ void jl_init_primitives(void) JL_GC_DISABLED // internal functions jl_builtin_apply_type = add_builtin_func("apply_type", jl_f_apply_type); jl_builtin__apply = add_builtin_func("_apply", jl_f__apply); + jl_builtin__apply_iterate = add_builtin_func("_apply_iterate", jl_f__apply_iterate); jl_builtin__expr = add_builtin_func("_expr", jl_f__expr); jl_builtin_svec = add_builtin_func("svec", jl_f_svec); add_builtin_func("_apply_pure", jl_f__apply_pure); diff --git a/src/ccall.cpp b/src/ccall.cpp index a19e3e092dd37..422b5c80dbdc8 100644 --- a/src/ccall.cpp +++ b/src/ccall.cpp @@ -672,7 +672,8 @@ static jl_cgval_t emit_cglobal(jl_codectx_t &ctx, jl_value_t **args, size_t narg else { rt = (jl_value_t*)jl_voidpointer_type; } - Type *lrt = julia_type_to_llvm(rt); + Type *lrt = T_size; + assert(lrt == julia_type_to_llvm(rt)); interpret_symbol_arg(ctx, sym, args[1], "cglobal", false); @@ -917,10 +918,7 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar jl_value_t *tti = jl_svecref(tt,i); bool toboxed; Type *t = julia_type_to_llvm(tti, &toboxed); - if (toboxed) - argtypes.push_back(T_prjlvalue); - else - argtypes.push_back(t); + argtypes.push_back(t); if (4 + i > nargs) { jl_error("Missing arguments to llvmcall!"); } @@ -935,8 +933,6 @@ static jl_cgval_t emit_llvmcall(jl_codectx_t &ctx, jl_value_t **args, size_t nar Function *f; bool retboxed; Type *rettype = julia_type_to_llvm(rtt, &retboxed); - if (retboxed) - rettype = T_prjlvalue; if (isString) { // Make sure to find a unique name std::string ir_name; @@ -1193,9 +1189,7 @@ std::string generate_func_sig(const char *fname) } } - t = julia_struct_to_llvm(tti, unionall_env, &isboxed); - if (isboxed) - t = T_prjlvalue; + t = julia_struct_to_llvm(tti, unionall_env, &isboxed, llvmcall); if (t == NULL || t == T_void) { return make_errmsg(fname, i + 1, " doesn't correspond to a C type"); } @@ -1326,7 +1320,7 @@ static bool verify_ref_type(jl_codectx_t &ctx, jl_value_t* ref, jl_unionall_t *u static const std::string verify_ccall_sig(jl_value_t *&rt, jl_value_t *at, jl_unionall_t *unionall_env, jl_svec_t *sparam_vals, - Type *&lrt, bool &retboxed, bool &static_rt) + Type *&lrt, bool &retboxed, bool &static_rt, bool llvmcall=false) { JL_TYPECHK(ccall, type, rt); JL_TYPECHK(ccall, simplevector, at); @@ -1336,11 +1330,9 @@ static const std::string verify_ccall_sig(jl_value_t *&rt, jl_value_t *at, rt = (jl_value_t*)jl_any_type; } - lrt = julia_struct_to_llvm(rt, unionall_env, &retboxed); + lrt = julia_struct_to_llvm(rt, unionall_env, &retboxed, llvmcall); if (lrt == NULL) return "return type doesn't correspond to a C type"; - else if (retboxed) - lrt = T_prjlvalue; // is return type fully statically known? if (unionall_env == NULL) { @@ -1459,7 +1451,9 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) rt, at, unionall, ctx.spvals_ptr == NULL ? ctx.linfo->sparam_vals : NULL, /* outputs: */ - lrt, retboxed, static_rt); + lrt, retboxed, static_rt, + /* optional arguments */ + llvmcall); if (err.empty()) { // some extra checks for ccall if (!retboxed && static_rt) { @@ -1516,7 +1510,7 @@ static jl_cgval_t emit_ccall(jl_codectx_t &ctx, jl_value_t **args, size_t nargs) isboxed = false; } else { - largty = julia_struct_to_llvm(tti, unionall, &isboxed); + largty = julia_struct_to_llvm(tti, unionall, &isboxed, llvmcall); } if (isboxed) { ary = boxed(ctx, argv[0]); @@ -1965,9 +1959,6 @@ jl_cgval_t function_sig_t::emit_a_ccall( } else { Type *jlrt = julia_type_to_llvm(rt, &jlretboxed); // compute the real "julian" return type and compute whether it is boxed - if (jlretboxed) { - jlrt = T_prjlvalue; - } if (type_is_ghost(jlrt)) { return ghostValue(rt); } diff --git a/src/ccalltest.c b/src/ccalltest.c index d2596b6c083da..ffe7a718e0af7 100644 --- a/src/ccalltest.c +++ b/src/ccalltest.c @@ -91,7 +91,7 @@ JL_DLLEXPORT complex_t *cptest(complex_t *a) { JL_DLLEXPORT complex_t *cptest_static(complex_t *a) { if (verbose) fprintf(stderr,"%" PRIjint " + %" PRIjint " i\n", a->real, a->imag); - complex_t *b = (complex_t*)malloc(sizeof(complex_t)); + complex_t *b = (complex_t*)malloc_s(sizeof(complex_t)); b->real = a->real; b->imag = a->imag; return b; diff --git a/src/cgutils.cpp b/src/cgutils.cpp index 9d7c1564a0998..62761d396615c 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -504,13 +504,15 @@ static Value *julia_binding_gv(jl_codectx_t &ctx, jl_binding_t *b) // --- mapping between julia and llvm types --- -static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua_env, bool *isboxed); +static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua_env, bool *isboxed, bool llvmcall=false); static unsigned convert_struct_offset(Type *lty, unsigned byte_offset) { const DataLayout &DL = jl_data_layout; const StructLayout *SL = DL.getStructLayout(cast(lty)); - return SL->getElementContainingOffset(byte_offset); + unsigned idx = SL->getElementContainingOffset(byte_offset); + assert(SL->getElementOffset(idx) == byte_offset); + return idx; } static unsigned convert_struct_offset(jl_codectx_t &ctx, Type *lty, unsigned byte_offset) @@ -539,12 +541,12 @@ JL_DLLEXPORT Type *julia_type_to_llvm(jl_value_t *jt, bool *isboxed) return t; } if (isboxed) *isboxed = true; - return T_pjlvalue; + return T_prjlvalue; } } // converts a julia bitstype into the equivalent LLVM bitstype -static Type *bitstype_to_llvm(jl_value_t *bt) +static Type *bitstype_to_llvm(jl_value_t *bt, bool llvmcall = false) { assert(jl_is_primitivetype(bt)); if (bt == (jl_value_t*)jl_bool_type) @@ -553,6 +555,8 @@ static Type *bitstype_to_llvm(jl_value_t *bt) return T_int32; if (bt == (jl_value_t*)jl_int64_type) return T_int64; + if (llvmcall && (bt == (jl_value_t*)jl_float16_type)) + return T_float16; if (bt == (jl_value_t*)jl_float32_type) return T_float32; if (bt == (jl_value_t*)jl_float64_type) @@ -566,7 +570,7 @@ static Type *bitstype_to_llvm(jl_value_t *bt) // fields depend on any of the parameters of the containing type) static bool julia_struct_has_layout(jl_datatype_t *dt, jl_unionall_t *ua) { - if (dt->layout || dt->struct_decl || jl_justbits((jl_value_t*)dt)) + if (dt->layout || dt->struct_decl) return true; if (ua) { jl_svec_t *types = jl_get_fieldtypes(dt); @@ -588,7 +592,7 @@ static unsigned jl_field_align(jl_datatype_t *dt, size_t i) return std::min(al, jl_datatype_align(dt)); } -static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isboxed) +static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isboxed, bool llvmcall) { // this function converts a Julia Type into the equivalent LLVM struct // use this where C-compatible (unboxed) structs are desired @@ -597,11 +601,12 @@ static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isbox if (jt == (jl_value_t*)jl_bottom_type) return T_void; if (jl_is_primitivetype(jt)) - return bitstype_to_llvm(jt); + return bitstype_to_llvm(jt, llvmcall); if (jl_is_structtype(jt)) { jl_datatype_t *jst = (jl_datatype_t*)jt; bool isTuple = jl_is_tuple_type(jt); - if (jst->struct_decl != NULL) + // don't use pre-filled struct_decl for llvmcall + if (!llvmcall && (jst->struct_decl != NULL)) return (Type*)jst->struct_decl; if (jl_is_structtype(jt) && !(jst->layout && jl_is_layout_opaque(jst->layout))) { jl_svec_t *ftypes = jl_get_fieldtypes(jst); @@ -621,14 +626,17 @@ static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isbox if (jlasttype != NULL && ty != jlasttype) isvector = false; jlasttype = ty; - bool isptr; size_t fsz = 0, al = 0; - isptr = !jl_islayout_inline(ty, &fsz, &al); - if (!isptr && jl_is_uniontype(ty)) - fsz += 1; + bool isptr = !jl_islayout_inline(ty, &fsz, &al); + if (jst->layout) { + assert(isptr == jl_field_isptr(jst, i)); + assert((isptr ? sizeof(void*) : fsz + jl_is_uniontype(ty)) == jl_field_size(jst, i)); + assert(al <= jl_field_align(jst, i)); + } Type *lty; if (isptr) { - lty = T_pjlvalue; + lty = T_prjlvalue; + isvector = false; } else if (ty == (jl_value_t*)jl_bool_type) { lty = T_int8; @@ -637,8 +645,8 @@ static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isbox // pick an Integer type size such that alignment will be correct // and always end with an Int8 (selector byte) Type *AlignmentType = IntegerType::get(jl_LLVMContext, 8 * al); - unsigned NumATy = (fsz - 1) / al; - unsigned remainder = (fsz - 1) % al; + unsigned NumATy = fsz / al; + unsigned remainder = fsz % al; while (NumATy--) latypes.push_back(AlignmentType); while (remainder--) @@ -649,7 +657,8 @@ static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isbox continue; } else { - lty = julia_type_to_llvm(ty); + lty = julia_struct_to_llvm(ty, NULL, &isptr, llvmcall); + assert(!isptr); } if (lasttype != NULL && lasttype != lty) isarray = false; @@ -668,8 +677,8 @@ static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isbox // VecElement type is unwrapped in LLVM decl = latypes[0]; } - else if (isTuple && isarray && lasttype != T_int1 && !type_is_ghost(lasttype)) { - if (isvector && jl_special_vector_alignment(ntypes, jlasttype) != 0) + else if (isarray && !type_is_ghost(lasttype)) { + if (isTuple && isvector && jl_special_vector_alignment(ntypes, jlasttype) != 0) decl = VectorType::get(lasttype, ntypes); else decl = ArrayType::get(lasttype, ntypes); @@ -692,17 +701,17 @@ static Type *julia_struct_to_llvm(jl_value_t *jt, jl_unionall_t *ua, bool *isbox // if (jl_is_uniontype(jt)) { // // pick an Integer type size such that alignment will be correct // // and always end with an Int8 (selector byte) - // lty = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), (fsz - 1) / al); + // lty = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * al), fsz / al); // std::vector Elements(2); // Elements[0] = lty; // Elements[1] = T_int8; - // unsigned remainder = (fsz - 1) % al; + // unsigned remainder = fsz % al; // while (remainder--) // Elements.push_back(T_int8); // lty = StructType::get(jl_LLVMContext, makeArrayRef(Elements)); // } if (isboxed) *isboxed = true; - return T_pjlvalue; + return T_prjlvalue; } static bool is_datatype_all_pointers(jl_datatype_t *dt) @@ -1279,8 +1288,6 @@ static jl_cgval_t typed_load(jl_codectx_t &ctx, Value *ptr, Value *idx_0based, j Type *elty = julia_type_to_llvm(jltype, &isboxed); if (type_is_ghost(elty)) return ghostValue(jltype); - if (isboxed) - elty = T_prjlvalue; Type *ptrty = PointerType::get(elty, ptr->getType()->getPointerAddressSpace()); Value *data; if (ptr->getType() != ptrty) @@ -1336,20 +1343,12 @@ static void typed_store(jl_codectx_t &ctx, if (parent != NULL) emit_write_barrier(ctx, parent, r); } - Value *data; Type *ptrty = PointerType::get(elty, ptr->getType()->getPointerAddressSpace()); - if (ptr->getType() != ptrty) { - if (isboxed) { - data = emit_bitcast(ctx, ptr, T_pprjlvalue); - } else { - data = emit_bitcast(ctx, ptr, ptrty); - } - } else { - data = ptr; - } + if (ptr->getType() != ptrty) + ptr = ctx.builder.CreateBitCast(ptr, ptrty); if (idx_0based) - data = ctx.builder.CreateInBoundsGEP(r->getType(), data, idx_0based); - Instruction *store = ctx.builder.CreateAlignedStore(r, data, isboxed || alignment ? alignment : julia_alignment(jltype)); + ptr = ctx.builder.CreateInBoundsGEP(r->getType(), ptr, idx_0based); + Instruction *store = ctx.builder.CreateAlignedStore(r, ptr, isboxed || alignment ? alignment : julia_alignment(jltype)); if (aliasscope) store->setMetadata("noalias", aliasscope); if (tbaa) @@ -1468,15 +1467,84 @@ static void emit_memcpy(jl_codectx_t &ctx, Value *dst, MDNode *tbaa_dst, const j +static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &strct, unsigned idx, jl_datatype_t *jt); + static bool emit_getfield_unknownidx(jl_codectx_t &ctx, - jl_cgval_t *ret, const jl_cgval_t &strct, + jl_cgval_t *ret, jl_cgval_t strct, Value *idx, jl_datatype_t *stt, jl_value_t *inbounds) { size_t nfields = jl_datatype_nfields(stt); bool maybe_null = (unsigned)stt->ninitialized != nfields; + auto idx0 = [&]() { + return emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields), inbounds); + }; + if (nfields == 0) { + (void)idx0(); + *ret = jl_cgval_t(); + return true; + } + if (nfields == 1) { + (void)idx0(); + *ret = emit_getfield_knownidx(ctx, strct, 0, stt); + return true; + } + assert(!jl_is_vecelement_type((jl_value_t*)stt)); + + if (!strct.ispointer()) { // unboxed + assert(jl_justbits((jl_value_t*)stt)); + bool isboxed = is_datatype_all_pointers(stt); + bool issame = is_tupletype_homogeneous(stt->types); + if (issame) { + jl_value_t *jft = jl_svecref(stt->types, 0); + if (strct.isghost) { + (void)idx0(); + *ret = ghostValue(jft); + return true; + } + if (isa(strct.V->getType())) { + assert(stt->layout->npointers == 0); // we could, but don't emit this + idx = idx0(); + if (sizeof(void*) != sizeof(int)) + idx = ctx.builder.CreateTrunc(idx, T_int32); // llvm3.3 requires this, harmless elsewhere + Value *fld = ctx.builder.CreateExtractElement(strct.V, idx); + *ret = mark_julia_type(ctx, fld, false, jft); + return true; + } + else if (isa(strct.V->getType())) { + if (!isboxed && nfields > 3) { + // For small objects and tracked pointers, emit a set of Select statements, + // otherwise emit as a stack load. This keeps LLVM from becoming unhappy + // about seeing loads of tracked pointers. + strct = value_to_pointer(ctx, strct); + assert(strct.ispointer()); + } + // fall-through to next branch, where we'll handle it + } + else { + llvm_unreachable("homogeneous struct should have had a homogeneous type"); + } + } + if (isboxed || (issame && isa(strct.V->getType()))) { + assert((cast(strct.V->getType())->getElementType() == T_prjlvalue) == isboxed); + Value *idx = idx0(); + unsigned i = 0; + Value *fld = ctx.builder.CreateExtractValue(strct.V, makeArrayRef(i)); + for (i = 1; i < nfields; i++) { + fld = ctx.builder.CreateSelect( + ctx.builder.CreateICmpEQ(idx, ConstantInt::get(idx->getType(), i)), + ctx.builder.CreateExtractValue(strct.V, makeArrayRef(i)), + fld); + } + jl_value_t *jft = issame ? jl_svecref(stt->types, 0) : (jl_value_t*)jl_any_type; + if (isboxed && maybe_null) + null_pointer_check(ctx, fld); + *ret = mark_julia_type(ctx, fld, isboxed, jft); + return true; + } + } + if (strct.ispointer()) { // boxed or stack if (is_datatype_all_pointers(stt)) { - idx = emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields), inbounds); size_t minimum_field_size = std::numeric_limits::max(); size_t minimum_align = JL_HEAP_ALIGNMENT; for (size_t i = 0; i < nfields; ++i) { @@ -1493,7 +1561,7 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, Value *fldptr = ctx.builder.CreateInBoundsGEP( T_prjlvalue, maybe_decay_tracked(emit_bitcast(ctx, data_pointer(ctx, strct), T_pprjlvalue)), - idx); + idx0()); Value *fld = tbaa_decorate(strct.tbaa, maybe_mark_load_dereferenceable( ctx.builder.CreateLoad(T_prjlvalue, fldptr), @@ -1505,17 +1573,17 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, } else if (is_tupletype_homogeneous(stt->types)) { assert(nfields > 0); // nf == 0 trapped by all_pointers case - jl_value_t *jt = jl_svecref(stt->types, 0); - idx = emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields), inbounds); + jl_value_t *jft = jl_svecref(stt->types, 0); + idx = idx0(); Value *ptr = maybe_decay_tracked(data_pointer(ctx, strct)); - if (!stt->mutabl && !(maybe_null && jt == (jl_value_t*)jl_bool_type)) { + if (!stt->mutabl && !(maybe_null && jft == (jl_value_t*)jl_bool_type)) { // just compute the pointer and let user load it when necessary - Type *fty = julia_type_to_llvm(jt); + Type *fty = julia_type_to_llvm(jft); Value *addr = ctx.builder.CreateInBoundsGEP(fty, emit_bitcast(ctx, ptr, PointerType::get(fty, 0)), idx); - *ret = mark_julia_slot(addr, jt, NULL, strct.tbaa); + *ret = mark_julia_slot(addr, jft, NULL, strct.tbaa); return true; } - *ret = typed_load(ctx, ptr, idx, jt, strct.tbaa, nullptr, false); + *ret = typed_load(ctx, ptr, idx, jft, strct.tbaa, nullptr, false); return true; } else if (strct.isboxed) { @@ -1525,49 +1593,24 @@ static bool emit_getfield_unknownidx(jl_codectx_t &ctx, return true; } } - else if (is_tupletype_homogeneous(stt->types)) { - assert(jl_justbits((jl_value_t*)stt)); - if (nfields == 0) { - idx = emit_bounds_check( - ctx, ghostValue(stt), (jl_value_t*)stt, - idx, ConstantInt::get(T_size, nfields), inbounds); - *ret = jl_cgval_t(); - return true; - } - assert(!jl_field_isptr(stt, 0)); - jl_value_t *jt = jl_svecref(stt->types, 0); - Value *idx0 = emit_bounds_check(ctx, strct, (jl_value_t*)stt, idx, ConstantInt::get(T_size, nfields), inbounds); - if (strct.isghost) { - *ret = ghostValue(jt); - return true; - } - // llvm::VectorType - if (sizeof(void*) != sizeof(int)) - idx0 = ctx.builder.CreateTrunc(idx0, T_int32); // llvm3.3 requires this, harmless elsewhere - Value *fld = ctx.builder.CreateExtractElement(strct.V, idx0); - *ret = mark_julia_type(ctx, fld, false, jt); - return true; - } return false; } static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &strct, unsigned idx, jl_datatype_t *jt) { jl_value_t *jfty = jl_field_type(jt, idx); - Type *elty = julia_type_to_llvm(jfty); if (jfty == jl_bottom_type) { raise_exception(ctx, literal_pointer_val(ctx, jl_undefref_exception)); return jl_cgval_t(); // unreachable } - if (type_is_ghost(elty)) + if (type_is_ghost(julia_type_to_llvm(jfty))) return ghostValue(jfty); - Value *fldv = NULL; bool maybe_null = idx >= (unsigned)jt->ninitialized; + size_t byte_offset = jl_field_offset(jt, idx); if (strct.ispointer()) { Value *staddr = maybe_decay_tracked(data_pointer(ctx, strct)); bool isboxed; Type *lt = julia_type_to_llvm((jl_value_t*)jt, &isboxed); - size_t byte_offset = jl_field_offset(jt, idx); Value *addr; if (isboxed) { // byte_offset == 0 is an important special case here, e.g. @@ -1584,29 +1627,17 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st } } else { - if (VectorType *vlt = dyn_cast(lt)) { - // doesn't have the struct wrapper, so this must have been a VecElement - // cast to the element type so that it can be addressed with GEP - lt = vlt->getElementType(); - staddr = emit_bitcast(ctx, staddr, lt->getPointerTo()); - Value *llvm_idx = ConstantInt::get(T_size, idx); - addr = ctx.builder.CreateInBoundsGEP(lt, staddr, llvm_idx); - } - else if (lt->isSingleValueType()) { - addr = emit_bitcast(ctx, staddr, lt->getPointerTo()); - } - else { - staddr = emit_bitcast(ctx, staddr, lt->getPointerTo()); - if (isa(lt)) - addr = emit_struct_gep(ctx, lt, staddr, byte_offset); - else - addr = ctx.builder.CreateConstGEP2_32(lt, staddr, 0, idx); - } + staddr = maybe_bitcast(ctx, staddr, lt->getPointerTo()); + if (jl_is_vecelement_type((jl_value_t*)jt)) + addr = staddr; // VecElement types are unwrapped in LLVM. + else if (isa(lt)) + addr = emit_struct_gep(ctx, lt, staddr, byte_offset); + else + addr = ctx.builder.CreateConstInBoundsGEP2_32(lt, staddr, 0, idx); } - unsigned align = jl_field_align(jt, idx); if (jl_field_isptr(jt, idx)) { Instruction *Load = maybe_mark_load_dereferenceable( - ctx.builder.CreateLoad(T_prjlvalue, emit_bitcast(ctx, addr, T_pprjlvalue)), + ctx.builder.CreateLoad(T_prjlvalue, maybe_bitcast(ctx, addr, T_pprjlvalue)), maybe_null, jl_field_type(jt, idx)); Value *fldv = tbaa_decorate(strct.tbaa, Load); if (maybe_null) @@ -1614,13 +1645,17 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st return mark_julia_type(ctx, fldv, true, jfty); } else if (jl_is_uniontype(jfty)) { - int fsz = jl_field_size(jt, idx); + size_t fsz = 0, al = 0; + bool isptr = !jl_islayout_inline(jfty, &fsz, &al); + assert(!isptr && fsz == jl_field_size(jt, idx) - 1); (void)isptr; Value *ptindex; - if (isa(lt)) - ptindex = emit_struct_gep(ctx, lt, staddr, byte_offset + fsz - 1); - else + if (isboxed) { ptindex = ctx.builder.CreateConstInBoundsGEP1_32( - T_int8, emit_bitcast(ctx, staddr, T_pint8), byte_offset + fsz - 1); + T_int8, emit_bitcast(ctx, staddr, T_pint8), byte_offset + fsz); + } + else { + ptindex = emit_struct_gep(ctx, cast(lt), staddr, byte_offset + fsz); + } Instruction *tindex0 = tbaa_decorate(tbaa_unionselbyte, ctx.builder.CreateLoad(T_int8, ptindex)); //tindex0->setMetadata(LLVMContext::MD_range, MDNode::get(jl_LLVMContext, { // ConstantAsMetadata::get(ConstantInt::get(T_int8, 0)), @@ -1628,11 +1663,10 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st Value *tindex = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), tindex0); if (jt->mutabl) { // move value to an immutable stack slot (excluding tindex) - Type *AT = ArrayType::get(IntegerType::get(jl_LLVMContext, 8 * align), (fsz + align - 2) / align); - AllocaInst *lv = emit_static_alloca(ctx, AT); - if (align > 1) - lv->setAlignment(align); - emit_memcpy(ctx, lv, strct.tbaa, addr, strct.tbaa, fsz - 1, align); + Type *ET = IntegerType::get(jl_LLVMContext, 8 * al); + AllocaInst *lv = emit_static_alloca(ctx, ET); + lv->setOperand(0, ConstantInt::get(T_int32, (fsz + al - 1) / al)); + emit_memcpy(ctx, lv, strct.tbaa, addr, strct.tbaa, fsz, al); addr = lv; } return mark_julia_slot(addr, jfty, tindex, strct.tbaa); @@ -1641,21 +1675,68 @@ static jl_cgval_t emit_getfield_knownidx(jl_codectx_t &ctx, const jl_cgval_t &st // just compute the pointer and let user load it when necessary return mark_julia_slot(addr, jfty, NULL, strct.tbaa); } + unsigned align = jl_field_align(jt, idx); return typed_load(ctx, addr, NULL, jfty, strct.tbaa, nullptr, true, align); } else if (isa(strct.V)) { return jl_cgval_t(); } else { - if (strct.V->getType()->isVectorTy()) { - fldv = ctx.builder.CreateExtractElement(strct.V, ConstantInt::get(T_int32, idx)); + Value *obj = strct.V; // aka emit_unbox + Type *T = obj->getType(); + Value *fldv; + if (jl_is_vecelement_type((jl_value_t*)jt)) { + // VecElement types are unwrapped in LLVM. + fldv = obj; + } + else if (isa(T)) { + fldv = ctx.builder.CreateExtractElement(obj, ConstantInt::get(T_int32, idx)); + } + else if (!jl_field_isptr(jt, idx) && jl_is_uniontype(jfty)) { + int fsz = jl_field_size(jt, idx) - 1; + unsigned ptindex = convert_struct_offset(ctx, T, byte_offset + fsz); + AllocaInst *lv = NULL; + if (fsz > 0) { + unsigned st_idx = convert_struct_offset(ctx, T, byte_offset); + IntegerType *ET = cast(T->getStructElementType(st_idx)); + unsigned align = (ET->getBitWidth() + 7) / 8; + lv = emit_static_alloca(ctx, ET); + lv->setOperand(0, ConstantInt::get(T_int32, (fsz + align - 1) / align)); + // emit all of the align-sized words + unsigned i = 0; + for (; i < fsz / align; i++) { + unsigned fld = st_idx + i; + Value *fldv = ctx.builder.CreateExtractValue(obj, makeArrayRef(fld)); + Value *fldp = ctx.builder.CreateConstInBoundsGEP1_32(ET, lv, i); + ctx.builder.CreateStore(fldv, fldp); + } + // emit remaining bytes up to tindex + if (i < ptindex - st_idx) { + Value *staddr = ctx.builder.CreateConstInBoundsGEP1_32(ET, lv, i); + staddr = ctx.builder.CreateBitCast(staddr, T_pint8); + for (; i < ptindex - st_idx; i++) { + Value *fldv = ctx.builder.CreateExtractValue(obj, makeArrayRef(st_idx + i)); + Value *fldp = ctx.builder.CreateConstInBoundsGEP1_32(T_int8, staddr, i); + ctx.builder.CreateStore(fldv, fldp); + } + } + } + Value *tindex0 = ctx.builder.CreateExtractValue(obj, makeArrayRef(ptindex)); + Value *tindex = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), tindex0); + return mark_julia_slot(lv, jfty, tindex, tbaa_stack); } else { - // VecElement types are unwrapped in LLVM. - assert( strct.V->getType()->isSingleValueType() ); - fldv = strct.V; + unsigned st_idx; + if (isa(T)) + st_idx = idx; + else if (isa(T)) + st_idx = convert_struct_offset(ctx, T, byte_offset); + else + llvm_unreachable("encountered incompatible type for a struct"); + fldv = ctx.builder.CreateExtractValue(obj, makeArrayRef(st_idx)); } - assert(!jl_field_isptr(jt, idx)); + if (maybe_null && jl_field_isptr(jt, idx)) + null_pointer_check(ctx, fldv); return mark_julia_type(ctx, fldv, false, jfty); } } @@ -1972,66 +2053,75 @@ static void init_bits_cgval(jl_codectx_t &ctx, Value *newv, const jl_cgval_t& v, static jl_value_t *static_constant_instance(Constant *constant, jl_value_t *jt) { - assert(constant != NULL); + assert(constant != NULL && jl_is_concrete_type(jt)); + jl_datatype_t *jst = (jl_datatype_t*)jt; if (isa(constant)) return NULL; - ConstantInt *cint = dyn_cast(constant); - if (cint != NULL) { - assert(jl_is_datatype(jt)); - if (jt == (jl_value_t*)jl_bool_type) + if (ConstantInt *cint = dyn_cast(constant)) { + if (jst == jl_bool_type) return cint->isZero() ? jl_false : jl_true; return jl_new_bits(jt, const_cast(cint->getValue().getRawData())); } - ConstantFP *cfp = dyn_cast(constant); - if (cfp != NULL) { - assert(jl_is_datatype(jt)); + if (ConstantFP *cfp = dyn_cast(constant)) { return jl_new_bits(jt, const_cast(cfp->getValueAPF().bitcastToAPInt().getRawData())); } - ConstantPointerNull *cpn = dyn_cast(constant); - if (cpn != NULL) { - assert(jl_is_cpointer_type(jt)); + if (isa(constant)) { uint64_t val = 0; return jl_new_bits(jt, &val); } // issue #8464 - ConstantExpr *ce = dyn_cast(constant); - if (ce != NULL) { - if (ce->isCast()) { - return static_constant_instance(dyn_cast(ce->getOperand(0)), jt); + if (ConstantExpr *ce = dyn_cast(constant)) { + unsigned OpCode = ce->getOpcode(); + if (OpCode == Instruction::BitCast || OpCode == Instruction::PtrToInt || OpCode == Instruction::IntToPtr) { + return static_constant_instance(ce->getOperand(0), jt); } + return NULL; } - size_t nargs = 0; - if (ConstantStruct *cst = dyn_cast(constant)) - nargs = cst->getType()->getNumElements(); - else if (ConstantVector *cvec = dyn_cast(constant)) - nargs = cvec->getType()->getNumElements(); - else if (ConstantArray *carr = dyn_cast(constant)) - nargs = carr->getType()->getNumElements(); - else if (ConstantDataVector *cdv = dyn_cast(constant)) - nargs = cdv->getType()->getNumElements(); - else if (isa(constant)) + if (isa(constant)) return NULL; - else - assert(false && "Cannot process this type of constant"); - assert(jl_is_tuple_type(jt)); + size_t nargs; + if (const auto *CC = dyn_cast(constant)) + nargs = CC->getNumOperands(); + else if (const auto *CAZ = dyn_cast(constant)) + nargs = CAZ->getNumElements(); + else if (const auto *CDS = dyn_cast(constant)) + nargs = CDS->getNumElements(); + else + return NULL; + assert(nargs > 0 && jst->instance == NULL); + if (nargs != jl_datatype_nfields(jst)) + return NULL; - jl_value_t **tupleargs; - JL_GC_PUSHARGS(tupleargs, nargs); + jl_value_t **flds; + JL_GC_PUSHARGS(flds, nargs); for (size_t i = 0; i < nargs; i++) { - tupleargs[i] = static_constant_instance(constant->getAggregateElement(i), jl_tparam(jt, i)); + jl_value_t *ft = jl_field_type(jst, i); + if (jl_field_isptr(jst, i) || jl_is_uniontype(ft)) { + JL_GC_POP(); + return NULL; // TODO: handle this? + } + unsigned llvm_idx = i; + if (i > 0 && isa(constant->getType())) + llvm_idx = convert_struct_offset(constant->getType(), jl_field_offset(jst, i)); + Constant *fld = constant->getAggregateElement(llvm_idx); + flds[i] = static_constant_instance(fld, ft); + if (flds[i] == NULL) { + JL_GC_POP(); + return NULL; // must have been unreachable + } } - jl_value_t *tpl = jl_f_tuple(NULL, tupleargs, nargs); + jl_value_t *obj = jl_new_structv(jst, flds, nargs); JL_GC_POP(); - return tpl; + return obj; } static Value *call_with_attrs(jl_codectx_t &ctx, Function *func, Value *v) @@ -2459,14 +2549,14 @@ static void emit_setfield(jl_codectx_t &ctx, emit_write_barrier(ctx, boxed(ctx, strct), r); } else if (jl_is_uniontype(jfty)) { - int fsz = jl_field_size(sty, idx0); + int fsz = jl_field_size(sty, idx0) - 1; // compute tindex from rhs jl_cgval_t rhs_union = convert_julia_type(ctx, rhs, jfty); if (rhs_union.typ == jl_bottom_type) return; Value *tindex = compute_tindex_unboxed(ctx, rhs_union, jfty); tindex = ctx.builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1)); - Value *ptindex = ctx.builder.CreateInBoundsGEP(T_int8, emit_bitcast(ctx, maybe_decay_tracked(addr), T_pint8), ConstantInt::get(T_size, fsz - 1)); + Value *ptindex = ctx.builder.CreateInBoundsGEP(T_int8, emit_bitcast(ctx, maybe_decay_tracked(addr), T_pint8), ConstantInt::get(T_size, fsz)); tbaa_decorate(tbaa_unionselbyte, ctx.builder.CreateStore(tindex, ptindex)); // copy data if (!rhs.isghost) { @@ -2502,23 +2592,26 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg // whether we should perform the initialization with the struct as a IR value // or instead initialize the stack buffer with stores bool init_as_value = false; - if (lt->isVectorTy() || - jl_is_vecelement_type(ty)) { // maybe also check the size ? + if (lt->isVectorTy() || jl_is_vecelement_type(ty)) { // maybe also check the size ? init_as_value = true; } Value *strct; - if (type_is_ghost(lt)) + if (type_is_ghost(lt)) { strct = NULL; - else if (init_as_value) + } + else if (init_as_value) { strct = UndefValue::get(lt); - else + } + else { strct = emit_static_alloca(ctx, lt); + } for (unsigned i = 0; i < na; i++) { jl_value_t *jtype = jl_svecref(sty->types, i); - const jl_cgval_t &fval_info = argv[i]; + jl_cgval_t fval_info = argv[i]; emit_typecheck(ctx, fval_info, jtype, "new"); + fval_info = update_julia_type(ctx, fval_info, jtype); if (type_is_ghost(lt)) continue; Type *fty = julia_type_to_llvm(jtype); @@ -2533,47 +2626,83 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg dest = ctx.builder.CreateConstInBoundsGEP2_32(lt, strct, 0, llvm_idx); } Value *fval = NULL; + assert(!jl_field_isptr(sty, i)); if (jl_is_uniontype(jtype)) { - assert(!init_as_value && "unimplemented"); // compute tindex from rhs jl_cgval_t rhs_union = convert_julia_type(ctx, fval_info, jtype); if (rhs_union.typ == jl_bottom_type) return jl_cgval_t(); Value *tindex = compute_tindex_unboxed(ctx, rhs_union, jtype); tindex = ctx.builder.CreateNUWSub(tindex, ConstantInt::get(T_int8, 1)); - int fsz = jl_field_size(sty, i); - Value *ptindex = emit_struct_gep(ctx, lt, strct, offs + fsz - 1); - tbaa_decorate(tbaa_unionselbyte, ctx.builder.CreateStore(tindex, ptindex)); - if (!rhs_union.isghost) - emit_unionmove(ctx, dest, tbaa_stack, fval_info, nullptr); - // If you wanted to implement init_as_value, - // would need to emit the union-move into temporary memory, - // then load it and combine with the tindex. - // But more efficient to just store it directly. + size_t fsz = 0, al = 0; + bool isptr = !jl_islayout_inline(jtype, &fsz, &al); + assert(!isptr && fsz == jl_field_size(sty, i) - 1); (void)isptr; + if (init_as_value) { + // If you wanted to implement init_as_value, + // would need to emit the union-move into temporary memory, + // then load it and combine with the tindex. + // But more efficient to just store it directly. + unsigned ptindex = convert_struct_offset(ctx, lt, offs + fsz); + if (fsz > 0 && !fval_info.isghost) { + Type *ET = IntegerType::get(jl_LLVMContext, 8 * al); + assert(lt->getStructElementType(llvm_idx) == ET); + AllocaInst *lv = emit_static_alloca(ctx, ET); + lv->setOperand(0, ConstantInt::get(T_int32, (fsz + al - 1) / al)); + emit_unionmove(ctx, lv, tbaa_stack, fval_info, nullptr); + // emit all of the align-sized words + unsigned i = 0; + for (; i < fsz / al; i++) { + Value *fldp = ctx.builder.CreateConstInBoundsGEP1_32(ET, lv, i); + Value *fldv = tbaa_decorate(tbaa_stack, ctx.builder.CreateLoad(ET, fldp)); + strct = ctx.builder.CreateInsertValue(strct, fldv, makeArrayRef(llvm_idx + i)); + } + // emit remaining bytes up to tindex + if (i < ptindex - llvm_idx) { + Value *staddr = ctx.builder.CreateConstInBoundsGEP1_32(ET, lv, i); + staddr = ctx.builder.CreateBitCast(staddr, T_pint8); + for (; i < ptindex - llvm_idx; i++) { + Value *fldp = ctx.builder.CreateConstInBoundsGEP1_32(T_int8, staddr, i); + Value *fldv = tbaa_decorate(tbaa_stack, ctx.builder.CreateLoad(T_int8, fldp)); + strct = ctx.builder.CreateInsertValue(strct, fldv, makeArrayRef(llvm_idx + i)); + } + } + } + llvm_idx = ptindex; + fval = tindex; + } + else { + Value *ptindex = emit_struct_gep(ctx, lt, strct, offs + fsz); + tbaa_decorate(tbaa_unionselbyte, ctx.builder.CreateStore(tindex, ptindex)); + if (!rhs_union.isghost) + emit_unionmove(ctx, dest, tbaa_stack, fval_info, nullptr); + } } else { fval = emit_unbox(ctx, fty, fval_info, jtype, dest, tbaa_stack); } if (init_as_value) { assert(fval); - if (lt->isVectorTy()) - strct = ctx.builder.CreateInsertElement(strct, fval, ConstantInt::get(T_int32, llvm_idx)); - else if (jl_is_vecelement_type(ty)) + if (jl_is_vecelement_type(ty)) strct = fval; // VecElement type comes unwrapped in LLVM. + else if (lt->isVectorTy()) + strct = ctx.builder.CreateInsertElement(strct, fval, ConstantInt::get(T_int32, llvm_idx)); else if (lt->isAggregateType()) - strct = ctx.builder.CreateInsertValue(strct, fval, ArrayRef(&i, 1)); + strct = ctx.builder.CreateInsertValue(strct, fval, makeArrayRef(llvm_idx)); else assert(false); } } for (size_t i = nargs; i < nf; i++) { if (!jl_field_isptr(sty, i) && jl_is_uniontype(jl_field_type(sty, i))) { - tbaa_decorate(tbaa_unionselbyte, ctx.builder.CreateStore( - ConstantInt::get(T_int8, 0), - ctx.builder.CreateInBoundsGEP( - T_int8, - emit_bitcast(ctx, strct, T_pint8), - ConstantInt::get(T_size, jl_field_offset(sty, i) + jl_field_size(sty, i) - 1)))); + unsigned offs = jl_field_offset(sty, i); + int fsz = jl_field_size(sty, i) - 1; + unsigned llvm_idx = convert_struct_offset(ctx, cast(lt), offs + fsz); + if (init_as_value) + strct = ctx.builder.CreateInsertValue(strct, ConstantInt::get(T_int8, 0), makeArrayRef(llvm_idx)); + else + tbaa_decorate(tbaa_unionselbyte, ctx.builder.CreateStore( + ConstantInt::get(T_int8, 0), + ctx.builder.CreateConstInBoundsGEP2_32(lt, strct, 0, llvm_idx))); } } if (type_is_ghost(lt)) @@ -2587,14 +2716,7 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg literal_pointer_val(ctx, (jl_value_t*)ty)); jl_cgval_t strctinfo = mark_julia_type(ctx, strct, true, ty); strct = decay_derived(strct); - for (size_t i = 0; i < nf; i++) { - if (jl_field_isptr(sty, i)) { - tbaa_decorate(strctinfo.tbaa, ctx.builder.CreateStore( - ConstantPointerNull::get(cast(T_prjlvalue)), - ctx.builder.CreateInBoundsGEP(T_prjlvalue, emit_bitcast(ctx, strct, T_pprjlvalue), - ConstantInt::get(T_size, jl_field_offset(sty, i) / sizeof(void*))))); - } - } + undef_derived_strct(ctx.builder, strct, sty, strctinfo.tbaa); for (size_t i = nargs; i < nf; i++) { if (!jl_field_isptr(sty, i) && jl_is_uniontype(jl_field_type(sty, i))) { tbaa_decorate(tbaa_unionselbyte, ctx.builder.CreateStore( @@ -2603,12 +2725,14 @@ static jl_cgval_t emit_new_struct(jl_codectx_t &ctx, jl_value_t *ty, size_t narg ConstantInt::get(T_size, jl_field_offset(sty, i) + jl_field_size(sty, i) - 1)))); } } - bool need_wb = false; // TODO: verify that nargs <= nf (currently handled by front-end) for (size_t i = 0; i < nargs; i++) { const jl_cgval_t &rhs = argv[i]; - if (jl_field_isptr(sty, i) && !rhs.isboxed) - need_wb = true; + bool need_wb; // set to true if the store might cause the allocation of a box newer than the struct + if (jl_field_isptr(sty, i)) + need_wb = !rhs.isboxed; + else + need_wb = false; emit_typecheck(ctx, rhs, jl_svecref(sty->types, i), "new"); emit_setfield(ctx, sty, strctinfo, i, rhs, false, need_wb); } diff --git a/src/clangsa/GCChecker.cpp b/src/clangsa/GCChecker.cpp index a232241675615..acd20c69a50a7 100644 --- a/src/clangsa/GCChecker.cpp +++ b/src/clangsa/GCChecker.cpp @@ -19,6 +19,15 @@ #endif #include +#if defined(__GNUC__) +# define USED_FUNC __attribute__((used)) +#elif defined(_COMPILER_MICROSOFT_) +// Does MSVC have this? +# define USED_FUNC +#else +# define USED_FUNC +#endif + namespace { using namespace clang; using namespace ento; @@ -164,7 +173,6 @@ namespace { static bool isGCTrackedType(QualType Type); bool isGloballyRootedType(QualType Type) const; - bool isBundleOfGCValues(QualType QT) const; static void dumpState(const ProgramStateRef &State); static bool declHasAnnotation(const clang::Decl *D, const char *which); static bool isFDAnnotatedNotSafepoint(const clang::FunctionDecl *FD); @@ -287,12 +295,6 @@ SymbolRef GCChecker::walkToRoot(callback f, const ProgramStateRef &State, const namespace Helpers { - static SymbolRef trySuperRegion(SValExplainer &Ex, ProgramStateRef State, SVal Val) { - const MemRegion *R = Val.getAsRegion(); - const SubRegion *SR = R->getAs(); - return SR ? State->getSVal(SR->getSuperRegion()).getAsSymbol() : nullptr; - } - static const VarRegion *walk_back_to_global_VR(const MemRegion *Region) { if (!Region) return nullptr; @@ -451,7 +453,7 @@ PDP GCChecker::GCValueBugVisitor::VisitNode( if (NewValueState->FD) { bool isFunctionSafepoint = !isFDAnnotatedNotSafepoint(NewValueState->FD); bool maybeUnrooted = declHasAnnotation(NewValueState->PVD, "julia_maybe_unrooted"); - assert(isFunctionSafepoint || maybeUnrooted); + assert(isFunctionSafepoint || maybeUnrooted); (void)maybeUnrooted; Pos = PathDiagnosticLocation{NewValueState->PVD, BRC.getSourceManager()}; if (!isFunctionSafepoint) @@ -684,14 +686,6 @@ bool GCChecker::isFDAnnotatedNotSafepoint(const clang::FunctionDecl *FD) { return declHasAnnotation(FD, "julia_not_safepoint"); } -bool GCChecker::isBundleOfGCValues(QualType QT) const { - return isJuliaType([](StringRef Name) { - if (Name.endswith_lower("typemap_intersection_env")) - return true; - return false; - }, QT); -} - bool GCChecker::isGCTrackedType(QualType QT) { return isValueCollection(QT) || isJuliaType([](StringRef Name) { if (Name.endswith_lower("jl_value_t") || @@ -749,23 +743,8 @@ bool GCChecker::isGloballyRootedType(QualType QT) const { bool GCChecker::isSafepoint(const CallEvent &Call) const { bool isCalleeSafepoint = true; - if (Call.isGlobalCFunction("malloc") || - Call.isGlobalCFunction("memcpy") || - Call.isGlobalCFunction("memset") || - Call.isGlobalCFunction("memcmp") || - Call.isGlobalCFunction("mmap") || - Call.isGlobalCFunction("munmap") || - Call.isGlobalCFunction("mprotect") || - Call.isGlobalCFunction("madvise") || - Call.isGlobalCFunction("write") || - Call.isGlobalCFunction("vasprintf") || - Call.isGlobalCFunction("strrchr") || - Call.isGlobalCFunction("free") || - Call.isGlobalCFunction("__assert_fail") || - Call.isGlobalCFunction("fflush") || - Call.isGlobalCFunction("__isnan") || - Call.isGlobalCFunction("__isnanf")) - { + if (Call.isInSystemHeader()) { + // defined by -isystem per https://clang.llvm.org/docs/UsersManual.html#controlling-diagnostics-in-system-headers isCalleeSafepoint = false; } else { auto *Decl = Call.getDecl(); @@ -1149,7 +1128,7 @@ void GCChecker::checkPostStmt(const UnaryOperator *UO, CheckerContext &C) const } } -void GCChecker::dumpState(const ProgramStateRef &State) { +USED_FUNC void GCChecker::dumpState(const ProgramStateRef &State) { GCValueMapTy AMap = State->get(); llvm::raw_ostream &Out = llvm::outs(); Out << "State: " << "\n"; diff --git a/src/codegen.cpp b/src/codegen.cpp index 2141ea22361c4..980d1bb166e78 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -210,6 +210,7 @@ static MDNode *tbaa_gcframe; // GC frame // LLVM should have enough info for alias analysis of non-gcframe stack slot // this is mainly a place holder for `jl_cgval_t::tbaa` static MDNode *tbaa_stack; // stack slot +static MDNode *tbaa_unionselbyte; // a selector byte in isbits Union struct fields static MDNode *tbaa_data; // Any user data that `pointerset/ref` are allowed to alias static MDNode *tbaa_binding; // jl_binding_t::value static MDNode *tbaa_value; // jl_value_t, that is not jl_array_t @@ -217,7 +218,6 @@ static MDNode *tbaa_mutab; // mutable type static MDNode *tbaa_immut; // immutable type static MDNode *tbaa_ptrarraybuf; // Data in an array of boxed values static MDNode *tbaa_arraybuf; // Data in an array of POD -static MDNode *tbaa_unionselbyte; // a selector byte in isbits Union struct fields static MDNode *tbaa_array; // jl_array_t static MDNode *tbaa_arrayptr; // The pointer inside a jl_array_t static MDNode *tbaa_arraysize; // A size in a jl_array_t @@ -523,7 +523,6 @@ class jl_codectx_t { MDNode *aliasscope = NULL; std::string funcName; int vaSlot = -1; // name of vararg argument - bool has_sret = false; int nReqArgs = 0; int nargs = 0; int nvargs = -1; @@ -557,6 +556,7 @@ static CallInst *emit_jlcall(jl_codectx_t &ctx, Value *theFptr, Value *theF, static Value *literal_pointer_val(jl_codectx_t &ctx, jl_value_t *p); static GlobalVariable *prepare_global_in(Module *M, GlobalVariable *G); #define prepare_global(G) prepare_global_in(jl_Module, (G)) +static Instruction *tbaa_decorate(MDNode *md, Instruction *load_or_store); // --- convenience functions for tagging llvm values with julia types --- @@ -573,13 +573,29 @@ static GlobalVariable *get_pointer_to_constant(Constant *val, StringRef name, Mo return gv; } -static AllocaInst *emit_static_alloca(jl_codectx_t &ctx, Type *lty, int arraysize=1) +static AllocaInst *emit_static_alloca(jl_codectx_t &ctx, Type *lty) { - return new AllocaInst(lty, - 0, - ConstantInt::get(T_int32, arraysize), "", /*InsertBefore=*/ctx.ptlsStates); + return new AllocaInst(lty, 0, "", /*InsertBefore=*/ctx.ptlsStates); } +static void undef_derived_strct(IRBuilder<> &irbuilder, Value *ptr, jl_datatype_t *sty, MDNode *tbaa) +{ + assert(ptr->getType()->getPointerAddressSpace() != AddressSpace::Tracked); + if (sty->layout->npointers == 0) + return; + ptr = irbuilder.CreateBitCast(ptr, T_prjlvalue->getPointerTo(ptr->getType()->getPointerAddressSpace())); + Value *V_null = ConstantPointerNull::get(cast(T_prjlvalue)); + size_t i, nf = jl_datatype_nfields(sty); + for (i = 0; i < nf; i++) { + if (jl_field_isptr(sty, i)) { + tbaa_decorate(tbaa, irbuilder.CreateStore(V_null, + irbuilder.CreateInBoundsGEP(T_prjlvalue, ptr, + ConstantInt::get(T_size, jl_field_offset(sty, i) / sizeof(void*))))); + } + } +} + + static inline jl_cgval_t ghostValue(jl_value_t *typ) { if (typ == jl_bottom_type) @@ -628,11 +644,25 @@ static inline jl_cgval_t mark_julia_slot(Value *v, jl_value_t *typ, Value *tinde return tagval; } +static bool valid_as_globalinit(const Value *v) { + if (isa(v)) { + // llvm can't handle all the things that could be inside a ConstantExpr + // (such as addrspacecast), and we don't really mind losing this optimization + return false; + } + if (const auto *CC = dyn_cast(v)) { + for (const Value *elem : CC->operand_values()) + if (!valid_as_globalinit(elem)) + return false; + } + return isa(v); +} + static inline jl_cgval_t value_to_pointer(jl_codectx_t &ctx, Value *v, jl_value_t *typ, Value *tindex) { Value *loc; - if (Constant *cv = dyn_cast(v)) { - loc = get_pointer_to_constant(cv, "", *jl_Module); + if (valid_as_globalinit(v)) { // llvm can't handle all the things that could be inside a ConstantExpr + loc = get_pointer_to_constant(cast(v), "", *jl_Module); } else { loc = emit_static_alloca(ctx, v->getType()); @@ -1414,8 +1444,10 @@ void jl_generate_fptr(jl_code_instance_t *output) break; ucache = ucache->next; } - if (codeinst->invoke) + if (codeinst->invoke) { + JL_UNLOCK(&codegen_lock); return; + } if (ucache != NULL) { codeinst->specptr = ucache->specptr; codeinst->rettype_const = ucache->rettype_const; @@ -2218,7 +2250,53 @@ static jl_cgval_t emit_getfield(jl_codectx_t &ctx, const jl_cgval_t &strct, jl_s return mark_julia_type(ctx, result, true, jl_any_type); } -static Value *emit_bits_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2); +static Value *emit_box_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2) +{ + jl_value_t *rt1 = arg1.typ; + jl_value_t *rt2 = arg2.typ; + + int ptr_comparable = 0; // whether this type is unique'd by pointer + if (rt1 == (jl_value_t*)jl_symbol_type || rt2 == (jl_value_t*)jl_symbol_type) + ptr_comparable = 1; + if (jl_is_mutable_datatype(rt1) && // excludes abstract types + rt1 != (jl_value_t*)jl_string_type && // technically mutable, but compared by contents + rt1 != (jl_value_t*)jl_simplevector_type) + ptr_comparable = 1; + if (jl_is_mutable_datatype(rt2) && // excludes abstract types + rt2 != (jl_value_t*)jl_string_type && // technically mutable, but compared by contents + rt2 != (jl_value_t*)jl_simplevector_type) + ptr_comparable = 1; + if (jl_subtype(rt1, (jl_value_t*)jl_type_type) || + jl_subtype(rt2, (jl_value_t*)jl_type_type)) { + // need to use typeseq for most types + ptr_comparable = 0; + if ((jl_is_type_type(rt1) && jl_is_concrete_type(jl_tparam0(rt1))) || + (jl_is_type_type(rt2) && jl_is_concrete_type(jl_tparam0(rt2)))) { + // but can compare some types by pointer + ptr_comparable = 1; + } + } + if (ptr_comparable) { + Value *varg1 = arg1.constant ? literal_pointer_val(ctx, arg1.constant) : arg1.V; + Value *varg2 = arg2.constant ? literal_pointer_val(ctx, arg2.constant) : arg2.V; + assert(varg1 && varg2 && (arg1.isboxed || arg1.TIndex) && (arg2.isboxed || arg2.TIndex) && + "Only boxed types are valid for pointer comparison."); + varg1 = maybe_decay_tracked(varg1); + varg2 = maybe_decay_tracked(varg2); + if (cast(varg1->getType())->getAddressSpace() != cast(varg2->getType())->getAddressSpace()) { + varg1 = decay_derived(varg1); + varg2 = decay_derived(varg2); + } + return ctx.builder.CreateICmpEQ(emit_bitcast(ctx, varg1, T_pint8), + emit_bitcast(ctx, varg2, T_pint8)); + } + + Value *varg1 = mark_callee_rooted(boxed(ctx, arg1)); + Value *varg2 = mark_callee_rooted(boxed(ctx, arg2)); + return ctx.builder.CreateTrunc(ctx.builder.CreateCall(prepare_call(jlegal_func), {varg1, varg2}), T_int1); +} + +static Value *emit_bits_compare(jl_codectx_t &ctx, jl_cgval_t arg1, jl_cgval_t arg2); static Value *emit_bitsunion_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2) { @@ -2252,7 +2330,7 @@ static Value *emit_bitsunion_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, return ctx.builder.CreateAnd(phi, ctx.builder.CreateICmpEQ(arg1.TIndex, arg2.TIndex)); } -static Value *emit_bits_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgval_t &arg2) +static Value *emit_bits_compare(jl_codectx_t &ctx, jl_cgval_t arg1, jl_cgval_t arg2) { bool isboxed; Type *at = julia_type_to_llvm(arg1.typ, &isboxed); @@ -2287,14 +2365,13 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const } if (at->isAggregateType()) { // Struct or Array - assert(arg1.ispointer() && arg2.ispointer()); jl_datatype_t *sty = (jl_datatype_t*)arg1.typ; size_t sz = jl_datatype_size(sty); - Value *varg1 = maybe_decay_tracked(data_pointer(ctx, arg1)); - Value *varg2 = maybe_decay_tracked(data_pointer(ctx, arg2)); - if (sz > 512 && !sty->layout->haspadding) { - varg1 = decay_derived(varg1); - varg2 = decay_derived(varg2); + Value *varg1 = arg1.ispointer() ? maybe_decay_tracked(data_pointer(ctx, arg1)) : arg1.V; + Value *varg2 = arg2.ispointer() ? maybe_decay_tracked(data_pointer(ctx, arg2)) : arg2.V; + if (sz > 512 && !sty->layout->haspadding && sty->layout->npointers == 0) { + varg1 = decay_derived(arg1.ispointer() ? varg1 : value_to_pointer(ctx, arg1).V); + varg2 = decay_derived(arg2.ispointer() ? varg2 : value_to_pointer(ctx, arg2).V); Value *answer = ctx.builder.CreateCall(prepare_call(memcmp_derived_func), { maybe_bitcast(ctx, varg1, T_pint8), maybe_bitcast(ctx, varg2, T_pint8), @@ -2304,45 +2381,57 @@ static Value *emit_bits_compare(jl_codectx_t &ctx, const jl_cgval_t &arg1, const } else { Type *atp = at->getPointerTo(); - if (cast(varg1->getType())->getAddressSpace() != cast(varg2->getType())->getAddressSpace()) { - varg1 = decay_derived(varg1); - varg2 = decay_derived(varg2); - } - varg1 = maybe_bitcast(ctx, varg1, atp); - varg2 = maybe_bitcast(ctx, varg2, atp); + if (arg1.ispointer()) + varg1 = maybe_bitcast(ctx, varg1, atp); + if (arg2.ispointer()) + varg2 = maybe_bitcast(ctx, varg2, atp); jl_svec_t *types = sty->types; Value *answer = ConstantInt::get(T_int1, 1); for (size_t i = 0, l = jl_svec_len(types); i < l; i++) { + // TODO: should we replace this with `subAns = emit_f_is(emit_getfield_knownidx(ctx, arg1, i, arg1.typ), emit_getfield_knownidx(ctx, arg2, i, arg2.typ))` + // or is there any value in all this extra effort?? jl_value_t *fldty = jl_svecref(types, i); if (type_is_ghost(julia_type_to_llvm(fldty))) continue; unsigned byte_offset = jl_field_offset(sty, i); Value *subAns, *fld1, *fld2; - if (isa(at)) { - fld1 = emit_struct_gep(ctx, at, varg1, byte_offset); - fld2 = emit_struct_gep(ctx, at, varg2, byte_offset); - } - else { - fld1 = ctx.builder.CreateConstInBoundsGEP2_32(at, varg1, 0, i); - fld2 = ctx.builder.CreateConstInBoundsGEP2_32(at, varg2, 0, i); - } + unsigned llvm_idx = (i > 0 && isa(at)) ? convert_struct_offset(ctx, at, byte_offset) : i; + if (arg1.ispointer()) + fld1 = ctx.builder.CreateConstInBoundsGEP2_32(at, varg1, 0, llvm_idx); + else + fld1 = ctx.builder.CreateExtractValue(varg1, llvm_idx); + if (arg2.ispointer()) + fld2 = ctx.builder.CreateConstInBoundsGEP2_32(at, varg2, 0, llvm_idx); + else + fld2 = ctx.builder.CreateExtractValue(varg2, llvm_idx); + assert(!jl_field_isptr(sty, i)); if (jl_is_uniontype(fldty)) { unsigned tindex_offset = byte_offset + jl_field_size(sty, i) - 1; - Value *ptindex1 = emit_struct_gep(ctx, at, varg1, tindex_offset); - Value *ptindex2 = emit_struct_gep(ctx, at, varg2, tindex_offset); - Value *tindex1 = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), - ctx.builder.CreateLoad(T_int8, ptindex1)); - Value *tindex2 = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), - ctx.builder.CreateLoad(T_int8, ptindex2)); - subAns = emit_bitsunion_compare(ctx, - mark_julia_slot(fld1, fldty, tindex1, arg1.tbaa), - mark_julia_slot(fld2, fldty, tindex2, arg2.tbaa)); + jl_cgval_t fld1_info; + jl_cgval_t fld2_info; + if (arg1.ispointer()) { + Value *tindex1 = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), + ctx.builder.CreateLoad(T_int8, emit_struct_gep(ctx, at, varg1, tindex_offset))); + fld1_info = mark_julia_slot(fld1, fldty, tindex1, arg1.tbaa); + } + else { + fld1_info = emit_getfield_knownidx(ctx, arg1, i, sty); + } + if (arg2.ispointer()) { + Value *tindex2 = ctx.builder.CreateNUWAdd(ConstantInt::get(T_int8, 1), + ctx.builder.CreateLoad(T_int8, emit_struct_gep(ctx, at, varg2, tindex_offset))); + fld2_info = mark_julia_slot(fld2, fldty, tindex2, arg2.tbaa); + } + else { + fld2_info = emit_getfield_knownidx(ctx, arg2, i, sty); + } + subAns = emit_bitsunion_compare(ctx, fld1_info, fld2_info); } else { assert(jl_is_concrete_type(fldty)); - subAns = emit_bits_compare(ctx, - mark_julia_slot(fld1, fldty, NULL, arg1.tbaa), - mark_julia_slot(fld2, fldty, NULL, arg2.tbaa)); + jl_cgval_t fld1_info = arg1.ispointer() ? mark_julia_slot(fld1, fldty, NULL, arg1.tbaa) : mark_julia_type(ctx, fld1, false, fldty); + jl_cgval_t fld2_info = arg2.ispointer() ? mark_julia_slot(fld2, fldty, NULL, arg2.tbaa) : mark_julia_type(ctx, fld2, false, fldty); + subAns = emit_bits_compare(ctx, fld1_info, fld2_info); } answer = ctx.builder.CreateAnd(answer, subAns); } @@ -2407,45 +2496,7 @@ static Value *emit_f_is(jl_codectx_t &ctx, const jl_cgval_t &arg1, const jl_cgva // if (arg1.tindex || arg2.tindex) // TODO: handle with emit_bitsunion_compare - int ptr_comparable = 0; // whether this type is unique'd by pointer - if (rt1 == (jl_value_t*)jl_symbol_type || rt2 == (jl_value_t*)jl_symbol_type) - ptr_comparable = 1; - if (jl_is_mutable_datatype(rt1) && // excludes abstract types - rt1 != (jl_value_t*)jl_string_type && // technically mutable, but compared by contents - rt1 != (jl_value_t*)jl_simplevector_type) - ptr_comparable = 1; - if (jl_is_mutable_datatype(rt2) && // excludes abstract types - rt2 != (jl_value_t*)jl_string_type && // technically mutable, but compared by contents - rt2 != (jl_value_t*)jl_simplevector_type) - ptr_comparable = 1; - if (jl_subtype(rt1, (jl_value_t*)jl_type_type) || - jl_subtype(rt2, (jl_value_t*)jl_type_type)) { - // need to use typeseq for most types - ptr_comparable = 0; - if ((jl_is_type_type(rt1) && jl_is_concrete_type(jl_tparam0(rt1))) || - (jl_is_type_type(rt2) && jl_is_concrete_type(jl_tparam0(rt2)))) { - // but can compare some types by pointer - ptr_comparable = 1; - } - } - if (ptr_comparable) { - Value *varg1 = arg1.constant ? literal_pointer_val(ctx, arg1.constant) : arg1.V; - Value *varg2 = arg2.constant ? literal_pointer_val(ctx, arg2.constant) : arg2.V; - assert(varg1 && varg2 && (arg1.isboxed || arg1.TIndex) && (arg2.isboxed || arg2.TIndex) && - "Only boxed types are valid for pointer comparison."); - varg1 = maybe_decay_tracked(varg1); - varg2 = maybe_decay_tracked(varg2); - if (cast(varg1->getType())->getAddressSpace() != cast(varg2->getType())->getAddressSpace()) { - varg1 = decay_derived(varg1); - varg2 = decay_derived(varg2); - } - return ctx.builder.CreateICmpEQ(emit_bitcast(ctx, varg1, T_pint8), - emit_bitcast(ctx, varg2, T_pint8)); - } - - Value *varg1 = mark_callee_rooted(boxed(ctx, arg1)); - Value *varg2 = mark_callee_rooted(boxed(ctx, arg2)); - return ctx.builder.CreateTrunc(ctx.builder.CreateCall(prepare_call(jlegal_func), {varg1, varg2}), T_int1); + return emit_box_compare(ctx, arg1, arg2); } static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, @@ -2517,11 +2568,13 @@ static bool emit_builtin_call(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } } - else if (f == jl_builtin__apply && nargs == 2 && ctx.vaSlot > 0) { + else if (((f == jl_builtin__apply && nargs == 2) || + (f == jl_builtin__apply_iterate && nargs == 3)) && ctx.vaSlot > 0) { + int arg_start = f == jl_builtin__apply ? 2 : 3; // turn Core._apply(f, Tuple) ==> f(Tuple...) using the jlcall calling convention if Tuple is the va allocation - if (LoadInst *load = dyn_cast_or_null(argv[2].V)) { + if (LoadInst *load = dyn_cast_or_null(argv[arg_start].V)) { if (load->getPointerOperand() == ctx.slots[ctx.vaSlot].boxroot && ctx.argArray) { - Value *theF = maybe_decay_untracked(boxed(ctx, argv[1])); + Value *theF = maybe_decay_untracked(boxed(ctx, argv[arg_start-1])); Value *nva = emit_n_varargs(ctx); #ifdef _P64 nva = ctx.builder.CreateTrunc(nva, T_int32); @@ -3096,7 +3149,7 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_code_instance_t case jl_returninfo_t::Ghosts: break; case jl_returninfo_t::SRet: - result = emit_static_alloca(ctx, cft->getParamType(0)->getContainedType(0)); + result = emit_static_alloca(ctx, cft->getParamType(0)->getPointerElementType()); argvals[idx] = result; idx++; break; @@ -3117,21 +3170,16 @@ static jl_cgval_t emit_call_specfun_other(jl_codectx_t &ctx, jl_code_instance_t continue; assert(idx < nfargs); Type *at = cft->getParamType(idx); - const jl_cgval_t &arg = argv[i]; + jl_cgval_t arg = argv[i]; if (isboxed) { - assert(at == T_prjlvalue && (et == T_pjlvalue || et == T_prjlvalue)); + assert(at == T_prjlvalue && et == T_prjlvalue); argvals[idx] = maybe_decay_untracked(boxed(ctx, arg)); } else if (et->isAggregateType()) { - if (!arg.ispointer()) { - // This can happen in dead code if there's a type mismatch - // Exit early - CreateTrap(ctx.builder); - return jl_cgval_t(); - } + if (!arg.ispointer()) + arg = value_to_pointer(ctx, arg); // can lazy load on demand, no copy needed assert(at == PointerType::get(et, AddressSpace::Derived)); - assert(arg.ispointer()); argvals[idx] = decay_derived(maybe_bitcast(ctx, data_pointer(ctx, arg), at)); } @@ -3668,9 +3716,9 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) bool allunbox; size_t min_align, nbytes; dest = try_emit_union_alloca(ctx, ((jl_uniontype_t*)phiType), allunbox, min_align, nbytes); - Value *phi = try_emit_union_alloca(ctx, ((jl_uniontype_t*)phiType), allunbox, min_align, nbytes); - Value *ptr = NULL; if (dest) { + Instruction *phi = dest->clone(); + phi->insertAfter(dest); PHINode *Tindex_phi = PHINode::Create(T_int8, jl_array_len(edges), "tindex_phi"); BB->getInstList().insert(InsertPt, Tindex_phi); PHINode *ptr_phi = PHINode::Create(T_prjlvalue, jl_array_len(edges), "ptr_phi"); @@ -3684,7 +3732,7 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) ctx.builder.CreateMemCpy(phi, dest, nbytes, min_align, false); #endif ctx.builder.CreateLifetimeEnd(dest); - ptr = ctx.builder.CreateSelect(isboxed, + Value *ptr = ctx.builder.CreateSelect(isboxed, maybe_bitcast(ctx, decay_derived(ptr_phi), T_pint8), maybe_bitcast(ctx, decay_derived(phi), T_pint8)); jl_cgval_t val = mark_julia_slot(ptr, phiType, Tindex_phi, tbaa_stack); // XXX: this TBAA is wrong for ptr_phi @@ -3706,8 +3754,6 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) } bool isboxed; Type *vtype = julia_type_to_llvm(phiType, &isboxed); - if (isboxed) - vtype = T_prjlvalue; // The frontend should really not emit this, but we allow it // for convenience. if (type_is_ghost(vtype)) { @@ -3720,6 +3766,8 @@ static void emit_phinode_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) jl_cgval_t slot; PHINode *value_phi = NULL; if (vtype->isAggregateType()) { + // the value will be moved into dest in the predecessor critical block. + // here it's moved into phi in the successor (from dest) dest = emit_static_alloca(ctx, vtype); Value *phi = emit_static_alloca(ctx, vtype); #if JL_LLVM_VERSION >= 70000 @@ -3773,7 +3821,8 @@ static void emit_ssaval_assign(jl_codectx_t &ctx, ssize_t idx, jl_value_t *r) ctx.ssavalue_assigned.at(idx) = true; } -static void emit_varinfo_assign(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_cgval_t rval_info, jl_value_t *l=NULL) { +static void emit_varinfo_assign(jl_codectx_t &ctx, jl_varinfo_t &vi, jl_cgval_t rval_info, jl_value_t *l=NULL) +{ if (!vi.used) return; @@ -4342,16 +4391,12 @@ static void emit_cfunc_invalidate( Value *arg_v = &*AI; ++AI; Type *at = arg_v->getType(); - if (isboxed) { - assert(at == T_prjlvalue && et == T_pjlvalue); - myargs[i] = mark_julia_type(ctx, arg_v, true, jt); - } - else if (et->isAggregateType()) { + if (!isboxed && et->isAggregateType()) { myargs[i] = mark_julia_slot(arg_v, jt, NULL, tbaa_const); } else { assert(at == et); - myargs[i] = mark_julia_type(ctx, arg_v, false, jt); + myargs[i] = mark_julia_type(ctx, arg_v, isboxed, jt); } (void)at; } @@ -4753,15 +4798,17 @@ static Function* gen_cfun_wrapper( Value *result; if (jlfunc_sret || returninfo.cc == jl_returninfo_t::Union) { // fuse the two sret together, or emit an alloca to hold it - if (sig.sret && jlfunc_sret) + if (sig.sret && jlfunc_sret) { result = emit_bitcast(ctx, sretPtr, cft->getParamType(0)); - else - result = emit_static_alloca(ctx, cft->getParamType(0)->getContainedType(0)); + } + else { + result = emit_static_alloca(ctx, cft->getParamType(0)->getPointerElementType()); + } args.push_back(result); } for (size_t i = 0; i < nargs + 1; i++) { // figure out how to repack the arguments - const jl_cgval_t &inputarg = inputargs[i]; + jl_cgval_t &inputarg = inputargs[i]; Value *arg; jl_value_t *spect = jl_nth_slot_type(lam->specTypes, i); bool isboxed; @@ -4774,6 +4821,8 @@ static Function* gen_cfun_wrapper( } else if (T->isAggregateType()) { // aggregate types are passed by pointer + if (!inputarg.ispointer()) + inputarg = value_to_pointer(ctx, inputarg); arg = maybe_bitcast(ctx, decay_derived(data_pointer(ctx, inputarg)), T->getPointerTo()); } @@ -5160,7 +5209,7 @@ static Function *jl_cfunction_object(jl_value_t *ff, jl_value_t *declrt, jl_tupl } // generate a julia-callable function that calls f (AKA lam) -static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlretty, const jl_returninfo_t &f, StringRef funcName, Module *M) +static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlretty, const jl_returninfo_t &f, int retarg, StringRef funcName, Module *M) { Function *w = Function::Create(jl_func_sig, GlobalVariable::ExternalLinkage, funcName, M); add_return_attr(w, Attribute::NonNull); @@ -5185,9 +5234,10 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret ctx.builder.SetCurrentDebugLocation(noDbg); allocate_gc_frame(ctx, b0); + // TODO: replace this with emit_call_specfun_other? FunctionType *ftype = f.decl->getFunctionType(); size_t nfargs = ftype->getNumParams(); - Value **args = (Value**) alloca(nfargs*sizeof(Value*)); + Value **args = (Value**) alloca(nfargs * sizeof(Value*)); unsigned idx = 0; AllocaInst *result; switch (f.cc) { @@ -5196,7 +5246,7 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret case jl_returninfo_t::Ghosts: break; case jl_returninfo_t::SRet: - result = ctx.builder.CreateAlloca(ftype->getParamType(0)->getContainedType(0)); + result = ctx.builder.CreateAlloca(ftype->getParamType(0)->getPointerElementType()); args[idx] = result; idx++; break; @@ -5208,6 +5258,7 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret idx++; break; } + for (size_t i = 0; i < jl_nparams(lam->specTypes) && idx < nfargs; ++i) { jl_value_t *ty = jl_nth_slot_type(lam->specTypes, i); bool isboxed; @@ -5219,7 +5270,7 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret theArg = funcArg; } else { - Value *argPtr = ctx.builder.CreateInBoundsGEP(argArray, ConstantInt::get(T_size, i - 1)); + Value *argPtr = ctx.builder.CreateConstInBoundsGEP1_32(T_prjlvalue, argArray, i - 1); theArg = maybe_mark_load_dereferenceable(ctx.builder.CreateLoad(argPtr), false, ty); } if (!isboxed) { @@ -5235,28 +5286,38 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret call->setAttributes(f.decl->getAttributes()); jl_cgval_t retval; - switch (f.cc) { - case jl_returninfo_t::Boxed: - retval = mark_julia_type(ctx, call, true, jlretty); - break; - case jl_returninfo_t::Register: - retval = mark_julia_type(ctx, call, false, jlretty); - break; - case jl_returninfo_t::SRet: - retval = mark_julia_slot(result, jlretty, NULL, tbaa_stack); - break; - case jl_returninfo_t::Union: - // result is technically not right here, but `boxed` will only look at it - // for the unboxed values, so it's ok. - retval = mark_julia_slot(result, - jlretty, - ctx.builder.CreateExtractValue(call, 1), - tbaa_stack); - retval.Vboxed = ctx.builder.CreateExtractValue(call, 0); - break; - case jl_returninfo_t::Ghosts: - retval = mark_julia_slot(NULL, jlretty, call, tbaa_stack); - break; + if (retarg != -1) { + Value *theArg; + if (retarg == 0) + theArg = funcArg; + else + theArg = ctx.builder.CreateLoad(ctx.builder.CreateConstInBoundsGEP1_32(T_prjlvalue, argArray, retarg - 1)); + retval = mark_julia_type(ctx, theArg, true, jl_any_type); + } + else { + switch (f.cc) { + case jl_returninfo_t::Boxed: + retval = mark_julia_type(ctx, call, true, jlretty); + break; + case jl_returninfo_t::Register: + retval = mark_julia_type(ctx, call, false, jlretty); + break; + case jl_returninfo_t::SRet: + retval = mark_julia_slot(result, jlretty, NULL, tbaa_stack); + break; + case jl_returninfo_t::Union: + // result is technically not right here, but `boxed` will only look at it + // for the unboxed values, so it's ok. + retval = mark_julia_slot(result, + jlretty, + ctx.builder.CreateExtractValue(call, 1), + tbaa_stack); + retval.Vboxed = ctx.builder.CreateExtractValue(call, 0); + break; + case jl_returninfo_t::Ghosts: + retval = mark_julia_slot(NULL, jlretty, call, tbaa_stack); + break; + } } ctx.builder.CreateRet(boxed(ctx, retval)); assert(!ctx.roots); @@ -5344,20 +5405,22 @@ static jl_returninfo_t get_specsig_function(Module *M, const std::string &name, else { props.cc = jl_returninfo_t::Register; } - } else { - rt = T_prjlvalue; } } + AttributeList attributes; // function declaration attributes if (props.cc == jl_returninfo_t::SRet) { - attributes = attributes.addAttribute(jl_LLVMContext, 1, Attribute::StructRet); - attributes = attributes.addAttribute(jl_LLVMContext, 1, Attribute::NoAlias); - attributes = attributes.addAttribute(jl_LLVMContext, 1, Attribute::NoCapture); + unsigned argno = 1; + attributes = attributes.addAttribute(jl_LLVMContext, argno, Attribute::StructRet); + attributes = attributes.addAttribute(jl_LLVMContext, argno, Attribute::NoAlias); + attributes = attributes.addAttribute(jl_LLVMContext, argno, Attribute::NoCapture); } if (props.cc == jl_returninfo_t::Union) { - attributes = attributes.addAttribute(jl_LLVMContext, 1, Attribute::NoAlias); - attributes = attributes.addAttribute(jl_LLVMContext, 1, Attribute::NoCapture); + unsigned argno = 1; + attributes = attributes.addAttribute(jl_LLVMContext, argno, Attribute::NoAlias); + attributes = attributes.addAttribute(jl_LLVMContext, argno, Attribute::NoCapture); } + for (size_t i = 0; i < jl_nparams(sig); i++) { jl_value_t *jt = jl_tparam(sig, i); bool isboxed; @@ -5370,10 +5433,9 @@ static jl_returninfo_t get_specsig_function(Module *M, const std::string &name, attributes = attributes.addParamAttribute(jl_LLVMContext, argno, Attribute::ReadOnly); ty = PointerType::get(ty, AddressSpace::Derived); } - if (isboxed) - ty = PointerType::get(cast(ty)->getElementType(), AddressSpace::Tracked); fsig.push_back(ty); } + FunctionType *ftype = FunctionType::get(rt, fsig, false); Function *f = M ? cast_or_null(M->getNamedValue(name)) : NULL; if (f == NULL) { @@ -5582,15 +5644,38 @@ static std::unique_ptr emit_function( jl_setup_module(M, params); jl_returninfo_t returninfo = {}; Function *f = NULL; + bool has_sret = false; if (specsig) { // assumes !va and !needsparams returninfo = get_specsig_function(M, funcName.str(), lam->specTypes, jlrettype); f = returninfo.decl; - ctx.has_sret = (returninfo.cc == jl_returninfo_t::SRet || returninfo.cc == jl_returninfo_t::Union); + has_sret = (returninfo.cc == jl_returninfo_t::SRet || returninfo.cc == jl_returninfo_t::Union); jl_init_function(f); + // common pattern: see if all return statements are an argument in that + // case the apply-generic call can re-use the original box for the return + int retarg = [stmts, nreq]() { + int retarg = -1; + for (size_t i = 0; i < jl_array_len(stmts); ++i) { + jl_value_t *stmt = jl_array_ptr_ref(stmts, i); + if (jl_is_expr(stmt) && ((jl_expr_t*)stmt)->head == return_sym) { + stmt = jl_exprarg(stmt, 0); + if (!jl_is_slot(stmt)) + return -1; + unsigned sl = jl_slot_number(stmt) - 1; + if (sl >= nreq) + return -1; + if (retarg == -1) + retarg = sl; + else if ((unsigned)retarg != sl) + return -1; + } + } + return retarg; + }(); + std::stringstream wrapName; wrapName << "jfptr_" << unadorned_name << "_" << globalUnique; - Function *fwrap = gen_invoke_wrapper(lam, jlrettype, returninfo, wrapName.str(), M); + Function *fwrap = gen_invoke_wrapper(lam, jlrettype, returninfo, retarg, wrapName.str(), M); declarations->functionObject = strdup(fwrap->getName().str().c_str()); } else { @@ -5712,7 +5797,7 @@ static std::unique_ptr emit_function( varinfo.dinfo = dbuilder.createParameterVariable( SP, // Scope (current function will be fill in later) jl_symbol_name(argname), // Variable name - ctx.has_sret + i + 1, // Argument number (1-based) + has_sret + i + 1, // Argument number (1-based) topfile, // File toplineno == -1 ? 0 : toplineno, // Line // Variable type @@ -5724,7 +5809,7 @@ static std::unique_ptr emit_function( ctx.slots[ctx.vaSlot].dinfo = dbuilder.createParameterVariable( SP, // Scope (current function will be fill in later) std::string(jl_symbol_name(slot_symbol(ctx, ctx.vaSlot))) + "...", // Variable name - ctx.has_sret + nreq + 1, // Argument number (1-based) + has_sret + nreq + 1, // Argument number (1-based) topfile, // File toplineno == -1 ? 0 : toplineno, // Line (for now, use lineno of the function) julia_type_to_di(ctx.slots[ctx.vaSlot].value.typ, &dbuilder, false), @@ -5738,7 +5823,7 @@ static std::unique_ptr emit_function( continue; // LLVM 4.0: Assume the variable has default alignment varinfo.dinfo = dbuilder.createAutoVariable( - SP, // Scope (current function will be fill in later) + SP, // Scope (current function will be fill in later) jl_symbol_name(s), // Variable name topfile, // File toplineno == -1 ? 0 : toplineno, // Line (for now, use lineno of the function) @@ -5935,7 +6020,7 @@ static std::unique_ptr emit_function( return theArg; }; - if (ctx.has_sret) + if (has_sret) AI++; // skip sret slot for (i = 0; i < nreq; i++) { jl_sym_t *s = (jl_sym_t*)jl_array_ptr_ref(src->slotnames, i); @@ -6146,7 +6231,7 @@ static std::unique_ptr emit_function( std::vector scope_stack; std::vector scope_list_stack; { - size_t nstmts = jl_array_len(src->code); + size_t nstmts = jl_array_len(stmts); aliasscopes.resize(nstmts + 1, nullptr); MDBuilder mbuilder(jl_LLVMContext); MDNode *alias_domain = mbuilder.createAliasScopeDomain(ctx.name); @@ -6338,7 +6423,7 @@ static std::unique_ptr emit_function( Value *isboxed_union = NULL; Value *retval; - Value *sret = ctx.has_sret ? &*f->arg_begin() : NULL; + Value *sret = has_sret ? f->arg_begin() : NULL; Type *retty = f->getReturnType(); switch (returninfo.cc) { case jl_returninfo_t::Boxed: @@ -6505,18 +6590,18 @@ static std::unique_ptr emit_function( ctx.builder.SetCurrentDebugLocation(noDbg); ctx.builder.ClearInsertionPoint(); - auto undef_value_for_type = [&](jl_value_t *phiType, Type *UndefType) { - Value *VNUndef; - if (UndefType == T_prjlvalue) { - VNUndef = (llvm::Value*)ConstantPointerNull::get(cast(T_prjlvalue)); - } else { - VNUndef = (llvm::Value*)UndefValue::get(UndefType); - } - return VNUndef; + auto undef_value_for_type = [&](Type *T) { + Value *undef; + if (T == T_prjlvalue) + // make sure gc pointers (including ptr_phi of union-split) are initialized to NULL + undef = Constant::getNullValue(T); + else + undef = UndefValue::get(T); + return undef; }; // Codegen Phi nodes - std::map, BasicBlock*> BB_rewrite_map; + std::map, BasicBlock*> BB_rewrite_map; std::vector ToDelete; for (auto &tup : ctx.PhiNodes) { jl_cgval_t phi_result; @@ -6529,119 +6614,72 @@ static std::unique_ptr emit_function( jl_array_t *edges = (jl_array_t*)jl_fieldref_noalloc(r, 0); jl_array_t *values = (jl_array_t*)jl_fieldref_noalloc(r, 1); PHINode *TindexN = cast_or_null(phi_result.TIndex); + DenseSet preds; for (size_t i = 0; i < jl_array_len(edges); ++i) { size_t edge = jl_unbox_long(jl_array_ptr_ref(edges, i)); jl_value_t *value = jl_array_ptr_ref(values, i); - Value *V = NULL; - BasicBlock *IncomingBB = come_from_bb[edge]; - BasicBlock *FromBB = IncomingBB; - std::pair LookupKey(IncomingBB, PhiBB); - if (BB_rewrite_map.count(LookupKey)) { - FromBB = BB_rewrite_map[LookupKey]; - } + // This edge value is undef, handle it the same as if the edge wasn't listed at all + if (!value) + continue; + BasicBlock *FromBB = come_from_bb[edge]; // This edge was statically unreachable. Don't codegen it. if (!FromBB) continue; - // We folded this branch to an unconditional branch, only codegen it once - if (cast(FromBB->getTerminator())->isUnconditional()) { - bool found = false; + // see if this edge has already been rewritten + // (we'll continue appending blocks to the current end) + std::pair LookupKey(FromBB, PhiBB); + if (BB_rewrite_map.count(LookupKey)) { + FromBB = BB_rewrite_map[LookupKey]; + } + if (!preds.insert(FromBB).second) { + // Only codegen this branch once for each PHI (the expression must be the same on all branches) +#ifndef NDEBUG for (size_t j = 0; j < i; ++j) { size_t j_edge = jl_unbox_long(jl_array_ptr_ref(edges, j)); if (j_edge == edge) { - found = true; assert(jl_egal(value, jl_array_ptr_ref(values, j))); } } - if (found) - continue; - } -#ifndef JL_NDEBUG - if (FromBB) { - bool found_pred = false; - for (BasicBlock *pred : predecessors(PhiBB)) { - found_pred = pred == FromBB; - if (found_pred) - break; - } - assert(found_pred); - } #endif - ctx.builder.SetInsertPoint(FromBB->getTerminator()); - if (dest) - ctx.builder.CreateLifetimeStart(dest); - jl_cgval_t val; - if (!value || jl_is_ssavalue(value)) { - ssize_t idx = value ? ((jl_ssavalue_t*)value)->id : 0; - idx -= 1; - if (!value || !ctx.ssavalue_assigned.at(idx)) { - Value *RTindex = TindexN ? UndefValue::get(T_int8) : NULL; - if (VN) { // otherwise, it's all-unboxed - Value *undef; - if (isa(VN->getType())) { - bool isboxed; - Type *lphity = julia_type_to_llvm(phiType, &isboxed); - if (!isboxed) { - // the emit_phinode_assign emitted a memcpy in this case, - // so this needs to ensure the pointer is valid, while the contents are undef - undef = decay_derived(emit_static_alloca(ctx, lphity)); - } - else { - // but make sure gc pointers (including ptr_phi of union-split) are NULL - undef = ConstantPointerNull::get(cast(VN->getType())); - if (TindexN) // let the runtime / optimizer know this is unknown / boxed / null, so that it won't try to union_move / copy it later - RTindex = ConstantInt::get(T_int8, 0x80); - } - } - else { - undef = undef_value_for_type(phiType, VN->getType()); - } - VN->addIncoming(undef, FromBB); - } - if (TindexN) - TindexN->addIncoming(RTindex, FromBB); - continue; - } - val = ctx.SAvalues.at(idx); - } - else { - val = emit_expr(ctx, value); + continue; } - if (val.constant) - val = mark_julia_const(val.constant); // be over-conservative at making sure `.typ` is set concretely, not tindex + assert(find(pred_begin(PhiBB), pred_end(PhiBB), FromBB) != pred_end(PhiBB)); // consistency check TerminatorInst *terminator = FromBB->getTerminator(); - if (!isa(terminator) || - (cast(terminator)->isConditional() && - !(terminator->getSuccessor(0) == terminator->getSuccessor(1)))) { - bool found = false; - for (size_t i = 0; i < terminator->getNumSuccessors(); ++i) { - if (terminator->getSuccessor(i) == PhiBB) { - // Can't use `llvm::SplitCriticalEdge` here because - // we may have invalid phi nodes in the destination. - BasicBlock *NewBB = BasicBlock::Create(terminator->getContext(), - FromBB->getName() + "." + PhiBB->getName() + "_crit_edge"); - terminator->setSuccessor(i, NewBB); - Function::iterator FBBI = FromBB->getIterator(); - ctx.f->getBasicBlockList().insert(++FBBI, NewBB); - ctx.builder.SetInsertPoint(NewBB); - terminator = BranchInst::Create(PhiBB); - found = true; - break; - } + if (!terminator->getParent()->getUniqueSuccessor()) { + // Can't use `llvm::SplitCriticalEdge` here because + // we may have invalid phi nodes in the destination. + BasicBlock *NewBB = BasicBlock::Create(terminator->getContext(), + FromBB->getName() + "." + PhiBB->getName() + "_crit_edge"); + Function::iterator FBBI = FromBB->getIterator(); + ctx.f->getBasicBlockList().insert(++FBBI, NewBB); // insert after existing block +#if JL_LLVM_VERSION >= 90000 + terminator->replaceSuccessorWith(PhiBB, NewBB); +#else + for (unsigned Idx = 0, NumSuccessors = terminator->getNumSuccessors(); Idx != NumSuccessors; ++Idx) { + if (terminator->getSuccessor(Idx) == PhiBB) + terminator->setSuccessor(Idx, NewBB); } - assert(found); - (void)found; +#endif + DebugLoc Loc = terminator->getDebugLoc(); + terminator = BranchInst::Create(PhiBB); + terminator->setDebugLoc(Loc); + ctx.builder.SetInsertPoint(NewBB); } else { terminator->removeFromParent(); ctx.builder.SetInsertPoint(FromBB); } + if (dest) + ctx.builder.CreateLifetimeStart(dest); + jl_cgval_t val = emit_expr(ctx, value); + if (val.constant) + val = mark_julia_const(val.constant); // be over-conservative at making sure `.typ` is set concretely, not tindex if (!jl_is_uniontype(phiType) || !TindexN) { + Type *lty = julia_type_to_llvm(phiType); if (VN) { - // XXX: this code assumes that `val` is of type `phiType` statically, - // that must be true dynamically, but we have not propagated that information here, - // and thus this might generate invalid code + Value *V; if (val.typ == (jl_value_t*)jl_bottom_type) { - V = undef_value_for_type(phiType, VN->getType()); + V = undef_value_for_type(VN->getType()); } else if (VN && VN->getType() == T_prjlvalue) { // Includes the jl_is_uniontype(phiType) && !TindexN case @@ -6652,27 +6690,17 @@ static std::unique_ptr emit_function( } VN->addIncoming(V, ctx.builder.GetInsertBlock()); assert(!TindexN); - } else if (dest && val.typ != (jl_value_t*)jl_bottom_type) { -#if JL_LLVM_VERSION >= 70000 - ctx.builder.CreateMemCpy(maybe_decay_tracked(dest), - jl_datatype_align(phiType), - maybe_decay_tracked(data_pointer(ctx, val)), - 0, - jl_datatype_size(phiType), - false); -#else - ctx.builder.CreateMemCpy(maybe_decay_tracked(dest), - maybe_decay_tracked(data_pointer(ctx, val)), - jl_datatype_size(phiType), - jl_datatype_align(phiType), - false); -#endif + } + else if (dest && val.typ != (jl_value_t*)jl_bottom_type) { + assert(lty != T_prjlvalue); + (void)emit_unbox(ctx, lty, val, phiType, maybe_decay_tracked(dest)); } } else { - Value *RTindex = NULL; + Value *RTindex; + Value *V; if (val.typ == (jl_value_t*)jl_bottom_type) { - V = undef_value_for_type(phiType, VN->getType()); + V = undef_value_for_type(VN->getType()); RTindex = UndefValue::get(T_int8); } else if (jl_is_concrete_type(val.typ) || val.constant) { @@ -6717,24 +6745,32 @@ static std::unique_ptr emit_function( if (TindexN) TindexN->addIncoming(RTindex, ctx.builder.GetInsertBlock()); } + // put the branch back at the end of our current basic block ctx.builder.Insert(terminator); - // Check any phi nodes in the Phi block to see if by splitting the edges, - // we made things inconsistent - if (FromBB != ctx.builder.GetInsertBlock()) { - BB_rewrite_map[LookupKey] = ctx.builder.GetInsertBlock(); + // Record the current tail of this Phi edge in the rewrite map and + // check any phi nodes in the Phi block to see if by emitting on the edges + // we made things inconsistent. + BasicBlock *NewBB = ctx.builder.GetInsertBlock(); + if (FromBB != NewBB) { + BB_rewrite_map[LookupKey] = NewBB; + preds.insert(NewBB); +#if JL_LLVM_VERSION >= 90000 + PhiBB->replacePhiUsesWith(FromBB, NewBB); +#else for (BasicBlock::iterator I = PhiBB->begin(); isa(I); ++I) { PHINode *PN = cast(I); ssize_t BBIdx = PN->getBasicBlockIndex(FromBB); if (BBIdx == -1) continue; - PN->setIncomingBlock(BBIdx, ctx.builder.GetInsertBlock()); + PN->setIncomingBlock(BBIdx, NewBB); } +#endif } + ctx.builder.ClearInsertionPoint(); } // In LLVM IR it is illegal to have phi nodes without incoming values, even if - // there are no operands, so delete any such phi nodes - if (pred_begin(PhiBB) == pred_end(PhiBB)) - { + // there are no operands (no incoming branches), so delete any such phi nodes + if (pred_empty(PhiBB)) { if (VN) ToDelete.push_back(VN); if (TindexN) @@ -6742,28 +6778,23 @@ static std::unique_ptr emit_function( continue; } // Julia PHINodes may be incomplete with respect to predecessors, LLVM's may not - Value *VNUndef = nullptr; - if (VN || TindexN) { - for (auto *pred : predecessors(PhiBB)) { - PHINode *PhiN = VN ? VN : TindexN; - bool found = false; - for (size_t i = 0; i < PhiN->getNumIncomingValues(); ++i) { - found = pred == PhiN->getIncomingBlock(i); - if (found) - break; - } - if (!found) { - if (VN) { - if (!VNUndef) { - VNUndef = undef_value_for_type(phiType, VN->getType()); - } - VN->addIncoming(VNUndef, pred); - } - if (TindexN) { - TindexN->addIncoming(UndefValue::get(TindexN->getType()), pred); - } - } + for (auto *FromBB : predecessors(PhiBB)) { + if (preds.count(FromBB)) + continue; + ctx.builder.SetInsertPoint(FromBB->getTerminator()); + // PHI is undef on this branch. But still may need to put a valid pointer in place. + Value *RTindex = TindexN ? UndefValue::get(T_int8) : NULL; + if (VN) { + Value *undef = undef_value_for_type(VN->getType()); + VN->addIncoming(undef, FromBB); + if (TindexN) // let the runtime / optimizer know this is unknown / boxed / null, so that it won't try to union_move / copy it later + RTindex = ConstantInt::get(T_int8, 0x80); } + if (TindexN) + TindexN->addIncoming(RTindex, FromBB); + if (dest) + ctx.builder.CreateLifetimeStart(dest); + ctx.builder.ClearInsertionPoint(); } } @@ -6873,8 +6904,10 @@ static std::unique_ptr emit_function( std::pair tbaa_make_child(const char *name, MDNode *parent=nullptr, bool isConstant=false) { MDBuilder mbuilder(jl_LLVMContext); - if (tbaa_root == nullptr) - tbaa_root = mbuilder.createTBAARoot("jtbaa"); + if (tbaa_root == nullptr) { + MDNode *jtbaa = mbuilder.createTBAARoot("jtbaa"); + tbaa_root = mbuilder.createTBAAScalarTypeNode("jtbaa", jtbaa); + } MDNode *scalar = mbuilder.createTBAAScalarTypeNode(name, parent ? parent : tbaa_root); MDNode *n = mbuilder.createTBAAStructTagNode(scalar, scalar, 0, isConstant); return std::make_pair(n, scalar); @@ -6942,7 +6975,9 @@ extern "C" void jl_fptr_to_llvm(void *fptr, jl_code_instance_t *lam, int specsig static void init_julia_llvm_meta(void) { tbaa_gcframe = tbaa_make_child("jtbaa_gcframe").first; - tbaa_stack = tbaa_make_child("jtbaa_stack").first; + MDNode *tbaa_stack_scalar; + std::tie(tbaa_stack, tbaa_stack_scalar) = tbaa_make_child("jtbaa_stack"); + tbaa_unionselbyte = tbaa_make_child("jtbaa_unionselbyte", tbaa_stack_scalar).first; MDNode *tbaa_data_scalar; std::tie(tbaa_data, tbaa_data_scalar) = tbaa_make_child("jtbaa_data"); tbaa_binding = tbaa_make_child("jtbaa_binding", tbaa_data_scalar).first; @@ -6962,7 +6997,6 @@ static void init_julia_llvm_meta(void) tbaa_arrayoffset = tbaa_make_child("jtbaa_arrayoffset", tbaa_array_scalar).first; tbaa_const = tbaa_make_child("jtbaa_const", nullptr, true).first; tbaa_arrayselbyte = tbaa_make_child("jtbaa_arrayselbyte", tbaa_array_scalar).first; - tbaa_unionselbyte = tbaa_make_child("jtbaa_unionselbyte", tbaa_data_scalar).first; Thunk = Attribute::get(jl_LLVMContext, "thunk"); } @@ -7278,6 +7312,7 @@ static void init_julia_llvm_env(Module *m) builtin_func_map[jl_f_typeassert] = jlcall_func_to_llvm("jl_f_typeassert", &jl_f_typeassert, m); builtin_func_map[jl_f_ifelse] = jlcall_func_to_llvm("jl_f_ifelse", &jl_f_ifelse, m); builtin_func_map[jl_f__apply] = jlcall_func_to_llvm("jl_f__apply", &jl_f__apply, m); + builtin_func_map[jl_f__apply_iterate] = jlcall_func_to_llvm("jl_f__apply_iterate", &jl_f__apply_iterate, m); builtin_func_map[jl_f__apply_pure] = jlcall_func_to_llvm("jl_f__apply_pure", &jl_f__apply_pure, m); builtin_func_map[jl_f__apply_latest] = jlcall_func_to_llvm("jl_f__apply_latest", &jl_f__apply_latest, m); builtin_func_map[jl_f_throw] = jlcall_func_to_llvm("jl_f_throw", &jl_f_throw, m); diff --git a/src/codegen_shared.h b/src/codegen_shared.h index aa4756256a386..6ecdfd3afc1b4 100644 --- a/src/codegen_shared.h +++ b/src/codegen_shared.h @@ -1,7 +1,9 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license +#include #include #include +#include enum AddressSpace { Generic = 0, @@ -17,6 +19,15 @@ enum AddressSpace { #define JLCALL_F_CC (CallingConv::ID)37 // (jl_value_t *arg0, jl_value_t **argv, uint32_t nargv) #define JLCALL_F2_CC (CallingConv::ID)38 // (jl_value_t *arg0, jl_value_t **argv, uint32_t nargv, jl_value_t *extra) +// return how many Tracked pointers are in T (count > 0), +// and if there is anything else in T (all == false) +struct CountTrackedPointers { + unsigned count = 0; + bool all = true; + bool derived = false; + CountTrackedPointers(llvm::Type *T); +}; + static inline void llvm_dump(llvm::Value *v) { v->print(llvm::dbgs(), true); diff --git a/src/datatype.c b/src/datatype.c index 9cf1ec58e09e9..17a780f59d7c8 100644 --- a/src/datatype.c +++ b/src/datatype.c @@ -99,29 +99,22 @@ jl_datatype_t *jl_new_uninitialized_datatype(void) } static jl_datatype_layout_t *jl_get_layout(uint32_t nfields, + uint32_t npointers, uint32_t alignment, int haspadding, - jl_fielddesc32_t desc[]) JL_NOTSAFEPOINT + jl_fielddesc32_t desc[], + uint32_t pointers[]) JL_NOTSAFEPOINT { // compute the smallest fielddesc type that can hold the layout description int fielddesc_type = 0; - uint32_t npointers = 0; - // First pointer field - uint32_t first_ptr = (uint32_t)-1; - // Last pointer field - uint32_t last_ptr = 0; if (nfields > 0) { uint32_t max_size = 0; uint32_t max_offset = desc[nfields - 1].offset; + if (npointers > 0 && pointers[npointers - 1] > max_offset) + max_offset = pointers[npointers - 1]; for (size_t i = 0; i < nfields; i++) { if (desc[i].size > max_size) max_size = desc[i].size; - if (desc[i].isptr) { - npointers++; - if (first_ptr == (uint32_t)-1) - first_ptr = i; - last_ptr = i; - } } jl_fielddesc8_t maxdesc8 = { 0, max_size, max_offset }; jl_fielddesc16_t maxdesc16 = { 0, max_size, max_offset }; @@ -138,25 +131,16 @@ static jl_datatype_layout_t *jl_get_layout(uint32_t nfields, } // allocate a new descriptor + // TODO: lots of these are the same--take advantage of the fact these are immutable to combine them uint32_t fielddesc_size = jl_fielddesc_size(fielddesc_type); - int has_padding = nfields && npointers; - jl_datatype_layout_t *flddesc = - (jl_datatype_layout_t*)jl_gc_perm_alloc(sizeof(jl_datatype_layout_t) + - nfields * fielddesc_size + - (has_padding ? sizeof(uint32_t) : 0), 0, 4, 0); - if (has_padding) { - if (first_ptr > UINT16_MAX) - first_ptr = UINT16_MAX; - last_ptr = nfields - last_ptr - 1; - if (last_ptr > UINT16_MAX) - last_ptr = UINT16_MAX; - flddesc = (jl_datatype_layout_t*)(((char*)flddesc) + sizeof(uint32_t)); - jl_datatype_layout_n_nonptr(flddesc) = (first_ptr << 16) | last_ptr; - } + jl_datatype_layout_t *flddesc = (jl_datatype_layout_t*)jl_gc_perm_alloc( + sizeof(jl_datatype_layout_t) + nfields * fielddesc_size + (npointers << fielddesc_type), + 0, 4, 0); flddesc->nfields = nfields; flddesc->alignment = alignment; flddesc->haspadding = haspadding; flddesc->fielddesc_type = fielddesc_type; + flddesc->npointers = npointers; // fill out the fields of the new descriptor jl_fielddesc8_t* desc8 = (jl_fielddesc8_t*)jl_dt_layout_fields(flddesc); @@ -179,12 +163,20 @@ static jl_datatype_layout_t *jl_get_layout(uint32_t nfields, desc32[i].isptr = desc[i].isptr; } } - uint32_t nexp = 0; - while (npointers >= 0x10000) { - nexp++; - npointers = npointers >> 1; + uint8_t* ptrs8 = (uint8_t*)jl_dt_layout_ptrs(flddesc); + uint16_t* ptrs16 = (uint16_t*)jl_dt_layout_ptrs(flddesc); + uint32_t* ptrs32 = (uint32_t*)jl_dt_layout_ptrs(flddesc); + for (size_t i = 0; i < npointers; i++) { + if (fielddesc_type == 0) { + ptrs8[i] = pointers[i]; + } + else if (fielddesc_type == 1) { + ptrs16[i] = pointers[i]; + } + else { + ptrs32[i] = pointers[i]; + } } - flddesc->npointers = npointers | (nexp << 16); return flddesc; } @@ -236,7 +228,7 @@ STATIC_INLINE int jl_is_datatype_make_singleton(jl_datatype_t *d) d->uid != 0 && !d->mutabl); } -STATIC_INLINE void jl_allocate_singleton_instance(jl_datatype_t *st) +STATIC_INLINE void jl_maybe_allocate_singleton_instance(jl_datatype_t *st) { if (jl_is_datatype_make_singleton(st)) { st->instance = jl_gc_alloc(jl_get_ptls_states(), 0, st); @@ -244,18 +236,18 @@ STATIC_INLINE void jl_allocate_singleton_instance(jl_datatype_t *st) } } -static unsigned union_isbits(jl_value_t *ty, size_t *nbytes, size_t *align) JL_NOTSAFEPOINT +static unsigned union_isinlinable(jl_value_t *ty, int pointerfree, size_t *nbytes, size_t *align) JL_NOTSAFEPOINT { if (jl_is_uniontype(ty)) { - unsigned na = union_isbits(((jl_uniontype_t*)ty)->a, nbytes, align); + unsigned na = union_isinlinable(((jl_uniontype_t*)ty)->a, 1, nbytes, align); if (na == 0) return 0; - unsigned nb = union_isbits(((jl_uniontype_t*)ty)->b, nbytes, align); + unsigned nb = union_isinlinable(((jl_uniontype_t*)ty)->b, 1, nbytes, align); if (nb == 0) return 0; return na + nb; } - if (jl_is_datatype(ty) && jl_datatype_isinlinealloc(ty)) { + if (jl_is_datatype(ty) && jl_datatype_isinlinealloc(ty) && (!pointerfree || ((jl_datatype_t*)ty)->layout->npointers == 0)) { size_t sz = jl_datatype_size(ty); size_t al = jl_datatype_align(ty); if (*nbytes < sz) @@ -269,7 +261,7 @@ static unsigned union_isbits(jl_value_t *ty, size_t *nbytes, size_t *align) JL_N JL_DLLEXPORT int jl_islayout_inline(jl_value_t *eltype, size_t *fsz, size_t *al) JL_NOTSAFEPOINT { - unsigned countbits = union_isbits(eltype, fsz, al); + unsigned countbits = union_isinlinable(eltype, 0, fsz, al); return (countbits > 0 && countbits < 127) ? countbits : 0; } @@ -296,163 +288,201 @@ static int references_name(jl_value_t *p, jl_typename_t *name) JL_NOTSAFEPOINT return 0; } +static void throw_ovf(int should_malloc, void *desc, jl_datatype_t* st, int offset) +{ + if (should_malloc) + free(desc); + jl_errorf("type %s has field offset %d that exceeds the page size", jl_symbol_name(st->name->name), offset); +} + void jl_compute_field_offsets(jl_datatype_t *st) { - size_t sz = 0, alignm = 1; - size_t fldsz = 0, fldal = 0; - int homogeneous = 1; - jl_value_t *lastty = NULL; - uint64_t max_offset = (((uint64_t)1) << 32) - 1; - uint64_t max_size = max_offset >> 1; - - if (st->name->wrapper) { - jl_datatype_t *w = (jl_datatype_t*)jl_unwrap_unionall(st->name->wrapper); - // compute whether this type can be inlined - // based on whether its definition is self-referential - if (w->types != NULL) { - st->isbitstype = st->isinlinealloc = st->isconcretetype && !st->mutabl; - size_t i, nf = jl_svec_len(st->types); - for (i = 0; i < nf; i++) { - jl_value_t *fld = jl_svecref(st->types, i); - if (st->isbitstype) - st->isbitstype = jl_is_datatype(fld) && ((jl_datatype_t*)fld)->isbitstype; - if (st->isinlinealloc) - st->isinlinealloc = (jl_is_datatype(fld) && ((jl_datatype_t*)fld)->isbitstype) || jl_islayout_inline(fld, &fldsz, &fldal); - if (!st->zeroinit) - st->zeroinit = (jl_is_datatype(fld) && ((jl_datatype_t*)fld)->isinlinealloc) ? ((jl_datatype_t*)fld)->zeroinit : 1; - if (i < st->ninitialized) { - if (fld == jl_bottom_type) - st->has_concrete_subtype = 0; - else - st->has_concrete_subtype &= !jl_is_datatype(fld) || ((jl_datatype_t *)fld)->has_concrete_subtype; - } - } - if (st->isinlinealloc) { - size_t i, nf = jl_svec_len(w->types); - for (i = 0; i < nf; i++) { - jl_value_t *fld = jl_svecref(w->types, i); - if (references_name(fld, w->name)) { - st->isinlinealloc = 0; - st->isbitstype = 0; - st->zeroinit = 1; - break; - } - } - } - } - // If layout doesn't depend on type parameters, it's stored in st->name->wrapper - // and reused by all subtypes. - if (st != w && // this check allows us to re-compute layout for some types during init - w->layout) { - st->layout = w->layout; - st->size = w->size; - jl_allocate_singleton_instance(st); + const uint64_t max_offset = (((uint64_t)1) << 32) - 1; + const uint64_t max_size = max_offset >> 1; + + if (st->types == NULL || st->name->wrapper == NULL || (jl_is_namedtuple_type(st) && !jl_is_concrete_type((jl_value_t*)st))) + return; + jl_datatype_t *w = (jl_datatype_t*)jl_unwrap_unionall(st->name->wrapper); + if (w->types == NULL) // we got called too early--we'll be back + return; + size_t i, nfields = jl_svec_len(st->types); + int isinlinealloc = st->isconcretetype && !st->mutabl; + int isbitstype = isinlinealloc; + assert(st->ninitialized <= nfields); + if (st == w && st->layout) { + // this check allows us to force re-computation of the layout for some types during init + st->layout = NULL; + st->size = 0; + st->zeroinit = 0; + st->has_concrete_subtype = 1; + } + // If layout doesn't depend on type parameters, it's stored in st->name->wrapper + // and reused by all subtypes. + if (w->layout) { + st->layout = w->layout; + st->size = w->size; + st->zeroinit = w->zeroinit; + st->has_concrete_subtype = w->has_concrete_subtype; + if (jl_is_layout_opaque(st->layout)) { // e.g. jl_array_typename return; } } - if (st->types == NULL || (jl_is_namedtuple_type(st) && !jl_is_concrete_type((jl_value_t*)st))) - return; - uint32_t nfields = jl_svec_len(st->types); - if (nfields == 0) { + else if (nfields == 0) { + // if we have no fields, we can trivially skip the rest if (st == jl_symbol_type || st == jl_string_type) { // opaque layout - heap-allocated blob - static const jl_datatype_layout_t opaque_byte_layout = {0, 1, 0, 1, 0}; + static const jl_datatype_layout_t opaque_byte_layout = {0, 1, 1, 0, 0}; st->layout = &opaque_byte_layout; + return; } else if (st == jl_simplevector_type || st->name == jl_array_typename) { - static const jl_datatype_layout_t opaque_ptr_layout = {0, sizeof(void*), 0, 1, 0}; + static const jl_datatype_layout_t opaque_ptr_layout = {0, 1, sizeof(void*), 0, 0}; st->layout = &opaque_ptr_layout; + return; } else { // reuse the same layout for all singletons - static const jl_datatype_layout_t singleton_layout = {0, 1, 0, 0, 0}; + static const jl_datatype_layout_t singleton_layout = {0, 0, 1, 0, 0}; st->layout = &singleton_layout; - jl_allocate_singleton_instance(st); } - return; } - if (!jl_is_concrete_type((jl_value_t*)st)) { - // compute layout whenever field types have no free variables - for (size_t i = 0; i < nfields; i++) { - if (jl_has_free_typevars(jl_field_type(st, i))) - return; + else { + // compute a conservative estimate of whether there could exist an instance of a subtype of this + for (i = 0; st->has_concrete_subtype && i < st->ninitialized; i++) { + jl_value_t *fld = jl_svecref(st->types, i); + if (fld == jl_bottom_type) + st->has_concrete_subtype = 0; + else + st->has_concrete_subtype = !jl_is_datatype(fld) || ((jl_datatype_t *)fld)->has_concrete_subtype; + } + // compute layout for the wrapper object if the field types have no free variables + if (!st->isconcretetype) { + if (st != w) + return; // otherwise we would leak memory + for (i = 0; i < nfields; i++) { + if (jl_has_free_typevars(jl_field_type(st, i))) + return; // not worthwhile computing the rest + } } } - size_t descsz = nfields * sizeof(jl_fielddesc32_t); - jl_fielddesc32_t *desc; - int should_malloc = descsz >= jl_page_size; - if (should_malloc) - desc = (jl_fielddesc32_t*)malloc(descsz); - else - desc = (jl_fielddesc32_t*)alloca(descsz); - int haspadding = 0; - assert(st->name == jl_tuple_typename || - st == jl_symbol_type || - st == jl_simplevector_type || - nfields != 0); + // compute whether this type may ever be inlined + // based solely on whether its definition is self-referential + if (isinlinealloc) { + size_t i, nf = jl_svec_len(w->types); + for (i = 0; i < nf; i++) { + jl_value_t *fld = jl_svecref(w->types, i); + if (references_name(fld, w->name)) { + isinlinealloc = 0; + break; + } + } + for (i = 0; isbitstype && i < nfields; i++) { + jl_value_t *fld = jl_field_type(st, i); + isbitstype = jl_isbits(fld); + } + } - for (size_t i = 0; i < nfields; i++) { - jl_value_t *ty = jl_field_type(st, i); - size_t fsz = 0, al = 1; - if (jl_islayout_inline(ty, &fsz, &al)) { - if (__unlikely(fsz > max_size)) - // Should never happen - goto throw_ovf; - desc[i].isptr = 0; - if (jl_is_uniontype(ty)) { - haspadding = 1; - fsz += 1; // selector byte + // if we didn't reuse the layout above, compute it now + if (st->layout == NULL) { + size_t descsz = nfields * sizeof(jl_fielddesc32_t); + jl_fielddesc32_t *desc; + uint32_t *pointers; + int should_malloc = descsz >= jl_page_size; + if (should_malloc) + desc = (jl_fielddesc32_t*)malloc_s(descsz); + else + desc = (jl_fielddesc32_t*)alloca(descsz); + size_t sz = 0; + size_t alignm = 1; + int zeroinit = 0; + int haspadding = 0; + int homogeneous = 1; + uint32_t npointers = 0; + jl_value_t *firstty = jl_field_type(st, 0); + for (i = 0; i < nfields; i++) { + jl_value_t *fld = jl_field_type(st, i); + size_t fsz = 0, al = 1; + if (jl_islayout_inline(fld, &fsz, &al)) { // aka jl_datatype_isinlinealloc + if (__unlikely(fsz > max_size)) + // Should never happen + throw_ovf(should_malloc, desc, st, fsz); + desc[i].isptr = 0; + if (jl_is_uniontype(fld)) { + haspadding = 1; + fsz += 1; // selector byte + zeroinit = 1; + } + else { + if (((jl_datatype_t*)fld)->layout->haspadding) + haspadding = 1; + if (!zeroinit) + zeroinit = ((jl_datatype_t*)fld)->zeroinit; + } + } + else { + fsz = sizeof(void*); + if (fsz > MAX_ALIGN) + fsz = MAX_ALIGN; + al = fsz; + desc[i].isptr = 1; + zeroinit = 1; + npointers++; } - else { // isbits struct - if (((jl_datatype_t*)ty)->layout->haspadding) + assert(al <= JL_HEAP_ALIGNMENT && (JL_HEAP_ALIGNMENT % al) == 0); + if (al != 0) { + size_t alsz = LLT_ALIGN(sz, al); + if (sz & (al - 1)) haspadding = 1; + sz = alsz; + if (al > alignm) + alignm = al; } + homogeneous &= firstty == fld; + desc[i].offset = sz; + desc[i].size = fsz; + if (__unlikely(max_offset - sz < fsz)) + throw_ovf(should_malloc, desc, st, sz); + sz += fsz; } - else { - fsz = sizeof(void*); - if (fsz > MAX_ALIGN) - fsz = MAX_ALIGN; - al = fsz; - desc[i].isptr = 1; - } - assert(al <= JL_HEAP_ALIGNMENT && (JL_HEAP_ALIGNMENT % al) == 0); - if (al != 0) { - size_t alsz = LLT_ALIGN(sz, al); - if (sz & (al - 1)) - haspadding = 1; - sz = alsz; - if (al > alignm) + if (homogeneous && jl_is_tuple_type(st)) { + // Some tuples become LLVM vectors with stronger alignment than what was calculated above. + unsigned al = jl_special_vector_alignment(nfields, firstty); + assert(al % alignm == 0); + // JL_HEAP_ALIGNMENT is the biggest alignment we can guarantee on the heap. + if (al > JL_HEAP_ALIGNMENT) + alignm = JL_HEAP_ALIGNMENT; + else if (al) alignm = al; } - homogeneous &= lastty==NULL || lastty==ty; - lastty = ty; - desc[i].offset = sz; - desc[i].size = fsz; - if (__unlikely(max_offset - sz < fsz)) - goto throw_ovf; - sz += fsz; - } - if (homogeneous && lastty != NULL && jl_is_tuple_type(st)) { - // Some tuples become LLVM vectors with stronger alignment than what was calculated above. - unsigned al = jl_special_vector_alignment(nfields, lastty); - assert(al % alignm == 0); - // JL_HEAP_ALIGNMENT is the biggest alignment we can guarantee on the heap. - if (al > JL_HEAP_ALIGNMENT) - alignm = JL_HEAP_ALIGNMENT; - else if (al) - alignm = al; + st->size = LLT_ALIGN(sz, alignm); + if (st->size > sz) + haspadding = 1; + if (should_malloc && npointers) + pointers = (uint32_t*)malloc_s(npointers * sizeof(uint32_t)); + else + pointers = (uint32_t*)alloca(npointers * sizeof(uint32_t)); + size_t ptr_i = 0; + for (i = 0; i < nfields; i++) { + if (desc[i].isptr) + pointers[ptr_i++] = desc[i].offset / sizeof(jl_value_t**); + } + assert(ptr_i == npointers); + st->layout = jl_get_layout(nfields, npointers, alignm, haspadding, desc, pointers); + if (should_malloc) { + free(desc); + if (npointers) + free(pointers); + } } - st->size = LLT_ALIGN(sz, alignm); - if (st->size > sz) - haspadding = 1; - st->layout = jl_get_layout(nfields, alignm, haspadding, desc); - if (should_malloc) free(desc); - jl_allocate_singleton_instance(st); + // now finish deciding if this instantiation qualifies for special properties + assert(!isbitstype || st->layout->npointers == 0); // the definition of isbits + if (st->layout->npointers != 0) + isinlinealloc = 0; + st->isbitstype = isbitstype; + st->isinlinealloc = isinlinealloc; + jl_maybe_allocate_singleton_instance(st); return; - throw_ovf: - if (should_malloc) free(desc); - jl_errorf("type %s has field offset %d that exceeds the page size", jl_symbol_name(st->name->name), descsz); } static int is_anonfn_typename(char *name) @@ -554,7 +584,7 @@ JL_DLLEXPORT jl_datatype_t *jl_new_primitivetype(jl_value_t *name, jl_module_t * alignm = MAX_ALIGN; bt->isbitstype = bt->isinlinealloc = (parameters == jl_emptysvec); bt->size = nbytes; - bt->layout = jl_get_layout(0, alignm, 0, NULL); + bt->layout = jl_get_layout(0, 0, alignm, 0, NULL, NULL); bt->instance = NULL; return bt; } diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index b732d0b06a7aa..ad5e5c674539e 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -101,7 +101,7 @@ static void create_PRUNTIME_FUNCTION(uint8_t *Code, size_t Size, StringRef fnnam // GC safe DWORD mod_size = 0; #if defined(_CPU_X86_64_) - PRUNTIME_FUNCTION tbl = (PRUNTIME_FUNCTION)malloc(sizeof(RUNTIME_FUNCTION)); + PRUNTIME_FUNCTION tbl = (PRUNTIME_FUNCTION)malloc_s(sizeof(RUNTIME_FUNCTION)); tbl->BeginAddress = (DWORD)(Code - Section); tbl->EndAddress = (DWORD)(Code - Section + Size); tbl->UnwindData = (DWORD)(UnwindData - Section); @@ -427,7 +427,7 @@ static std::pair jl_demangle(const char *name) } if (end <= start) goto done; - ret = (char*)malloc(end - start + 1); + ret = (char*)malloc_s(end - start + 1); memcpy(ret, start, end - start); ret[end - start] = '\0'; return std::make_pair(ret, true); @@ -761,7 +761,7 @@ static void get_function_name_and_base(const object::ObjectFile *object, bool in if (auto name_or_err = sym_found.getName()) { auto nameref = name_or_err.get(); size_t len = nameref.size(); - *name = (char*)realloc(*name, len + 1); + *name = (char*)realloc_s(*name, len + 1); (*name)[len] = 0; memcpy(*name, nameref.data(), len); needs_name = false; diff --git a/src/dlload.c b/src/dlload.c index 8b48f4445dfe0..72213c4362311 100644 --- a/src/dlload.c +++ b/src/dlload.c @@ -37,15 +37,18 @@ static int endswith_extension(const char *path) return 0; size_t len = strlen(path); // Skip the first one since it is empty - for (size_t i = 1;i < N_EXTENSIONS;i++) { + for (size_t i = 1; i < N_EXTENSIONS; i++) { const char *ext = extensions[i]; size_t extlen = strlen(ext); - if (len < extlen) return 0; + if (len < extlen) + return 0; // Skip version extensions if present - size_t j = len-1; + size_t j = len - 1; while (j > 0) { - if (path[j] == '.' || (path[j] >= '0' && path[j] <= '9')) j--; - else break; + if (path[j] == '.' || (path[j] >= '0' && path[j] <= '9')) + j--; + else + break; } if ((j == len-1 || path[j+1] == '.') && memcmp(ext, path + j - extlen + 1, extlen) == 0) { return 1; @@ -54,25 +57,39 @@ static int endswith_extension(const char *path) return 0; } -#define PATHBUF 512 +#define PATHBUF 4096 extern char *julia_bindir; #define JL_RTLD(flags, FLAG) (flags & JL_RTLD_ ## FLAG ? RTLD_ ## FLAG : 0) -static const char * jl_dlerror(void) -{ #ifdef _OS_WINDOWS_ - static JL_THREAD_LOCAL CHAR reason[256]; - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, GetLastError(), - MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), - reason, sizeof(reason) / sizeof(reason[0]), NULL); - return (const char *)&reason[0]; -#else - return dlerror(); -#endif +static 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); + assert(res > 0 || GetLastError() == ERROR_INSUFFICIENT_BUFFER); + reason[len - 1] = '\0'; + LocalFree(errmsg); } +#endif JL_DLLEXPORT void *jl_dlopen(const char *filename, unsigned flags) { @@ -121,6 +138,9 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, { char path[PATHBUF]; int i; +#ifdef _OS_WINDOWS_ + int err; +#endif uv_stat_t stbuf; void *handle; int abspath; @@ -163,16 +183,23 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, size_t len = strlen(dl_path); if (len == 0) continue; - for (i=0; i < n_extensions; i++) { + for (i = 0; i < n_extensions; i++) { const char *ext = extensions[i]; path[0] = '\0'; if (dl_path[len-1] == PATHSEPSTRING[0]) snprintf(path, PATHBUF, "%s%s%s", dl_path, modname, ext); else snprintf(path, PATHBUF, "%s" PATHSEPSTRING "%s%s", dl_path, modname, ext); - handle = jl_dlopen(path, flags); - if (handle) - goto done; +#ifdef _OS_WINDOWS_ + if (i == 0) { // LoadLibrary already tested the extensions, we just need to check the `stat` result +#endif + handle = jl_dlopen(path, flags); + if (handle) + goto done; +#ifdef _OS_WINDOWS_ + err = GetLastError(); + } +#endif // bail out and show the error if file actually exists if (jl_stat(path, (char*)&stbuf) == 0) goto notfound; @@ -182,21 +209,30 @@ JL_DLLEXPORT void *jl_load_dynamic_library(const char *modname, unsigned flags, } // now fall back and look in default library paths, for all extensions - for(i=0; i < n_extensions; i++) { + for (i = 0; i < n_extensions; i++) { const char *ext = extensions[i]; path[0] = '\0'; snprintf(path, PATHBUF, "%s%s", modname, ext); handle = jl_dlopen(path, flags); if (handle) goto done; +#ifdef _OS_WINDOWS_ + err = GetLastError(); + break; // LoadLibrary already tested the rest +#endif } notfound: if (throw_err) { - const char * reason = jl_dlerror(); +#ifdef _OS_WINDOWS_ + char reason[256]; + win32_formatmessage(err, reason, sizeof(reason)); +#else + const char *reason = dlerror(); +#endif jl_errorf("could not load library \"%s\"\n%s", modname, reason); } - return NULL; + handle = NULL; done: return handle; @@ -216,20 +252,19 @@ JL_DLLEXPORT int jl_dlsym(void *handle, const char *symbol, void ** value, int t /* Next, check for errors. On Windows, a NULL pointer means the symbol * was not found. On everything else, we can have NULL symbols, so we check - * for non-NULL returns from dlerror(). Note that we unconditionally call - * jl_dlerror() on POSIX systems, but on Windows systems we only call it - * when we have been returned a NULL symbol.*/ - const char * err = NULL; + * for non-NULL returns from dlerror(). Note that means we unconditionally + * call dlerror() on POSIX systems.*/ #ifdef _OS_WINDOWS_ symbol_found = *value != NULL; #else - err = jl_dlerror(); + const char *err = dlerror(); symbol_found = err == NULL; #endif if (!symbol_found && throw_err) { #ifdef _OS_WINDOWS_ - err = jl_dlerror(); + char err[256]; + win32_formatmessage(GetLastError(), err, sizeof(err)); #endif jl_errorf("could not load symbol \"%s\":\n%s", symbol, err); } diff --git a/src/dump.c b/src/dump.c index c9b86661803a4..9fd00e5f521c3 100644 --- a/src/dump.c +++ b/src/dump.c @@ -334,7 +334,7 @@ static void jl_serialize_datatype(jl_serializer_state *s, jl_datatype_t *dt) JL_ jl_methtable_t *mt = dt->name->mt; size_t l = strlen(jl_symbol_name(mt->name)); char *prefixed; - prefixed = (char*)malloc(l + 2); + prefixed = (char*)malloc_s(l + 2); prefixed[0] = '#'; strcpy(&prefixed[1], jl_symbol_name(mt->name)); // remove ##kw suffix @@ -393,13 +393,10 @@ static void jl_serialize_datatype(jl_serializer_state *s, jl_datatype_t *dt) JL_ write_uint8(s->s, layout); if (layout == 0) { uint32_t nf = dt->layout->nfields; - write_int32(s->s, nf); - uint32_t alignment = ((uint32_t*)dt->layout)[1]; - write_int32(s->s, alignment); - if (dt->layout->npointers && nf) - write_int32(s->s, ((uint32_t*)dt->layout)[-1]); + uint32_t np = dt->layout->npointers; size_t fieldsize = jl_fielddesc_size(dt->layout->fielddesc_type); - ios_write(s->s, (char*)(&dt->layout[1]), nf * fieldsize); + ios_write(s->s, (const char*)dt->layout, sizeof(*dt->layout)); + ios_write(s->s, (const char*)(dt->layout + 1), nf * fieldsize + (np << dt->layout->fielddesc_type)); } } @@ -1441,29 +1438,17 @@ static jl_value_t *jl_deserialize_datatype(jl_serializer_state *s, int pos, jl_v } else { assert(layout == 0); - uint32_t nf = read_int32(s->s); - uint32_t alignment = read_int32(s->s); - union { - struct { - uint32_t nf; - uint32_t alignment; - } buffer; - jl_datatype_layout_t layout; - } header; - header.buffer.nf = nf; - header.buffer.alignment = alignment; - int has_padding = header.layout.npointers && nf; - uint8_t fielddesc_type = header.layout.fielddesc_type; + jl_datatype_layout_t buffer; + ios_read(s->s, (char*)&buffer, sizeof(buffer)); + uint32_t nf = buffer.nfields; + uint32_t np = buffer.npointers; + uint8_t fielddesc_type = buffer.fielddesc_type; size_t fielddesc_size = nf > 0 ? jl_fielddesc_size(fielddesc_type) : 0; jl_datatype_layout_t *layout = (jl_datatype_layout_t*)jl_gc_perm_alloc( - sizeof(jl_datatype_layout_t) + nf * fielddesc_size + - (has_padding ? sizeof(uint32_t) : 0), 0, 4, 0); - if (has_padding) { - layout = (jl_datatype_layout_t*)(((char*)layout) + sizeof(uint32_t)); - jl_datatype_layout_n_nonptr(layout) = read_int32(s->s); - } - *layout = header.layout; - ios_read(s->s, (char*)&layout[1], nf * fielddesc_size); + sizeof(jl_datatype_layout_t) + nf * fielddesc_size + (np << fielddesc_type), + 0, 4, 0); + *layout = buffer; + ios_read(s->s, (char*)(layout + 1), nf * fielddesc_size + (np << fielddesc_type)); dt->layout = layout; } } @@ -1523,7 +1508,7 @@ static jl_value_t *jl_deserialize_value_symbol(jl_serializer_state *s, uint8_t t len = read_uint8(s->s); else len = read_int32(s->s); - char *name = (char*)(len >= 256 ? malloc(len + 1) : alloca(len + 1)); + char *name = (char*)(len >= 256 ? malloc_s(len + 1) : alloca(len + 1)); ios_read(s->s, name, len); name[len] = '\0'; jl_value_t *sym = (jl_value_t*)jl_symbol(name); @@ -3225,7 +3210,7 @@ static jl_value_t *_jl_restore_incremental(ios_t *f, jl_array_t *mod_array) arraylist_t *tracee_list = NULL; if (jl_newmeth_tracer) - tracee_list = arraylist_new((arraylist_t*)malloc(sizeof(arraylist_t)), 0); + tracee_list = arraylist_new((arraylist_t*)malloc_s(sizeof(arraylist_t)), 0); // at this point, the AST is fully reconstructed, but still completely disconnected // now all of the interconnects will be created diff --git a/src/flisp/Makefile b/src/flisp/Makefile index d97075eb68830..2158e2a5b2499 100644 --- a/src/flisp/Makefile +++ b/src/flisp/Makefile @@ -110,10 +110,12 @@ $(BUILDDIR)/host/Makefile: $(BUILDDIR)/host/$(EXENAME): $(BUILDDIR)/host/Makefile make -C $(BUILDDIR)/host $(EXENAME) -ifneq ($(BUILDDIR)$(BUILDING_HOST_TOOLS),.) +ifneq ($(BUILDDIR),.) +ifneq ($(BUILDDIR),$(SRCDIR)) $(BUILDDIR)/flisp.boot: $(SRCDIR)/flisp.boot | $(BUILDDIR) cp $< $@ endif +endif test: ifneq ($(USEMSVC), 1) diff --git a/src/flisp/cvalues.c b/src/flisp/cvalues.c index ce4fcfee2cc54..071a0b1642971 100644 --- a/src/flisp/cvalues.c +++ b/src/flisp/cvalues.c @@ -136,6 +136,7 @@ value_t cvalue(fl_context_t *fl_ctx, fltype_t *type, size_t sz) pcv = (cvalue_t*)alloc_words(fl_ctx, CVALUE_NWORDS); pcv->type = type; pcv->data = malloc(sz); + // TODO: if pcv->data == NULL autorelease(fl_ctx, pcv); fl_ctx->malloc_pressure += sz; } @@ -220,6 +221,7 @@ void cv_pin(fl_context_t *fl_ctx, cvalue_t *cv) size_t sz = cv_len(cv); if (cv_isstr(fl_ctx, cv)) sz++; void *data = malloc(sz); + // TODO: if data == NULL memcpy(data, cv_data(cv), sz); cv->data = data; autorelease(fl_ctx, cv); @@ -570,6 +572,7 @@ value_t cvalue_copy(fl_context_t *fl_ctx, value_t v) size_t len = cv_len(cv); if (cv_isstr(fl_ctx, cv)) len++; ncv->data = malloc(len); + // TODO: if ncv->data == NULL memcpy(ncv->data, cv_data(cv), len); autorelease(fl_ctx, ncv); if (hasparent(cv)) { @@ -778,6 +781,7 @@ value_t fl_builtin(fl_context_t *fl_ctx, value_t *args, uint32_t nargs) value_t cbuiltin(fl_context_t *fl_ctx, const char *name, builtin_t f) { cvalue_t *cv = (cvalue_t*)malloc(CVALUE_NWORDS * sizeof(value_t)); + // TODO: if cv->data == NULL cv->type = fl_ctx->builtintype; cv->data = &cv->_space[0]; cv->len = sizeof(value_t); diff --git a/src/flisp/flisp.c b/src/flisp/flisp.c index 84bb3b371b83c..1b135780e7c8f 100644 --- a/src/flisp/flisp.c +++ b/src/flisp/flisp.c @@ -226,6 +226,7 @@ static symbol_t *mk_symbol(const char *str) size_t len = strlen(str); sym = (symbol_t*)malloc((offsetof(symbol_t,name)+len+1+7)&-8); + // TODO: if sym == NULL CHECK_ALIGN8(sym); sym->left = sym->right = NULL; sym->flags = 0; @@ -330,6 +331,7 @@ static value_t mk_cons(fl_context_t *fl_ctx) if (fl_ctx->n_allocd > GC_INTERVAL) gc(fl_ctx, 0); c = (cons_t*)((void**)malloc(3*sizeof(void*)) + 1); + // TODO: if c == NULL CHECK_ALIGN8(c); ((void**)c)[-1] = fl_ctx->tochain; fl_ctx->tochain = c; @@ -353,6 +355,7 @@ static value_t *alloc_words(fl_context_t *fl_ctx, int n) if (fl_ctx->n_allocd > GC_INTERVAL) gc(fl_ctx, 0); first = (value_t*)malloc((n+1)*sizeof(value_t)) + 1; + // TODO: if first == NULL CHECK_ALIGN8(first); first[-1] = (value_t)fl_ctx->tochain; fl_ctx->tochain = first; @@ -2339,6 +2342,7 @@ static void lisp_init(fl_context_t *fl_ctx, size_t initial_heapsize) comparehash_init(fl_ctx); fl_ctx->N_STACK = 262144; fl_ctx->Stack = (value_t*)malloc(fl_ctx->N_STACK*sizeof(value_t)); + // TODO: if fl_ctx->Stack == NULL CHECK_ALIGN8(fl_ctx->Stack); fl_ctx->NIL = builtin(OP_THE_EMPTY_LIST); diff --git a/src/flisp/julia_extensions.c b/src/flisp/julia_extensions.c index 6c0e4466f41c8..5a6602b4e4659 100644 --- a/src/flisp/julia_extensions.c +++ b/src/flisp/julia_extensions.c @@ -241,6 +241,7 @@ value_t fl_julia_strip_op_suffix(fl_context_t *fl_ctx, value_t *args, uint32_t n if (!op[i]) return args[0]; // no suffix to strip if (!i) return args[0]; // only suffix chars --- might still be a valid identifier char *opnew = strncpy((char*)malloc(i+1), op, i); + // TODO: if argument to opnew == NULL opnew[i] = 0; value_t opnew_symbol = symbol(fl_ctx, opnew); free(opnew); diff --git a/src/flisp/read.c b/src/flisp/read.c index 2b57688cd86be..494303ef9add7 100644 --- a/src/flisp/read.c +++ b/src/flisp/read.c @@ -460,6 +460,9 @@ static value_t read_string(fl_context_t *fl_ctx) uint32_t wc=0; buf = (char*)malloc(sz); + if (buf == NULL) { + lerror(fl_ctx, fl_ctx->ParseError, "read: out of memory reading string"); + } while (1) { if (i >= sz-4) { // -4: leaves room for longest utf8 sequence sz *= 2; diff --git a/src/flisp/types.c b/src/flisp/types.c index 945dce9ecc731..f7abbd053d3ec 100644 --- a/src/flisp/types.c +++ b/src/flisp/types.c @@ -23,6 +23,7 @@ fltype_t *get_type(fl_context_t *fl_ctx, value_t t) } ft = (fltype_t*)malloc(sizeof(fltype_t)); + // TODO: if ft == NULL ft->type = t; if (issymbol(t)) { ft->numtype = sym_to_numtype(fl_ctx, t); diff --git a/src/gc-debug.c b/src/gc-debug.c index 9bae5cd249c0a..d841236d22e7c 100644 --- a/src/gc-debug.c +++ b/src/gc-debug.c @@ -1310,7 +1310,7 @@ NOINLINE void gc_mark_loop_unwind(jl_ptls_t ptls, jl_gc_mark_sp_t sp, int pc_off break; } jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(data->parent); - jl_fielddesc8_t *desc = (jl_fielddesc8_t*)jl_dt_layout_fields(vt->layout); + uint8_t *desc = (uint8_t*)jl_dt_layout_ptrs(vt->layout); jl_safe_printf("%p: %s Object (8bit) %p :: %p -- [%d, %d)\n of type ", (void*)data, prefix, (void*)data->parent, ((void**)data->parent)[-1], (int)(data->begin - desc), (int)(data->end - desc)); @@ -1323,7 +1323,7 @@ NOINLINE void gc_mark_loop_unwind(jl_ptls_t ptls, jl_gc_mark_sp_t sp, int pc_off break; } jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(data->parent); - jl_fielddesc16_t *desc = (jl_fielddesc16_t*)jl_dt_layout_fields(vt->layout); + uint16_t *desc = (uint16_t*)jl_dt_layout_ptrs(vt->layout); jl_safe_printf("%p: %s Object (16bit) %p :: %p -- [%d, %d)\n of type ", (void*)data, prefix, (void*)data->parent, ((void**)data->parent)[-1], (int)(data->begin - desc), (int)(data->end - desc)); @@ -1336,7 +1336,7 @@ NOINLINE void gc_mark_loop_unwind(jl_ptls_t ptls, jl_gc_mark_sp_t sp, int pc_off break; } jl_datatype_t *vt = (jl_datatype_t*)jl_typeof(data->parent); - jl_fielddesc32_t *desc = (jl_fielddesc32_t*)jl_dt_layout_fields(vt->layout); + uint32_t *desc = (uint32_t*)jl_dt_layout_ptrs(vt->layout); jl_safe_printf("%p: %s Object (32bit) %p :: %p -- [%d, %d)\n of type ", (void*)data, prefix, (void*)data->parent, ((void**)data->parent)[-1], (int)(data->begin - desc), (int)(data->end - desc)); diff --git a/src/gc-pages.c b/src/gc-pages.c index db18617d8c6d7..95752eb426cca 100644 --- a/src/gc-pages.c +++ b/src/gc-pages.c @@ -173,6 +173,10 @@ NOINLINE jl_gc_pagemeta_t *jl_gc_alloc_page(void) JL_NOTSAFEPOINT struct jl_gc_metadata_ext info; JL_LOCK_NOGC(&gc_perm_lock); + int last_errno = errno; +#ifdef _OS_WINDOWS_ + DWORD last_error = GetLastError(); +#endif // scan over memory_map page-table for existing allocated but unused pages for (info.pagetable_i32 = memory_map.lb; info.pagetable_i32 < (REGION2_PG_COUNT + 31) / 32; info.pagetable_i32++) { uint32_t freemap1 = memory_map.freemap1[info.pagetable_i32]; @@ -245,6 +249,10 @@ NOINLINE jl_gc_pagemeta_t *jl_gc_alloc_page(void) JL_NOTSAFEPOINT #ifdef _OS_WINDOWS_ VirtualAlloc(info.meta->data, GC_PAGE_SZ, MEM_COMMIT, PAGE_READWRITE); #endif +#ifdef _OS_WINDOWS_ + SetLastError(last_error); +#endif + errno = last_errno; current_pg_count++; gc_final_count_page(current_pg_count); JL_UNLOCK_NOGC(&gc_perm_lock); diff --git a/src/gc.c b/src/gc.c index 831dc14545348..a40ed13889b34 100644 --- a/src/gc.c +++ b/src/gc.c @@ -45,7 +45,7 @@ static void jl_gc_register_callback(jl_gc_callback_list_t **list, return; list = &((*list)->next); } - *list = (jl_gc_callback_list_t *)malloc(sizeof(jl_gc_callback_list_t)); + *list = (jl_gc_callback_list_t *)malloc_s(sizeof(jl_gc_callback_list_t)); (*list)->next = NULL; (*list)->func = func; } @@ -209,6 +209,52 @@ static void jl_gc_wait_for_the_world(void) // malloc wrappers, aligned allocation +#if defined(_OS_WINDOWS_) +STATIC_INLINE void *jl_malloc_aligned(size_t sz, size_t align) +{ + return _aligned_malloc(sz ? sz : 1, align); +} +STATIC_INLINE void *jl_realloc_aligned(void *p, size_t sz, size_t oldsz, + size_t align) +{ + (void)oldsz; + return _aligned_realloc(p, sz ? sz : 1, align); +} +STATIC_INLINE void jl_free_aligned(void *p) JL_NOTSAFEPOINT +{ + _aligned_free(p); +} +#else +STATIC_INLINE void *jl_malloc_aligned(size_t sz, size_t align) +{ +#if defined(_P64) || defined(__APPLE__) + if (align <= 16) + return malloc(sz); +#endif + void *ptr; + if (posix_memalign(&ptr, align, sz)) + return NULL; + return ptr; +} +STATIC_INLINE void *jl_realloc_aligned(void *d, size_t sz, size_t oldsz, + size_t align) +{ +#if defined(_P64) || defined(__APPLE__) + if (align <= 16) + return realloc(d, sz); +#endif + void *b = jl_malloc_aligned(sz, align); + if (b != NULL) { + memcpy(b, d, oldsz > sz ? sz : oldsz); + free(d); + } + return b; +} +STATIC_INLINE void jl_free_aligned(void *p) JL_NOTSAFEPOINT +{ + free(p); +} +#endif #define malloc_cache_align(sz) jl_malloc_aligned(sz, JL_CACHE_BYTE_ALIGNMENT) #define realloc_cache_align(p, sz, oldsz) jl_realloc_aligned(p, sz, oldsz, JL_CACHE_BYTE_ALIGNMENT) @@ -927,7 +973,7 @@ void jl_gc_track_malloced_array(jl_ptls_t ptls, jl_array_t *a) JL_NOTSAFEPOINT // This is **NOT** a GC safe point. mallocarray_t *ma; if (ptls->heap.mafreelist == NULL) { - ma = (mallocarray_t*)malloc(sizeof(mallocarray_t)); + ma = (mallocarray_t*)malloc_s(sizeof(mallocarray_t)); } else { ma = ptls->heap.mafreelist; @@ -1074,7 +1120,7 @@ static NOINLINE jl_taggedvalue_t *add_page(jl_gc_pool_t *p) JL_NOTSAFEPOINT jl_ptls_t ptls = jl_get_ptls_states(); jl_gc_pagemeta_t *pg = jl_gc_alloc_page(); pg->osize = p->osize; - pg->ages = (uint8_t*)malloc(GC_PAGE_SZ / 8 / p->osize + 1); + pg->ages = (uint8_t*)malloc_s(GC_PAGE_SZ / 8 / p->osize + 1); pg->thread_n = ptls->tid; jl_taggedvalue_t *fl = reset_page(p, pg, NULL); p->newpages = fl; @@ -1515,10 +1561,10 @@ static void NOINLINE gc_mark_stack_resize(jl_gc_mark_cache_t *gc_cache, jl_gc_ma void **pc_stack = sp->pc_start; size_t stack_size = (char*)sp->pc_end - (char*)pc_stack; JL_LOCK_NOGC(&gc_cache->stack_lock); - gc_cache->data_stack = (jl_gc_mark_data_t *)realloc(old_data, stack_size * 2 * sizeof(jl_gc_mark_data_t)); + gc_cache->data_stack = (jl_gc_mark_data_t *)realloc_s(old_data, stack_size * 2 * sizeof(jl_gc_mark_data_t)); sp->data = (jl_gc_mark_data_t *)(((char*)sp->data) + (((char*)gc_cache->data_stack) - ((char*)old_data))); - sp->pc_start = gc_cache->pc_stack = (void**)realloc(pc_stack, stack_size * 2 * sizeof(void*)); + sp->pc_start = gc_cache->pc_stack = (void**)realloc_s(pc_stack, stack_size * 2 * sizeof(void*)); gc_cache->pc_stack_end = sp->pc_end = sp->pc_start + stack_size * 2; sp->pc += sp->pc_start - pc_stack; JL_UNLOCK_NOGC(&gc_cache->stack_lock); @@ -1688,15 +1734,13 @@ STATIC_INLINE int gc_mark_scan_objarray(jl_ptls_t ptls, jl_gc_mark_sp_t *sp, // Scan an object with 8bits field descriptors. see `gc_mark_obj8_t` STATIC_INLINE int gc_mark_scan_obj8(jl_ptls_t ptls, jl_gc_mark_sp_t *sp, gc_mark_obj8_t *obj8, - char *parent, jl_fielddesc8_t *begin, jl_fielddesc8_t *end, + char *parent, uint8_t *begin, uint8_t *end, jl_value_t **pnew_obj, uintptr_t *ptag, uint8_t *pbits) { (void)jl_assume(obj8 == (gc_mark_obj8_t*)sp->data); (void)jl_assume(begin < end); for (; begin < end; begin++) { - if (!begin->isptr) - continue; - jl_value_t **slot = (jl_value_t**)(parent + begin->offset); + jl_value_t **slot = &((jl_value_t**)parent)[*begin]; *pnew_obj = *slot; if (*pnew_obj) verify_parent2("object", parent, slot, "field(%d)", @@ -1723,15 +1767,13 @@ STATIC_INLINE int gc_mark_scan_obj8(jl_ptls_t ptls, jl_gc_mark_sp_t *sp, gc_mark // Scan an object with 16bits field descriptors. see `gc_mark_obj16_t` STATIC_INLINE int gc_mark_scan_obj16(jl_ptls_t ptls, jl_gc_mark_sp_t *sp, gc_mark_obj16_t *obj16, - char *parent, jl_fielddesc16_t *begin, jl_fielddesc16_t *end, + char *parent, uint16_t *begin, uint16_t *end, jl_value_t **pnew_obj, uintptr_t *ptag, uint8_t *pbits) JL_NOTSAFEPOINT { (void)jl_assume(obj16 == (gc_mark_obj16_t*)sp->data); (void)jl_assume(begin < end); for (; begin < end; begin++) { - if (!begin->isptr) - continue; - jl_value_t **slot = (jl_value_t**)(parent + begin->offset); + jl_value_t **slot = &((jl_value_t**)parent)[*begin]; *pnew_obj = *slot; if (*pnew_obj) verify_parent2("object", parent, slot, "field(%d)", @@ -1758,15 +1800,13 @@ STATIC_INLINE int gc_mark_scan_obj16(jl_ptls_t ptls, jl_gc_mark_sp_t *sp, gc_mar // Scan an object with 32bits field descriptors. see `gc_mark_obj32_t` STATIC_INLINE int gc_mark_scan_obj32(jl_ptls_t ptls, jl_gc_mark_sp_t *sp, gc_mark_obj32_t *obj32, - char *parent, jl_fielddesc32_t *begin, jl_fielddesc32_t *end, + char *parent, uint32_t *begin, uint32_t *end, jl_value_t **pnew_obj, uintptr_t *ptag, uint8_t *pbits) { (void)jl_assume(obj32 == (gc_mark_obj32_t*)sp->data); (void)jl_assume(begin < end); for (; begin < end; begin++) { - if (!begin->isptr) - continue; - jl_value_t **slot = (jl_value_t**)(parent + begin->offset); + jl_value_t **slot = &((jl_value_t**)parent)[*begin]; *pnew_obj = *slot; if (*pnew_obj) verify_parent2("object", parent, slot, "field(%d)", @@ -1917,13 +1957,13 @@ JL_EXTENSION NOINLINE void gc_mark_loop(jl_ptls_t ptls, jl_gc_mark_sp_t sp) gc_mark_obj8_t *obj8; char *obj8_parent; - jl_fielddesc8_t *obj8_begin; - jl_fielddesc8_t *obj8_end; + uint8_t *obj8_begin; + uint8_t *obj8_end; gc_mark_obj16_t *obj16; char *obj16_parent; - jl_fielddesc16_t *obj16_begin; - jl_fielddesc16_t *obj16_end; + uint16_t *obj16_begin; + uint16_t *obj16_end; pop: if (sp.pc == sp.pc_start) { @@ -1987,8 +2027,8 @@ scan_only: { obj32: { gc_mark_obj32_t *obj32 = gc_pop_markdata(&sp, gc_mark_obj32_t); char *parent = (char*)obj32->parent; - jl_fielddesc32_t *begin = obj32->begin; - jl_fielddesc32_t *end = obj32->end; + uint32_t *begin = obj32->begin; + uint32_t *end = obj32->end; if (gc_mark_scan_obj32(ptls, &sp, obj32, parent, begin, end, &new_obj, &tag, &bits)) goto mark; goto pop; @@ -2059,34 +2099,41 @@ excstack: { gc_mark_excstack_t *stackitr = gc_pop_markdata(&sp, gc_mark_excstack_t); jl_excstack_t *excstack = stackitr->s; size_t itr = stackitr->itr; - size_t i = stackitr->i; + size_t bt_index = stackitr->bt_index; + size_t jlval_index = stackitr->jlval_index; while (itr > 0) { size_t bt_size = jl_excstack_bt_size(excstack, itr); - uintptr_t *bt_data = jl_excstack_bt_data(excstack, itr); - while (i+2 < bt_size) { - if (bt_data[i] != JL_BT_INTERP_FRAME) { - i++; + jl_bt_element_t *bt_data = jl_excstack_bt_data(excstack, itr); + for (; bt_index < bt_size; bt_index += jl_bt_entry_size(bt_data + bt_index)) { + jl_bt_element_t *bt_entry = bt_data + bt_index; + if (jl_bt_is_native(bt_entry)) continue; - } - // found an interpreter frame to mark - new_obj = (jl_value_t*)bt_data[i+1]; - uintptr_t nptr = 0; - i += 3; - if (gc_try_setmark(new_obj, &nptr, &tag, &bits)) { - stackitr->i = i; - stackitr->itr = itr; - gc_repush_markdata(&sp, gc_mark_excstack_t); - goto mark; + // Found an extended backtrace entry: iterate over any + // GC-managed values inside. + size_t njlvals = jl_bt_num_jlvals(bt_entry); + while (jlval_index < njlvals) { + new_obj = jl_bt_entry_jlvalue(bt_entry, jlval_index); + uintptr_t nptr = 0; + jlval_index += 1; + if (gc_try_setmark(new_obj, &nptr, &tag, &bits)) { + stackitr->itr = itr; + stackitr->bt_index = bt_index; + stackitr->jlval_index = jlval_index; + gc_repush_markdata(&sp, gc_mark_excstack_t); + goto mark; + } } } - // mark the exception + // The exception comes last - mark it new_obj = jl_excstack_exception(excstack, itr); itr = jl_excstack_next(excstack, itr); - i = 0; + bt_index = 0; + jlval_index = 0; uintptr_t nptr = 0; if (gc_try_setmark(new_obj, &nptr, &tag, &bits)) { - stackitr->i = i; stackitr->itr = itr; + stackitr->bt_index = bt_index; + stackitr->jlval_index = jlval_index; gc_repush_markdata(&sp, gc_mark_excstack_t); goto mark; } @@ -2281,7 +2328,7 @@ mark: { } goto pop; } - if (!flags.ptrarray || a->data == NULL) + if (!flags.ptrarray || a->data == NULL || jl_array_len(a) == 0) goto pop; size_t l = jl_array_len(a); uintptr_t nptr = (l << 2) | (bits & GC_OLD); @@ -2343,6 +2390,7 @@ mark: { s = ta->gcstack; #ifdef COPY_STACKS if (ta->copy_stack) { + assert(tid != -1 && ptls2 != NULL); ub = (uintptr_t)ptls2->stackbase; lb = ub - ta->copy_stack; offset = (uintptr_t)stkbuf - lb; @@ -2359,7 +2407,7 @@ mark: { if (ta->excstack) { gc_setmark_buf_(ptls, ta->excstack, bits, sizeof(jl_excstack_t) + sizeof(uintptr_t)*ta->excstack->reserved_size); - gc_mark_excstack_t stackdata = {ta->excstack, ta->excstack->top, 0}; + gc_mark_excstack_t stackdata = {ta->excstack, ta->excstack->top, 0, 0}; gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(excstack), &stackdata, sizeof(stackdata), 1); } @@ -2367,10 +2415,11 @@ mark: { assert(layout->fielddesc_type == 0); size_t nfields = layout->nfields; assert(nfields > 0); - obj8_begin = (jl_fielddesc8_t*)jl_dt_layout_fields(layout); - obj8_end = obj8_begin + nfields; + uint32_t npointers = layout->npointers; + obj8_begin = (uint8_t*)jl_dt_layout_ptrs(layout); + obj8_end = obj8_begin + npointers; // assume tasks always reference young objects: set lowest bit - uintptr_t nptr = (9 << 2) | 1 | bits; + uintptr_t nptr = (npointers << 2) | 1 | bits; gc_mark_obj8_t markdata = {new_obj, obj8_begin, obj8_end, nptr}; gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(obj8), &markdata, sizeof(markdata), 0); @@ -2400,17 +2449,12 @@ mark: { uint32_t npointers = layout->npointers; if (npointers == 0) goto pop; - uintptr_t nptr = ((npointers & 0xff) << (npointers & 0x300)) << 2; - nptr = nptr | (bits & GC_OLD); - uint32_t offsets = jl_datatype_layout_n_nonptr(layout); - size_t nfields = layout->nfields; - nfields -= offsets & 0xffff; - size_t first = offsets >> 16; + uintptr_t nptr = npointers << 2 | (bits & GC_OLD); + assert(layout->nfields > 0 && layout->fielddesc_type != 3 && "opaque types should have been handled specially"); if (layout->fielddesc_type == 0) { - jl_fielddesc8_t *desc = (jl_fielddesc8_t*)jl_dt_layout_fields(layout); obj8_parent = (char*)new_obj; - obj8_begin = desc + first; - obj8_end = desc + nfields; + obj8_begin = (uint8_t*)jl_dt_layout_ptrs(layout); + obj8_end = obj8_begin + npointers; assert(obj8_begin < obj8_end); gc_mark_obj8_t markdata = {new_obj, obj8_begin, obj8_end, nptr}; gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(obj8), @@ -2419,10 +2463,9 @@ mark: { goto obj8_loaded; } else if (layout->fielddesc_type == 1) { - jl_fielddesc16_t *desc = (jl_fielddesc16_t*)jl_dt_layout_fields(layout); obj16_parent = (char*)new_obj; - obj16_begin = desc + first; - obj16_end = desc + nfields; + obj16_begin = (uint16_t*)jl_dt_layout_ptrs(layout); + obj16_end = obj16_begin + npointers; assert(obj16_begin < obj16_end); gc_mark_obj16_t markdata = {new_obj, obj16_begin, obj16_end, nptr}; gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(obj16), @@ -2433,9 +2476,9 @@ mark: { else if (layout->fielddesc_type == 2) { // This is very uncommon // Do not do store to load forwarding to save some code size - jl_fielddesc32_t *desc = (jl_fielddesc32_t*)jl_dt_layout_fields(layout); - assert(first < nfields); - gc_mark_obj32_t markdata = {new_obj, desc + first, desc + nfields, nptr}; + uint32_t *obj32_begin = (uint32_t*)jl_dt_layout_ptrs(layout); + uint32_t *obj32_end = obj32_begin + npointers; + gc_mark_obj32_t markdata = {new_obj, obj32_begin, obj32_end, nptr}; gc_mark_stack_push(&ptls->gc_cache, &sp, gc_mark_laddr(obj32), &markdata, sizeof(markdata), 0); sp.data = (jl_gc_mark_data_t *)(((char*)sp.data) + sizeof(markdata)); @@ -2444,7 +2487,6 @@ mark: { else { assert(layout->fielddesc_type == 3); jl_fielddescdyn_t *desc = (jl_fielddescdyn_t*)jl_dt_layout_fields(layout); - int old = jl_astaggedvalue(new_obj)->bits.gc & 2; export_gc_state(ptls, &sp); uintptr_t young = desc->markfunc(ptls, new_obj); @@ -2572,23 +2614,26 @@ JL_DLLEXPORT int jl_gc_enable(int on) } return prev; } + JL_DLLEXPORT int jl_gc_is_enabled(void) { jl_ptls_t ptls = jl_get_ptls_states(); return !ptls->disable_gc; } -JL_DLLEXPORT int64_t jl_gc_total_bytes(void) +JL_DLLEXPORT void jl_gc_get_total_bytes(int64_t *bytes) { jl_gc_num_t num = gc_num; combine_thread_gc_counts(&num); // Sync this logic with `base/util.jl:GC_Diff` - return (num.total_allocd + num.deferred_alloc + num.allocd); + *bytes = (num.total_allocd + num.deferred_alloc + num.allocd); } + JL_DLLEXPORT uint64_t jl_gc_total_hrtime(void) { return gc_num.total_time; } + JL_DLLEXPORT jl_gc_num_t jl_gc_num(void) { jl_gc_num_t num = gc_num; @@ -2596,14 +2641,20 @@ JL_DLLEXPORT jl_gc_num_t jl_gc_num(void) return num; } +// TODO: these were supposed to be thread local JL_DLLEXPORT int64_t jl_gc_diff_total_bytes(void) { int64_t oldtb = last_gc_total_bytes; - int64_t newtb = jl_gc_total_bytes(); + int64_t newtb; + jl_gc_get_total_bytes(&newtb); last_gc_total_bytes = newtb; return newtb - oldtb; } -void jl_gc_sync_total_bytes(void) {last_gc_total_bytes = jl_gc_total_bytes();} + +void jl_gc_sync_total_bytes(void) +{ + jl_gc_get_total_bytes(&last_gc_total_bytes); +} static void jl_gc_premark(jl_ptls_t ptls2) { @@ -2654,13 +2705,15 @@ static void jl_gc_queue_remset(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp static void jl_gc_queue_bt_buf(jl_gc_mark_cache_t *gc_cache, jl_gc_mark_sp_t *sp, jl_ptls_t ptls2) { - size_t n = 0; - while (n+2 < ptls2->bt_size) { - if (ptls2->bt_data[n] == JL_BT_INTERP_FRAME) { - gc_mark_queue_obj(gc_cache, sp, (jl_value_t*)ptls2->bt_data[n+1]); - n += 2; - } - n++; + jl_bt_element_t *bt_data = ptls2->bt_data; + size_t bt_size = ptls2->bt_size; + for (size_t i = 0; i < bt_size; i += jl_bt_entry_size(bt_data + i)) { + jl_bt_element_t *bt_entry = bt_data + i; + if (jl_bt_is_native(bt_entry)) + continue; + size_t njlvals = jl_bt_num_jlvals(bt_entry); + for (size_t j = 0; j < njlvals; j++) + gc_mark_queue_obj(gc_cache, sp, jl_bt_entry_jlvalue(bt_entry, j)); } } @@ -2892,6 +2945,10 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) return; } JL_TIMING(GC); + int last_errno = errno; +#ifdef _OS_WINDOWS_ + DWORD last_error = GetLastError(); +#endif // Now we are ready to wait for other threads to hit the safepoint, // we can do a few things that doesn't require synchronization. // TODO (concurrently queue objects) @@ -2926,6 +2983,10 @@ JL_DLLEXPORT void jl_gc_collect(jl_gc_collection_t collection) } gc_invoke_callbacks(jl_gc_cb_post_gc_t, gc_cblist_post_gc, (collection)); +#ifdef _OS_WINDOWS_ + SetLastError(last_error); +#endif + errno = last_errno; } void gc_mark_queue_all_roots(jl_ptls_t ptls, jl_gc_mark_sp_t *sp) @@ -2975,9 +3036,9 @@ void jl_init_thread_heap(jl_ptls_t ptls) gc_cache->nbig_obj = 0; JL_MUTEX_INIT(&gc_cache->stack_lock); size_t init_size = 1024; - gc_cache->pc_stack = (void**)malloc(init_size * sizeof(void*)); + gc_cache->pc_stack = (void**)malloc_s(init_size * sizeof(void*)); gc_cache->pc_stack_end = gc_cache->pc_stack + init_size; - gc_cache->data_stack = (jl_gc_mark_data_t *)malloc(init_size * sizeof(jl_gc_mark_data_t)); + gc_cache->data_stack = (jl_gc_mark_data_t *)malloc_s(init_size * sizeof(jl_gc_mark_data_t)); memset(&ptls->gc_num, 0, sizeof(jl_thread_gc_num_t)); assert(gc_num.interval == default_collect_interval); @@ -3122,9 +3183,17 @@ JL_DLLEXPORT void *jl_gc_managed_malloc(size_t sz) jl_throw(jl_memory_exception); ptls->gc_num.allocd += allocsz; ptls->gc_num.malloc++; + int last_errno = errno; +#ifdef _OS_WINDOWS_ + DWORD last_error = GetLastError(); +#endif void *b = malloc_cache_align(allocsz); if (b == NULL) jl_throw(jl_memory_exception); +#ifdef _OS_WINDOWS_ + SetLastError(last_error); +#endif + errno = last_errno; return b; } @@ -3148,6 +3217,10 @@ static void *gc_managed_realloc_(jl_ptls_t ptls, void *d, size_t sz, size_t olds ptls->gc_num.allocd += (allocsz - oldsz); ptls->gc_num.realloc++; + int last_errno = errno; +#ifdef _OS_WINDOWS_ + DWORD last_error = GetLastError(); +#endif void *b; if (isaligned) b = realloc_cache_align(d, allocsz, oldsz); @@ -3155,6 +3228,10 @@ static void *gc_managed_realloc_(jl_ptls_t ptls, void *d, size_t sz, size_t olds b = realloc(d, allocsz); if (b == NULL) jl_throw(jl_memory_exception); +#ifdef _OS_WINDOWS_ + SetLastError(last_error); +#endif + errno = last_errno; return b; } @@ -3219,7 +3296,17 @@ static void *gc_perm_alloc_large(size_t sz, int zero, unsigned align, unsigned o const size_t malloc_align = sizeof(void*) == 8 ? 16 : 4; if (align > 1 && (offset != 0 || align > malloc_align)) sz += align - 1; + int last_errno = errno; +#ifdef _OS_WINDOWS_ + DWORD last_error = GetLastError(); +#endif uintptr_t base = (uintptr_t)(zero ? calloc(1, sz) : malloc(sz)); + if (base == 0) + jl_throw(jl_memory_exception); +#ifdef _OS_WINDOWS_ + SetLastError(last_error); +#endif + errno = last_errno; jl_may_leak(base); unsigned diff = (offset - base) % align; return (void*)(base + diff); @@ -3247,13 +3334,18 @@ void *jl_gc_perm_alloc_nolock(size_t sz, int zero, unsigned align, unsigned offs void *ptr = gc_try_perm_alloc_pool(sz, align, offset); if (__likely(ptr)) return ptr; + int last_errno = errno; #ifdef _OS_WINDOWS_ + DWORD last_error = GetLastError(); void *pool = VirtualAlloc(NULL, GC_PERM_POOL_SIZE, MEM_COMMIT, PAGE_READWRITE); + SetLastError(last_error); + errno = last_errno; if (__unlikely(pool == NULL)) return NULL; #else void *pool = mmap(0, GC_PERM_POOL_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + errno = last_errno; if (__unlikely(pool == MAP_FAILED)) return NULL; #endif diff --git a/src/gc.h b/src/gc.h index a9c360bff3820..3e81193b67b5c 100644 --- a/src/gc.h +++ b/src/gc.h @@ -119,24 +119,24 @@ typedef struct { // A normal object with 8bits field descriptors typedef struct { jl_value_t *parent; // The parent object to trigger write barrier on. - jl_fielddesc8_t *begin; // Current field descriptor. - jl_fielddesc8_t *end; // End of field descriptor. + uint8_t *begin; // Current field descriptor. + uint8_t *end; // End of field descriptor. uintptr_t nptr; // See notes about `nptr` above. } gc_mark_obj8_t; // A normal object with 16bits field descriptors typedef struct { jl_value_t *parent; // The parent object to trigger write barrier on. - jl_fielddesc16_t *begin; // Current field descriptor. - jl_fielddesc16_t *end; // End of field descriptor. + uint16_t *begin; // Current field descriptor. + uint16_t *end; // End of field descriptor. uintptr_t nptr; // See notes about `nptr` above. } gc_mark_obj16_t; // A normal object with 32bits field descriptors typedef struct { jl_value_t *parent; // The parent object to trigger write barrier on. - jl_fielddesc32_t *begin; // Current field descriptor. - jl_fielddesc32_t *end; // End of field descriptor. + uint32_t *begin; // Current field descriptor. + uint32_t *end; // End of field descriptor. uintptr_t nptr; // See notes about `nptr` above. } gc_mark_obj32_t; @@ -153,9 +153,10 @@ typedef struct { // Exception stack data typedef struct { - jl_excstack_t *s; // Stack of exceptions - size_t itr; // Iterator into exception stack - size_t i; // Iterator into backtrace data for exception + jl_excstack_t *s; // Stack of exceptions + size_t itr; // Iterator into exception stack + size_t bt_index; // Current backtrace buffer entry index + size_t jlval_index; // Index into GC managed values for current bt entry } gc_mark_excstack_t; // Module bindings. This is also the beginning of module scanning. diff --git a/src/gf.c b/src/gf.c index 7168522de2933..5dc9fc71e7d17 100644 --- a/src/gf.c +++ b/src/gf.c @@ -201,6 +201,10 @@ jl_code_info_t *jl_type_infer(jl_method_instance_t *mi, size_t world, int force) } #endif jl_ptls_t ptls = jl_get_ptls_states(); + int last_errno = errno; +#ifdef _OS_WINDOWS_ + DWORD last_error = GetLastError(); +#endif size_t last_age = ptls->world_age; ptls->world_age = jl_typeinf_world; mi->inInference = 1; @@ -218,6 +222,10 @@ jl_code_info_t *jl_type_infer(jl_method_instance_t *mi, size_t world, int force) ptls->world_age = last_age; in_inference--; mi->inInference = 0; +#ifdef _OS_WINDOWS_ + SetLastError(last_error); +#endif + errno = last_errno; if (src && !jl_is_code_info(src)) { src = NULL; @@ -271,7 +279,7 @@ JL_DLLEXPORT jl_code_instance_t *jl_get_method_inferred( } JL_DLLEXPORT jl_code_instance_t *jl_set_method_inferred( - jl_method_instance_t *mi, jl_value_t *rettype, + jl_method_instance_t *mi JL_PROPAGATES_ROOT, jl_value_t *rettype, jl_value_t *inferred_const, jl_value_t *inferred, int32_t const_flags, size_t min_world, size_t max_world /*, jl_array_t *edges, int absolute_max*/) @@ -1825,6 +1833,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t { jl_code_instance_t *codeinst; codeinst = mi->cache; + while (codeinst) { if (codeinst->min_world <= world && world <= codeinst->max_world && codeinst->invoke != NULL) { return codeinst; @@ -1861,6 +1870,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t } } + JL_LOCK(&codegen_lock); codeinst = mi->cache; while (codeinst) { if (codeinst->min_world <= world && world <= codeinst->max_world && codeinst->functionObjectsDecls.functionObject != NULL) @@ -1885,6 +1895,7 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t jl_generate_fptr(ucache); if (ucache->invoke != jl_fptr_sparam && ucache->invoke != jl_fptr_interpret_call) { + JL_UNLOCK(&codegen_lock); return ucache; } jl_code_instance_t *codeinst = jl_set_method_inferred(mi, (jl_value_t*)jl_any_type, NULL, NULL, @@ -1892,14 +1903,17 @@ jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *mi, size_t codeinst->specptr = ucache->specptr; codeinst->rettype_const = ucache->rettype_const; jl_atomic_store_release(&codeinst->invoke, ucache->invoke); + JL_UNLOCK(&codegen_lock); return codeinst; } } + JL_UNLOCK(&codegen_lock); jl_generate_fptr(codeinst); return codeinst; } + JL_DLLEXPORT jl_value_t *jl_fptr_const_return(jl_value_t *f, jl_value_t **args, uint32_t nargs, jl_code_instance_t *m) { return m->rettype_const; @@ -2132,7 +2146,15 @@ STATIC_INLINE jl_value_t *_jl_invoke(jl_value_t *F, jl_value_t **args, uint32_t } codeinst = codeinst->next; } + int last_errno = errno; +#ifdef _OS_WINDOWS_ + DWORD last_error = GetLastError(); +#endif codeinst = jl_compile_method_internal(mfunc, world); +#ifdef _OS_WINDOWS_ + SetLastError(last_error); +#endif + errno = last_errno; jl_value_t *res = codeinst->invoke(F, args, nargs, codeinst); return verify_type(res); } @@ -2433,7 +2455,7 @@ jl_function_t *jl_new_generic_function_with_supertype(jl_sym_t *name, jl_module_ // type name is function name prefixed with # size_t l = strlen(jl_symbol_name(name)); char *prefixed; - prefixed = (char*)malloc(l+2); + prefixed = (char*)malloc_s(l+2); prefixed[0] = '#'; strcpy(&prefixed[1], jl_symbol_name(name)); jl_sym_t *tname = jl_symbol(prefixed); @@ -2471,7 +2493,7 @@ JL_DLLEXPORT jl_function_t *jl_get_kwsorter(jl_value_t *ty) name++; } size_t l = strlen(name); - char *suffixed = (char*)malloc(l+5); + char *suffixed = (char*)malloc_s(l+5); strcpy(&suffixed[0], name); strcpy(&suffixed[l], "##kw"); jl_sym_t *fname = jl_symbol(suffixed); diff --git a/src/init.c b/src/init.c index 5befe31a5ff20..b8f668d023372 100644 --- a/src/init.c +++ b/src/init.c @@ -40,9 +40,6 @@ JL_DLLEXPORT char *dirname(char *); #endif #ifdef _OS_WINDOWS_ -#define WIN32_LEAN_AND_MEAN -#include -#include extern int needsSymRefreshModuleList; extern BOOL (WINAPI *hSymRefreshModuleList)(HANDLE); #else @@ -153,7 +150,7 @@ struct uv_shutdown_queue { struct uv_shutdown_queue_item *first; struct uv_shutd static void jl_uv_exitcleanup_add(uv_handle_t *handle, struct uv_shutdown_queue *queue) { - struct uv_shutdown_queue_item *item = (struct uv_shutdown_queue_item*)malloc(sizeof(struct uv_shutdown_queue_item)); + struct uv_shutdown_queue_item *item = (struct uv_shutdown_queue_item*)malloc_s(sizeof(struct uv_shutdown_queue_item)); item->h = handle; item->next = NULL; if (queue->last) @@ -371,7 +368,7 @@ static void *init_stdio_handle(const char *stdio, uv_os_fd_t fd, int readable) jl_errorf("error initializing %s in uv_dup: %s (%s %d)", stdio, uv_strerror(err), uv_err_name(err), err); switch(uv_guess_handle(fd)) { case UV_TTY: - handle = malloc(sizeof(uv_tty_t)); + handle = malloc_s(sizeof(uv_tty_t)); if ((err = uv_tty_init(jl_io_loop, (uv_tty_t*)handle, fd, 0))) { jl_errorf("error initializing %s in uv_tty_init: %s (%s %d)", stdio, uv_strerror(err), uv_err_name(err), err); } @@ -401,7 +398,7 @@ static void *init_stdio_handle(const char *stdio, uv_os_fd_t fd, int readable) // ...and continue on as in the UV_FILE case JL_FALLTHROUGH; case UV_FILE: - handle = malloc(sizeof(jl_uv_file_t)); + handle = malloc_s(sizeof(jl_uv_file_t)); { jl_uv_file_t *file = (jl_uv_file_t*)handle; file->loop = jl_io_loop; @@ -411,7 +408,7 @@ static void *init_stdio_handle(const char *stdio, uv_os_fd_t fd, int readable) } break; case UV_NAMED_PIPE: - handle = malloc(sizeof(uv_pipe_t)); + handle = malloc_s(sizeof(uv_pipe_t)); if ((err = uv_pipe_init(jl_io_loop, (uv_pipe_t*)handle, 0))) { jl_errorf("error initializing %s in uv_pipe_init: %s (%s %d)", stdio, uv_strerror(err), uv_err_name(err), err); } @@ -421,7 +418,7 @@ static void *init_stdio_handle(const char *stdio, uv_os_fd_t fd, int readable) ((uv_pipe_t*)handle)->data = NULL; break; case UV_TCP: - handle = malloc(sizeof(uv_tcp_t)); + handle = malloc_s(sizeof(uv_tcp_t)); if ((err = uv_tcp_init(jl_io_loop, (uv_tcp_t*)handle))) { jl_errorf("error initializing %s in uv_tcp_init: %s (%s %d)", stdio, uv_strerror(err), uv_err_name(err), err); } @@ -479,9 +476,7 @@ static char *abspath(const char *in, int nprefix) if (out) { if (nprefix > 0) { size_t sz = strlen(out) + 1; - char *cpy = (char*)malloc(sz + nprefix); - if (!cpy) - jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno)); + char *cpy = (char*)malloc_s(sz + nprefix); memcpy(cpy, in, nprefix); memcpy(cpy + nprefix, out, sz); free(out); @@ -491,22 +486,16 @@ static char *abspath(const char *in, int nprefix) else { size_t sz = strlen(in + nprefix) + 1; if (in[nprefix] == PATHSEPSTRING[0]) { - out = (char*)malloc(sz + nprefix); - if (!out) - jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno)); + out = (char*)malloc_s(sz + nprefix); memcpy(out, in, sz + nprefix); } else { size_t path_size = PATH_MAX; - char *path = (char*)malloc(PATH_MAX); - if (!path) - jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno)); + char *path = (char*)malloc_s(PATH_MAX); if (uv_cwd(path, &path_size)) { jl_error("fatal error: unexpected error while retrieving current working directory"); } - out = (char*)malloc(path_size + 1 + sz + nprefix); - if (!out) - jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno)); + out = (char*)malloc_s(path_size + 1 + sz + nprefix); memcpy(out, in, nprefix); memcpy(out + nprefix, path, path_size); out[nprefix + path_size] = PATHSEPSTRING[0]; @@ -519,9 +508,7 @@ static char *abspath(const char *in, int nprefix) if (n <= 0) { jl_error("fatal error: jl_options.image_file path too long or GetFullPathName failed"); } - char *out = (char*)malloc(n + nprefix); - if (!out) - jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno)); + char *out = (char*)malloc_s(n + nprefix); DWORD m = GetFullPathName(in + nprefix, n, out + nprefix, NULL); if (n != m + 1) { jl_error("fatal error: jl_options.image_file path too long or GetFullPathName failed"); @@ -548,9 +535,7 @@ static const char *absformat(const char *in) size_t i, fmt_size = 0; for (i = 0; i < path_size; i++) fmt_size += (path[i] == '%' ? 2 : 1); - char *out = (char*)malloc(fmt_size + 1 + sz); - if (!out) - jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno)); + char *out = (char*)malloc_s(fmt_size + 1 + sz); fmt_size = 0; for (i = 0; i < path_size; i++) { // copy-replace pwd portion char c = path[i]; @@ -571,19 +556,15 @@ static void jl_resolve_sysimg_location(JL_IMAGE_SEARCH rel) // note: if you care about lost memory, you should call the appropriate `free()` function // on the original pointer for each `char*` you've inserted into `jl_options`, after // calling `julia_init()` - char *free_path = (char*)malloc(PATH_MAX); + char *free_path = (char*)malloc_s(PATH_MAX); size_t path_size = PATH_MAX; - if (!free_path) - jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno)); if (uv_exepath(free_path, &path_size)) { jl_error("fatal error: unexpected error while retrieving exepath"); } if (path_size >= PATH_MAX) { jl_error("fatal error: jl_options.julia_bin path too long"); } - jl_options.julia_bin = (char*)malloc(path_size+1); - if (!jl_options.julia_bin) - jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno)); + jl_options.julia_bin = (char*)malloc_s(path_size + 1); memcpy((char*)jl_options.julia_bin, free_path, path_size); ((char*)jl_options.julia_bin)[path_size] = '\0'; if (!jl_options.julia_bindir) { @@ -599,9 +580,7 @@ static void jl_resolve_sysimg_location(JL_IMAGE_SEARCH rel) if (jl_options.image_file) { if (rel == JL_IMAGE_JULIA_HOME && !isabspath(jl_options.image_file)) { // build time path, relative to JULIA_BINDIR - free_path = (char*)malloc(PATH_MAX); - if (!free_path) - jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno)); + free_path = (char*)malloc_s(PATH_MAX); int n = snprintf(free_path, PATH_MAX, "%s" PATHSEPSTRING "%s", jl_options.julia_bindir, jl_options.image_file); if (n >= PATH_MAX || n < 0) { diff --git a/src/interpreter-stacktrace.c b/src/interpreter-stacktrace.c index 18cd5f4243fc1..dacccb5e12e77 100644 --- a/src/interpreter-stacktrace.c +++ b/src/interpreter-stacktrace.c @@ -390,20 +390,29 @@ JL_DLLEXPORT int jl_is_enter_interpreter_frame(uintptr_t ip) return enter_interpreter_frame_start <= ip && ip <= enter_interpreter_frame_end; } -JL_DLLEXPORT size_t jl_capture_interp_frame(uintptr_t *data, uintptr_t sp, uintptr_t fp, size_t space_remaining) +JL_DLLEXPORT size_t jl_capture_interp_frame(jl_bt_element_t *bt_entry, uintptr_t sp, + uintptr_t fp, size_t space_remaining) { #ifdef FP_CAPTURE_OFFSET interpreter_state *s = (interpreter_state *)(fp-FP_CAPTURE_OFFSET); #else interpreter_state *s = (interpreter_state *)(sp+TOTAL_STACK_PADDING); #endif - int required_space = 3; + int need_module = !s->mi; + int required_space = need_module ? 4 : 3; if (space_remaining < required_space) - return 0; - // Sentinel value to indicate an interpreter frame - data[0] = JL_BT_INTERP_FRAME; - data[1] = s->mi ? (uintptr_t)s->mi : s->src ? (uintptr_t)s->src : (uintptr_t)jl_nothing; - data[2] = (uintptr_t)s->ip; + return 0; // Should not happen + size_t njlvalues = need_module ? 2 : 1; + uintptr_t entry_tags = jl_bt_entry_descriptor(njlvalues, 0, JL_BT_INTERP_FRAME_TAG, s->ip); + bt_entry[0].uintptr = JL_BT_NON_PTR_ENTRY; + bt_entry[1].uintptr = entry_tags; + bt_entry[2].jlvalue = s->mi ? (jl_value_t*)s->mi : + s->src ? (jl_value_t*)s->src : (jl_value_t*)jl_nothing; + if (need_module) { + // If we only have a CodeInfo (s->src), we are in a top level thunk and + // need to record the module separately. + bt_entry[3].jlvalue = (jl_value_t*)s->module; + } return required_space; } @@ -419,7 +428,8 @@ JL_DLLEXPORT int jl_is_enter_interpreter_frame(uintptr_t ip) return 0; } -JL_DLLEXPORT size_t jl_capture_interp_frame(uintptr_t *data, uintptr_t sp, uintptr_t fp, size_t space_remaining) +JL_DLLEXPORT size_t jl_capture_interp_frame(jl_bt_element_t *bt_entry, uintptr_t sp, + uintptr_t fp, size_t space_remaining) { // Leave bt_entry[0] as the native instruction ptr return 0; diff --git a/src/intrinsics.cpp b/src/intrinsics.cpp index bacde234884ac..281e1661a8d42 100644 --- a/src/intrinsics.cpp +++ b/src/intrinsics.cpp @@ -70,6 +70,7 @@ static void jl_init_intrinsic_functions_codegen(Module *m) float_func[trunc_llvm] = true; float_func[rint_llvm] = true; float_func[sqrt_llvm] = true; + float_func[sqrt_llvm_fast] = true; } extern "C" @@ -1264,6 +1265,10 @@ static Value *emit_untyped_intrinsic(jl_codectx_t &ctx, intrinsic f, Value **arg Value *sqrtintr = Intrinsic::getDeclaration(jl_Module, Intrinsic::sqrt, makeArrayRef(t)); return ctx.builder.CreateCall(sqrtintr, x); } + case sqrt_llvm_fast: { + Value *sqrtintr = Intrinsic::getDeclaration(jl_Module, Intrinsic::sqrt, makeArrayRef(t)); + return math_builder(ctx, true)().CreateCall(sqrtintr, x); + } default: assert(0 && "invalid intrinsic"); diff --git a/src/intrinsics.h b/src/intrinsics.h index 5cc938cc8b37a..1558769eb3643 100644 --- a/src/intrinsics.h +++ b/src/intrinsics.h @@ -88,6 +88,7 @@ ADD_I(trunc_llvm, 1) \ ADD_I(rint_llvm, 1) \ ADD_I(sqrt_llvm, 1) \ + ADD_I(sqrt_llvm_fast, 1) \ /* pointer access */ \ ADD_I(pointerref, 3) \ ADD_I(pointerset, 4) \ diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index cf339d8a4bdd3..078f581ad111f 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -213,7 +213,6 @@ void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level, // Run instcombine after redundancy elimination to exploit opportunities // opened up by them. - PM->add(createSinkingPass()); ////////////// **** #if JL_LLVM_VERSION < 70000 // This pass is a subset of InstructionCombiningPass in LLVM 7 // and therefore not required. diff --git a/src/jl_uv.c b/src/jl_uv.c index 3fcb42ddee6e5..e2643ee776e35 100644 --- a/src/jl_uv.c +++ b/src/jl_uv.c @@ -173,7 +173,7 @@ JL_DLLEXPORT void jl_uv_flush(uv_stream_t *stream) uv_buf_t buf; buf.base = (char*)(&buf + 1); buf.len = 0; - uv_write_t *write_req = (uv_write_t*)malloc(sizeof(uv_write_t)); + uv_write_t *write_req = (uv_write_t*)malloc_s(sizeof(uv_write_t)); write_req->data = (void*)&fired; if (uv_write(write_req, stream, &buf, 1, uv_flush_callback) != 0) { JL_UV_UNLOCK(); @@ -203,23 +203,10 @@ JL_DLLEXPORT void *jl_uv_write_handle(uv_write_t *req) { return req->handle; } extern volatile unsigned _threadedregion; -JL_DLLEXPORT int jl_run_once(uv_loop_t *loop) -{ - jl_ptls_t ptls = jl_get_ptls_states(); - if (loop && (_threadedregion || ptls->tid == 0)) { - jl_gc_safepoint_(ptls); - JL_UV_LOCK(); - loop->stop_flag = 0; - int r = uv_run(loop, UV_RUN_ONCE); - JL_UV_UNLOCK(); - return r; - } - return 0; -} - -JL_DLLEXPORT int jl_process_events(uv_loop_t *loop) +JL_DLLEXPORT int jl_process_events(void) { jl_ptls_t ptls = jl_get_ptls_states(); + uv_loop_t *loop = jl_io_loop; if (loop && (_threadedregion || ptls->tid == 0)) { jl_gc_safepoint_(ptls); if (jl_mutex_trylock(&jl_uv_mutex)) { @@ -260,7 +247,7 @@ JL_DLLEXPORT void jl_close_uv(uv_handle_t *handle) } if (handle->type == UV_NAMED_PIPE || handle->type == UV_TCP || handle->type == UV_TTY) { - uv_write_t *req = (uv_write_t*)malloc(sizeof(uv_write_t)); + uv_write_t *req = (uv_write_t*)malloc_s(sizeof(uv_write_t)); req->handle = (uv_stream_t*)handle; jl_uv_flush_close_callback(req, 0); JL_UV_UNLOCK(); @@ -532,9 +519,9 @@ JL_DLLEXPORT void jl_uv_puts(uv_stream_t *stream, const char *str, size_t n) } else { // Write to libuv stream... - uv_write_t *req = (uv_write_t*)malloc(sizeof(uv_write_t)+n); - char *data = (char*)(req+1); - memcpy(data,str,n); + uv_write_t *req = (uv_write_t*)malloc_s(sizeof(uv_write_t) + n); + char *data = (char*)(req + 1); + memcpy(data, str, n); uv_buf_t buf[1]; buf[0].base = data; buf[0].len = n; @@ -610,6 +597,10 @@ JL_DLLEXPORT void jl_safe_printf(const char *fmt, ...) JL_NOTSAFEPOINT { static char buf[1000]; buf[0] = '\0'; + int last_errno = errno; +#ifdef _OS_WINDOWS_ + DWORD last_error = GetLastError(); +#endif va_list args; va_start(args, fmt); @@ -621,6 +612,10 @@ JL_DLLEXPORT void jl_safe_printf(const char *fmt, ...) JL_NOTSAFEPOINT if (write(STDERR_FILENO, buf, strlen(buf)) < 0) { // nothing we can do; ignore the failure } +#ifdef _OS_WINDOWS_ + SetLastError(last_error); +#endif + errno = last_errno; } JL_DLLEXPORT void jl_exit(int exitcode) @@ -836,7 +831,7 @@ JL_DLLEXPORT int jl_tcp_connect(uv_tcp_t *handle, void *host, uint16_t port, { uv_sockaddr_in addr; jl_sockaddr_fill(&addr, port, host, ipv6); - uv_connect_t *req = (uv_connect_t*)malloc(sizeof(uv_connect_t)); + uv_connect_t *req = (uv_connect_t*)malloc_s(sizeof(uv_connect_t)); req->data = NULL; int r = uv_tcp_connect(req, handle, &addr.in, cb); if (r) @@ -996,7 +991,7 @@ void jl_work_notifier(uv_work_t *req, int status) JL_DLLEXPORT int jl_queue_work(work_cb_t work_func, void *work_args, void *work_retval, notify_cb_t notify_func, int notify_idx) { - struct work_baton *baton = (struct work_baton*) malloc(sizeof(struct work_baton)); + struct work_baton *baton = (struct work_baton*)malloc_s(sizeof(struct work_baton)); baton->req.data = (void*) baton; baton->work_func = work_func; baton->work_args = work_args; diff --git a/src/jlapi.c b/src/jlapi.c index c28fb9cf5aa27..fd1a87ce2c884 100644 --- a/src/jlapi.c +++ b/src/jlapi.c @@ -36,6 +36,26 @@ JL_DLLEXPORT int jl_is_initialized(void) return jl_main_module != NULL; } +JL_DLLEXPORT void jl_set_ARGS(int argc, char **argv) +{ + if (jl_core_module != NULL) { + jl_array_t *args = (jl_array_t*)jl_get_global(jl_core_module, jl_symbol("ARGS")); + if (args == NULL) { + args = jl_alloc_vec_any(0); + JL_GC_PUSH1(&args); + jl_set_const(jl_core_module, jl_symbol("ARGS"), (jl_value_t*)args); + JL_GC_POP(); + } + assert(jl_array_len(args) == 0); + jl_array_grow_end(args, argc); + int i; + for (i = 0; i < argc; i++) { + jl_value_t *s = (jl_value_t*)jl_cstr_to_string(argv[i]); + jl_arrayset(args, s, i); + } + } +} + // First argument is the usr/bin directory where the julia binary is, or NULL to guess. // Second argument is the path of a system image file (*.ji) relative to the // first argument path, or relative to the default julia home dir. diff --git a/src/jloptions.c b/src/jloptions.c index 4177b4af0ac26..ffbc258eeee44 100644 --- a/src/jloptions.c +++ b/src/jloptions.c @@ -332,17 +332,11 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) case 'L': // load { size_t sz = strlen(optarg) + 1; - char *arg = (char*)malloc(sz + 1); + char *arg = (char*)malloc_s(sz + 1); const char **newcmds; - if (!arg) - jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno)); arg[0] = c; memcpy(arg + 1, optarg, sz); - newcmds = (const char**)realloc(cmds, (ncmds + 2) * sizeof(char*)); - if (!newcmds) { - free(cmds); - jl_errorf("fatal error: failed to allocate memory: %s", strerror(errno)); - } + newcmds = (const char**)realloc_s(cmds, (ncmds + 2) * sizeof(char*)); cmds = newcmds; cmds[ncmds] = arg; ncmds++; @@ -628,26 +622,6 @@ JL_DLLEXPORT void jl_parse_opts(int *argcp, char ***argvp) *argcp -= proc_args; } -JL_DLLEXPORT void jl_set_ARGS(int argc, char **argv) -{ - if (jl_core_module != NULL) { - jl_array_t *args = (jl_array_t*)jl_get_global(jl_core_module, jl_symbol("ARGS")); - if (args == NULL) { - args = jl_alloc_vec_any(0); - JL_GC_PUSH1(&args); - jl_set_const(jl_core_module, jl_symbol("ARGS"), (jl_value_t*)args); - JL_GC_POP(); - } - assert(jl_array_len(args) == 0); - jl_array_grow_end(args, argc); - int i; - for (i=0; i < argc; i++) { - jl_value_t *s = (jl_value_t*)jl_cstr_to_string(argv[i]); - jl_arrayset(args, s, i); - } - } -} - JL_DLLEXPORT ssize_t jl_sizeof_jl_options(void) { return sizeof(jl_options_t); diff --git a/src/jltypes.c b/src/jltypes.c index 03c00d9dfa347..29e552f457600 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -995,17 +995,19 @@ void jl_precompute_memoized_dt(jl_datatype_t *dt) size_t i, l = jl_nparams(dt); for (i = 0; i < l; i++) { jl_value_t *p = jl_tparam(dt, i); - if (!dt->hasfreetypevars) + if (!dt->hasfreetypevars) { dt->hasfreetypevars = jl_has_free_typevars(p); + if (dt->hasfreetypevars) + dt->isconcretetype = 0; + } if (istuple && dt->isconcretetype) dt->isconcretetype = (jl_is_datatype(p) && ((jl_datatype_t*)p)->isconcretetype) || p == jl_bottom_type; - if (dt->isdispatchtuple) + if (dt->isdispatchtuple) { dt->isdispatchtuple = jl_is_datatype(p) && ((!jl_is_kind(p) && ((jl_datatype_t*)p)->isconcretetype) || (((jl_datatype_t*)p)->name == jl_type_typename && !((jl_datatype_t*)p)->hasfreetypevars)); + } } - if (dt->hasfreetypevars) - dt->isconcretetype = 0; } static void check_datatype_parameters(jl_typename_t *tn, jl_value_t **params, size_t np) @@ -1043,7 +1045,7 @@ static void check_datatype_parameters(jl_typename_t *tn, jl_value_t **params, si arraylist_t partial_inst; int inside_typedef = 0; -static jl_value_t *extract_wrapper(jl_value_t *t) +static jl_value_t *extract_wrapper(jl_value_t *t JL_PROPAGATES_ROOT) { t = jl_unwrap_unionall(t); if (jl_is_datatype(t)) @@ -1112,7 +1114,9 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value jl_types_equal(pi, tw)) { if (jl_is_vararg_type(iparams[i])) { tw = jl_wrap_vararg(tw, jl_tparam1(jl_unwrap_unionall(iparams[i]))); + JL_GC_PUSH1(&tw); tw = jl_rewrap_unionall(tw, iparams[i]); + JL_GC_POP(); } iparams[i] = tw; if (p) jl_gc_wb(p, tw); @@ -1273,6 +1277,12 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value ndt->ditype = NULL; ndt->size = 0; jl_precompute_memoized_dt(ndt); + if (istuple) + ndt->ninitialized = ntp - isvatuple; + else if (isnamedtuple) + ndt->ninitialized = jl_svec_len(ndt->types); + else + ndt->ninitialized = dt->ninitialized; // assign uid as early as possible if (cacheable && !ndt->abstract) @@ -1313,22 +1323,16 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value jl_gc_wb(ndt, ndt->types); } } + if (jl_is_primitivetype(dt)) { ndt->size = dt->size; ndt->layout = dt->layout; - ndt->isbitstype = ndt->isinlinealloc = (!ndt->hasfreetypevars); + ndt->isbitstype = ndt->isinlinealloc = ndt->isconcretetype; } else if (cacheable && ndt->types != NULL && !ndt->abstract) { jl_compute_field_offsets(ndt); } - if (istuple) - ndt->ninitialized = ntp - isvatuple; - else if (isnamedtuple) - ndt->ninitialized = jl_svec_len(ndt->types); - else - ndt->ninitialized = dt->ninitialized; - if (cacheable) { JL_UNLOCK(&typecache_lock); // Might GC } diff --git a/src/julia-parser.scm b/src/julia-parser.scm index 5d4c142051eca..ae638a9b37eb4 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -1046,10 +1046,11 @@ ((eqv? next #\( ) (take-token s) (let* ((opspc (ts:space? s)) + (lno (input-port-line (ts:port s))) (parens (parse-paren- s #t))) (if (cdr parens) ;; found an argument list (if opspc - (disallowed-space op #\( ) + (disallowed-space-error lno op #\( ) (parse-factor-with-initial-ex s (fix-syntactic-unary (cons op (tuple-to-arglist (car parens)))))) @@ -1135,9 +1136,14 @@ (parse-where-chain s decl-sig) decl-sig))) -(define (disallowed-space ex t) +(define (disallowed-space-error lno ex t) (error (string "space before \"" t "\" not allowed in \"" - (deparse ex) " " t "\""))) + (deparse ex) " " t "\" at " + current-filename ":" lno))) + +(define (disallow-space s ex t) + (if (ts:space? s) + (disallowed-space-error (input-port-line (ts:port s)) ex t))) ;; string macro suffix for given delimiter t (define (macsuffix t) @@ -1156,7 +1162,7 @@ ex (case t ((#\( ) - (if (ts:space? s) (disallowed-space ex t)) + (disallow-space s ex t) (take-token s) (let ((c (let ((al (parse-call-arglist s #\) ))) (receive @@ -1177,7 +1183,7 @@ c) (loop c)))) ((#\[ ) - (if (ts:space? s) (disallowed-space ex t)) + (disallow-space s ex t) (take-token s) ;; ref is syntax, so we can distinguish ;; a[i] = x from @@ -1197,14 +1203,13 @@ (loop (list* 'typed_comprehension ex (cdr al)))) (else (error "unknown parse-cat result (internal error)"))))))) ((|.|) - (if (ts:space? s) (disallowed-space ex t)) + (disallow-space s ex t) (take-token s) (loop (cond ((eqv? (peek-token s) #\() (begin (if (ts:space? s) - (error (string "space before \"(\" not allowed in \"" - (deparse ex) ". (\""))) + (disallow-space s `(|.| ,ex (quote ||)) #\()) (take-token s) `(|.| ,ex (tuple ,@(parse-call-arglist s #\) ))))) ((eqv? (peek-token s) ':) @@ -1231,7 +1236,7 @@ (loop (list t ex))) ((|.'|) (error "the \".'\" operator is discontinued")) ((#\{ ) - (if (ts:space? s) (disallowed-space ex t)) + (disallow-space s ex t) (take-token s) (loop (list* 'curly ex (parse-call-arglist s #\} )))) ((#\" #\`) @@ -1600,7 +1605,7 @@ (let ((nxt (peek-token s))) (cond ((eq? nxt '|.|) - (if (ts:space? s) (disallowed-space word nxt)) + (disallow-space s (car path) nxt) (take-token s) (loop (cons (unquote (macrocall-to-atsym (parse-unary-prefix s))) path))) ((or (memv nxt '(#\newline #\; #\, :)) @@ -2342,8 +2347,7 @@ ((eqv? t #\@) (take-token s) (let ((nxt (peek-token s))) - (if (ts:space? s) - (disallowed-space '@ nxt))) + (disallow-space s '@ nxt)) (with-space-sensitive (let ((startloc (line-number-node s)) (head (if (eq? (peek-token s) '|.|) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index ac477a9966ac3..b0c7cba25c397 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -1116,8 +1116,7 @@ (cond ((eventually-call? (cadar binds)) ;; f() = c - (let ((asgn (butlast (expand-forms (car binds)))) - (name (assigned-name (cadar binds)))) + (let ((name (assigned-name (cadar binds)))) (if (not (symbol? name)) (error "invalid let syntax")) (loop (cdr binds) @@ -1126,7 +1125,7 @@ ,(if (expr-contains-eq name (caddar binds)) `(local ,name) ;; might need a Box for recursive functions `(local-def ,name)) - ,asgn + ,(car binds) ,blk))))) ((or (symbol? (cadar binds)) (decl? (cadar binds))) @@ -2096,7 +2095,7 @@ (tuple-wrap (cdr a) '()))) (tuple-wrap (cdr a) (cons x run)))))) (expand-forms - `(call (core _apply) ,f ,@(tuple-wrap argl '()))))) + `(call (core _apply_iterate) (top iterate) ,f ,@(tuple-wrap argl '()))))) ((and (eq? (identifier-name f) '^) (length= e 4) (integer? (cadddr e))) (expand-forms @@ -4012,6 +4011,11 @@ f(x) = yt(x) (if (and tail (not have-ret?)) (emit-return '(null))) '(null))) + + ;; unsupported assignment operators + ((≔ ⩴ ≕ :=) + (error (string "unsupported assignment operator \"" (deparse (car e)) "\""))) + ((error) (error (cadr e))) (else diff --git a/src/julia.h b/src/julia.h index 3ab36075d1299..c4e05766afa40 100644 --- a/src/julia.h +++ b/src/julia.h @@ -236,6 +236,13 @@ typedef struct _jl_llvm_functions_t { typedef struct _jl_method_instance_t jl_method_instance_t; +typedef struct _jl_line_info_node_t { + jl_value_t *method; + jl_sym_t *file; + intptr_t line; + intptr_t inlined_at; +} jl_line_info_node_t; + // This type describes a single function body typedef struct _jl_code_info_t { // ssavalue-indexed arrays of properties: @@ -418,14 +425,19 @@ typedef struct { typedef struct { uint32_t nfields; + uint32_t npointers; // number of pointer uint32_t alignment : 9; // strictest alignment over all fields uint32_t haspadding : 1; // has internal undefined bytes - uint32_t npointers : 20; // number of pointer fields, top 4 bits are exponent (under-approximation) uint32_t fielddesc_type : 2; // 0 -> 8, 1 -> 16, 2 -> 32 // union { - // jl_fielddesc8_t field8[]; - // jl_fielddesc16_t field16[]; - // jl_fielddesc32_t field32[]; + // jl_fielddesc8_t field8[nfields]; + // jl_fielddesc16_t field16[nfields]; + // jl_fielddesc32_t field32[nfields]; + // }; + // union { // offsets relative to data start in words + // uint8_t ptr8[npointers]; + // uint16_t ptr16[npointers]; + // uint32_t ptr32[npointers]; // }; } jl_datatype_layout_t; @@ -742,9 +754,6 @@ extern void JL_GC_POP() JL_NOTSAFEPOINT; JL_DLLEXPORT int jl_gc_enable(int on); JL_DLLEXPORT int jl_gc_is_enabled(void); -JL_DLLEXPORT int64_t jl_gc_total_bytes(void); -JL_DLLEXPORT uint64_t jl_gc_total_hrtime(void); -JL_DLLEXPORT int64_t jl_gc_diff_total_bytes(void); typedef enum { JL_GC_AUTO = 0, // use heuristics to determine the collection type @@ -948,7 +957,25 @@ STATIC_INLINE char *jl_symbol_name_(jl_sym_t *s) JL_NOTSAFEPOINT } #define jl_symbol_name(s) jl_symbol_name_(s) +static inline uint32_t jl_fielddesc_size(int8_t fielddesc_type) JL_NOTSAFEPOINT +{ + return 2 << fielddesc_type; + //if (fielddesc_type == 0) { + // return sizeof(jl_fielddesc8_t); + //} + //else if (fielddesc_type == 1) { + // return sizeof(jl_fielddesc16_t); + //} + //else { + // return sizeof(jl_fielddesc32_t); + //} +} + #define jl_dt_layout_fields(d) ((const char*)(d) + sizeof(jl_datatype_layout_t)) +static inline const char *jl_dt_layout_ptrs(const jl_datatype_layout_t *l) JL_NOTSAFEPOINT +{ + return jl_dt_layout_fields(l) + jl_fielddesc_size(l->fielddesc_type) * l->nfields; +} #define DEFINE_FIELD_ACCESSORS(f) \ static inline uint32_t jl_field_##f(jl_datatype_t *st, \ @@ -973,20 +1000,7 @@ static inline int jl_field_isptr(jl_datatype_t *st, int i) JL_NOTSAFEPOINT { const jl_datatype_layout_t *ly = st->layout; assert(i >= 0 && (size_t)i < ly->nfields); - return ((const jl_fielddesc8_t*)(jl_dt_layout_fields(ly) + (i << (ly->fielddesc_type + 1))))->isptr; -} - -static inline uint32_t jl_fielddesc_size(int8_t fielddesc_type) JL_NOTSAFEPOINT -{ - if (fielddesc_type == 0) { - return sizeof(jl_fielddesc8_t); - } - else if (fielddesc_type == 1) { - return sizeof(jl_fielddesc16_t); - } - else { - return sizeof(jl_fielddesc32_t); - } + return ((const jl_fielddesc8_t*)(jl_dt_layout_fields(ly) + jl_fielddesc_size(ly->fielddesc_type) * i))->isptr; } #undef DEFINE_FIELD_ACCESSORS @@ -1790,9 +1804,7 @@ extern int had_exception; #define JL_STDERR jl_uv_stderr #define JL_STDIN jl_uv_stdin -JL_DLLEXPORT void jl_run_event_loop(uv_loop_t *loop); -JL_DLLEXPORT int jl_run_once(uv_loop_t *loop); -JL_DLLEXPORT int jl_process_events(uv_loop_t *loop); +JL_DLLEXPORT int jl_process_events(void); JL_DLLEXPORT uv_loop_t *jl_global_event_loop(void); diff --git a/src/julia_internal.h b/src/julia_internal.h index d408257150e00..c97c70af90825 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -111,6 +111,9 @@ STATIC_INLINE uint32_t jl_int32hash_fast(uint32_t a) return a; // identity hashing seems to work well enough here } + +// -- gc.c -- // + #define GC_CLEAN 0 // freshly allocated #define GC_MARKED 1 // reachable and young #define GC_OLD 2 // if it is reachable it will be marked as old @@ -305,40 +308,12 @@ jl_svec_t *jl_perm_symsvec(size_t n, ...); }), __VA_ARGS__) #endif -// Returns a int32 where the high 16 bits are a lower bound of the number of non-pointer fields -// at the beginning of the type and the low 16 bits are a lower bound on the number of non-pointer -// fields at the end of the type. This field only exists for a layout that has at least one -// pointer fields. -#define jl_datatype_layout_n_nonptr(layout) ((uint32_t*)(layout))[-1] - jl_value_t *jl_gc_realloc_string(jl_value_t *s, size_t sz); JL_DLLEXPORT void *jl_gc_counted_malloc(size_t sz); JL_DLLEXPORT void JL_NORETURN jl_throw_out_of_memory_error(void); -jl_code_info_t *jl_type_infer(jl_method_instance_t *li, size_t world, int force); -jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *meth JL_PROPAGATES_ROOT, size_t world); -void jl_generate_fptr(jl_code_instance_t *codeinst); -jl_code_instance_t *jl_compile_linfo( - jl_method_instance_t *li JL_PROPAGATES_ROOT, - jl_code_info_t *src JL_MAYBE_UNROOTED, - size_t world, - const jl_cgparams_t *params); -JL_DLLEXPORT jl_code_instance_t *jl_get_method_inferred( - jl_method_instance_t *mi JL_PROPAGATES_ROOT, jl_value_t *rettype, - size_t min_world, size_t max_world); -jl_method_instance_t *jl_get_unspecialized(jl_method_instance_t *method JL_PROPAGATES_ROOT); - -JL_DLLEXPORT int jl_compile_hint(jl_tupletype_t *types); -jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *lam JL_PROPAGATES_ROOT); -int jl_code_requires_compiler(jl_code_info_t *src); -jl_code_info_t *jl_new_code_info_from_ast(jl_expr_t *ast); -JL_DLLEXPORT jl_code_info_t *jl_new_code_info_uninit(void); - -jl_value_t *jl_argtype_with_function(jl_function_t *f, jl_value_t *types); - -JL_DLLEXPORT jl_value_t *jl_apply_2va(jl_value_t *f, jl_value_t **args, uint32_t nargs); - +JL_DLLEXPORT int64_t jl_gc_diff_total_bytes(void); void jl_gc_sync_total_bytes(void); void jl_gc_track_malloced_array(jl_ptls_t ptls, jl_array_t *a) JL_NOTSAFEPOINT; void jl_gc_count_allocd(size_t sz) JL_NOTSAFEPOINT; @@ -375,6 +350,31 @@ void jl_set_t_uid_ctr(int i); uint32_t jl_get_gs_ctr(void); void jl_set_gs_ctr(uint32_t ctr); +// -- functions -- // + +jl_code_info_t *jl_type_infer(jl_method_instance_t *li, size_t world, int force); +jl_code_instance_t *jl_compile_method_internal(jl_method_instance_t *meth JL_PROPAGATES_ROOT, size_t world); +void jl_generate_fptr(jl_code_instance_t *codeinst); +jl_code_instance_t *jl_compile_linfo( + jl_method_instance_t *li JL_PROPAGATES_ROOT, + jl_code_info_t *src JL_MAYBE_UNROOTED, + size_t world, + const jl_cgparams_t *params); +JL_DLLEXPORT jl_code_instance_t *jl_get_method_inferred( + jl_method_instance_t *mi JL_PROPAGATES_ROOT, jl_value_t *rettype, + size_t min_world, size_t max_world); +jl_method_instance_t *jl_get_unspecialized(jl_method_instance_t *method JL_PROPAGATES_ROOT); + +JL_DLLEXPORT int jl_compile_hint(jl_tupletype_t *types); +jl_code_info_t *jl_code_for_interpreter(jl_method_instance_t *lam JL_PROPAGATES_ROOT); +int jl_code_requires_compiler(jl_code_info_t *src); +jl_code_info_t *jl_new_code_info_from_ast(jl_expr_t *ast); +JL_DLLEXPORT jl_code_info_t *jl_new_code_info_uninit(void); + +jl_value_t *jl_argtype_with_function(jl_function_t *f, jl_value_t *types); + +JL_DLLEXPORT jl_value_t *jl_apply_2va(jl_value_t *f, jl_value_t **args, uint32_t nargs); + void JL_NORETURN jl_method_error(jl_function_t *f, jl_value_t **args, size_t na, size_t world); jl_value_t *jl_get_exceptionf(jl_datatype_t *exception_type, const char *fmt, ...); @@ -481,6 +481,8 @@ extern char jl_using_perf_jitevents; #endif extern size_t jl_arr_xtralloc_limit; +// -- init.c -- // + void jl_init_types(void) JL_GC_DISABLED; void jl_init_box_caches(void); void jl_init_frontend(void); @@ -590,7 +592,109 @@ jl_tupletype_t *arg_type_tuple(jl_value_t *arg1, jl_value_t **args, size_t nargs int jl_has_meta(jl_array_t *body, jl_sym_t *sym); -// backtraces +//-------------------------------------------------- +// Backtraces + +// Backtrace buffers: +// +// A backtrace buffer conceptually contains a stack of instruction pointers +// ordered from the inner-most frame to the outermost. We store them in a +// special raw format for two reasons: +// +// * Efficiency: Every `throw()` must populate the trace so it must be as +// efficient as possible. +// * Signal safety: For signal-based exceptions such as StackOverflowError +// the trace buffer needs to be filled from a signal handler where most +// operations are not allowed (including malloc) so we choose a flat +// preallocated buffer. +// +// The raw buffer layout contains "frame entries" composed of one or several +// values of type `jl_bt_element_t`. From the point of view of the GC, an entry +// is either: +// +// 1. A single instruction pointer to native code, not GC-managed. +// 2. An "extended entry": a mixture of raw data and pointers to julia objects +// which must be treated as GC roots. +// +// A single extended entry is seralized using multiple elements from the raw +// buffer; if `e` is the pointer to the first slot we have: +// +// e[0] JL_BT_NON_PTR_ENTRY - Special marker to distinguish extended entries +// e[1] descriptor - A bit packed uintptr_t containing a tag and +// the number of GC- managed and non-managed values +// e[2+j] - GC managed data +// e[2+ngc+i] - Non-GC-managed data +// +// The format of `descriptor` is, from LSB to MSB: +// 0:2 ngc Number of GC-managed pointers for this frame entry +// 3:5 nptr Number of non-GC-managed buffer elements +// 6:9 tag Entry type +// 10:... header Entry-specific header data +typedef struct _jl_bt_element_t { + union { + uintptr_t uintptr; // Metadata or native instruction ptr + jl_value_t* jlvalue; // Pointer to GC-managed value + }; +} jl_bt_element_t; + +#define JL_BT_NON_PTR_ENTRY (((uintptr_t)0)-1) +// Maximum size for an extended backtrace entry (likely significantly larger +// than the actual size of 3-4 for an interpreter frame) +#define JL_BT_MAX_ENTRY_SIZE 16 + +STATIC_INLINE int jl_bt_is_native(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT +{ + return bt_entry[0].uintptr != JL_BT_NON_PTR_ENTRY; +} + +// Extended backtrace entry header packing; the bit packing is done manually +// for precise layout control for interop with julia side. +STATIC_INLINE uintptr_t jl_bt_entry_descriptor(int ngc, int nptr, + int tag, uintptr_t header) JL_NOTSAFEPOINT +{ + assert(((ngc & 0x7) == ngc) && ((nptr & 0x7) == nptr) && ((tag & 0xf) == tag)); + return (ngc & 0x7) | (nptr & 0x7) << 3 | (tag & 0xf) << 6 | header << 10; +} + +// Unpacking of extended backtrace entry data +STATIC_INLINE size_t jl_bt_num_jlvals(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT +{ + assert(!jl_bt_is_native(bt_entry)); + return bt_entry[1].uintptr & 0x7; +} +STATIC_INLINE size_t jl_bt_num_uintvals(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT +{ + assert(!jl_bt_is_native(bt_entry)); + return (bt_entry[1].uintptr >> 3) & 0x7; +} +STATIC_INLINE int jl_bt_entry_tag(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT +{ + assert(!jl_bt_is_native(bt_entry)); + return (bt_entry[1].uintptr >> 6) & 0xf; +} +STATIC_INLINE uintptr_t jl_bt_entry_header(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT +{ + assert(!jl_bt_is_native(bt_entry)); + return bt_entry[1].uintptr >> 10; +} + +// Return `i`th GC-managed pointer for extended backtrace entry +// The returned value is rooted for the lifetime of the parent exception stack. +STATIC_INLINE jl_value_t *jl_bt_entry_jlvalue(jl_bt_element_t *bt_entry, size_t i) JL_NOTSAFEPOINT +{ + return bt_entry[2 + i].jlvalue; +} + +#define JL_BT_INTERP_FRAME_TAG 1 // An interpreter frame + +// Number of bt elements in frame. +STATIC_INLINE size_t jl_bt_entry_size(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT +{ + return jl_bt_is_native(bt_entry) ? + 1 : 2 + jl_bt_num_jlvals(bt_entry) + jl_bt_num_uintvals(bt_entry); +} + +// Function metadata arising from debug info lookup of instruction pointer typedef struct { char *func_name; char *file_name; @@ -634,23 +738,21 @@ typedef unw_cursor_t bt_cursor_t; typedef int bt_context_t; typedef int bt_cursor_t; #endif -// Special marker in backtrace data for encoding interpreter frames -#define JL_BT_INTERP_FRAME (((uintptr_t)0)-1) -// Maximum number of elements of bt_data taken up by interpreter frame -#define JL_BT_MAX_ENTRY_SIZE 3 -size_t rec_backtrace(uintptr_t *bt_data, size_t maxsize, int skip) JL_NOTSAFEPOINT; +size_t rec_backtrace(jl_bt_element_t *bt_data, size_t maxsize, int skip) JL_NOTSAFEPOINT; // Record backtrace from a signal handler. `ctx` is the context of the code // which was asynchronously interrupted. -size_t rec_backtrace_ctx(uintptr_t *bt_data, size_t maxsize, bt_context_t *ctx, +size_t rec_backtrace_ctx(jl_bt_element_t *bt_data, size_t maxsize, bt_context_t *ctx, int add_interp_frames) JL_NOTSAFEPOINT; #ifdef LIBOSXUNWIND -size_t rec_backtrace_ctx_dwarf(uintptr_t *bt_data, size_t maxsize, bt_context_t *ctx, int add_interp_frames) JL_NOTSAFEPOINT; +size_t rec_backtrace_ctx_dwarf(jl_bt_element_t *bt_data, size_t maxsize, bt_context_t *ctx, int add_interp_frames) JL_NOTSAFEPOINT; #endif -JL_DLLEXPORT void jl_get_backtrace(jl_array_t **bt, jl_array_t **bt2); -void jl_critical_error(int sig, bt_context_t *context, uintptr_t *bt_data, size_t *bt_size); +JL_DLLEXPORT jl_value_t *jl_get_backtrace(void); +void jl_critical_error(int sig, bt_context_t *context, jl_bt_element_t *bt_data, size_t *bt_size); JL_DLLEXPORT void jl_raise_debugger(void); int jl_getFunctionInfo(jl_frame_t **frames, uintptr_t pointer, int skipC, int noInline) JL_NOTSAFEPOINT; -JL_DLLEXPORT void jl_gdblookup(uintptr_t ip) JL_NOTSAFEPOINT; +JL_DLLEXPORT void jl_gdblookup(void* ip) JL_NOTSAFEPOINT; +void jl_print_native_codeloc(uintptr_t ip) JL_NOTSAFEPOINT; +void jl_print_bt_entry_codeloc(jl_bt_element_t *bt_data) JL_NOTSAFEPOINT; // *to is NULL or malloc'd pointer, from is allowed to be NULL STATIC_INLINE char *jl_copy_str(char **to, const char *from) { @@ -660,13 +762,14 @@ STATIC_INLINE char *jl_copy_str(char **to, const char *from) return NULL; } size_t len = strlen(from) + 1; - *to = (char*)realloc(*to, len); + *to = (char*)realloc_s(*to, len); memcpy(*to, from, len); return *to; } JL_DLLEXPORT int jl_is_interpreter_frame(uintptr_t ip) JL_NOTSAFEPOINT; JL_DLLEXPORT int jl_is_enter_interpreter_frame(uintptr_t ip) JL_NOTSAFEPOINT; -JL_DLLEXPORT size_t jl_capture_interp_frame(uintptr_t *data, uintptr_t sp, uintptr_t fp, size_t space_remaining) JL_NOTSAFEPOINT; +JL_DLLEXPORT size_t jl_capture_interp_frame(jl_bt_element_t *bt_data, uintptr_t sp, + uintptr_t fp, size_t space_remaining) JL_NOTSAFEPOINT; // Exception stack: a stack of pairs of (exception,raw_backtrace). // The stack may be traversed and accessed with the functions below. @@ -676,25 +779,25 @@ typedef struct _jl_excstack_t { // Pack all stack entries into a growable buffer to amortize allocation // across repeated exception handling. // Layout: [bt_data1... bt_size1 exc1 bt_data2... bt_size2 exc2 ..] - // uintptr_t data[]; // Access with jl_excstack_raw + // jl_bt_element_t data[]; // Access with jl_excstack_raw } jl_excstack_t; -STATIC_INLINE uintptr_t *jl_excstack_raw(jl_excstack_t *stack) JL_NOTSAFEPOINT +STATIC_INLINE jl_bt_element_t *jl_excstack_raw(jl_excstack_t *stack) JL_NOTSAFEPOINT { - return (uintptr_t*)(stack + 1); + return (jl_bt_element_t*)(stack + 1); } // Exception stack access STATIC_INLINE jl_value_t *jl_excstack_exception(jl_excstack_t *stack JL_PROPAGATES_ROOT, size_t itr) JL_NOTSAFEPOINT { - return (jl_value_t*)(jl_excstack_raw(stack)[itr-1]); + return jl_excstack_raw(stack)[itr-1].jlvalue; } STATIC_INLINE size_t jl_excstack_bt_size(jl_excstack_t *stack, size_t itr) JL_NOTSAFEPOINT { - return jl_excstack_raw(stack)[itr-2]; + return jl_excstack_raw(stack)[itr-2].uintptr; } -STATIC_INLINE uintptr_t *jl_excstack_bt_data(jl_excstack_t *stack, size_t itr) JL_NOTSAFEPOINT +STATIC_INLINE jl_bt_element_t *jl_excstack_bt_data(jl_excstack_t *stack, size_t itr) JL_NOTSAFEPOINT { return jl_excstack_raw(stack) + itr-2 - jl_excstack_bt_size(stack, itr); } @@ -708,9 +811,10 @@ void jl_reserve_excstack(jl_excstack_t **stack JL_REQUIRE_ROOTED_SLOT, size_t reserved_size); void jl_push_excstack(jl_excstack_t **stack JL_REQUIRE_ROOTED_SLOT JL_ROOTING_ARGUMENT, jl_value_t *exception JL_ROOTED_ARGUMENT, - uintptr_t *bt_data, size_t bt_size); + jl_bt_element_t *bt_data, size_t bt_size); void jl_copy_excstack(jl_excstack_t *dest, jl_excstack_t *src) JL_NOTSAFEPOINT; +//-------------------------------------------------- // timers // Returns time in nanosec JL_DLLEXPORT uint64_t jl_hrtime(void); @@ -849,6 +953,7 @@ JL_DLLEXPORT jl_value_t *jl_floor_llvm(jl_value_t *a); JL_DLLEXPORT jl_value_t *jl_trunc_llvm(jl_value_t *a); JL_DLLEXPORT jl_value_t *jl_rint_llvm(jl_value_t *a); JL_DLLEXPORT jl_value_t *jl_sqrt_llvm(jl_value_t *a); +JL_DLLEXPORT jl_value_t *jl_sqrt_llvm_fast(jl_value_t *a); JL_DLLEXPORT jl_value_t *jl_abs_float(jl_value_t *a); JL_DLLEXPORT jl_value_t *jl_copysign_float(jl_value_t *a, jl_value_t *b); JL_DLLEXPORT jl_value_t *jl_flipsign_int(jl_value_t *a, jl_value_t *b); @@ -866,59 +971,10 @@ extern jl_mutex_t typecache_lock; extern jl_mutex_t codegen_lock; extern jl_mutex_t safepoint_lock; -// -- gc.c -- // - #if defined(__APPLE__) void jl_mach_gc_end(void); #endif -#if defined(_OS_WINDOWS_) -STATIC_INLINE void *jl_malloc_aligned(size_t sz, size_t align) -{ - return _aligned_malloc(sz ? sz : 1, align); -} -STATIC_INLINE void *jl_realloc_aligned(void *p, size_t sz, size_t oldsz, - size_t align) -{ - (void)oldsz; - return _aligned_realloc(p, sz ? sz : 1, align); -} -STATIC_INLINE void jl_free_aligned(void *p) JL_NOTSAFEPOINT -{ - _aligned_free(p); -} -#else -STATIC_INLINE void *jl_malloc_aligned(size_t sz, size_t align) -{ -#if defined(_P64) || defined(__APPLE__) - if (align <= 16) - return malloc(sz); -#endif - void *ptr; - if (posix_memalign(&ptr, align, sz)) - return NULL; - return ptr; -} -STATIC_INLINE void *jl_realloc_aligned(void *d, size_t sz, size_t oldsz, - size_t align) -{ -#if defined(_P64) || defined(__APPLE__) - if (align <= 16) - return realloc(d, sz); -#endif - void *b = jl_malloc_aligned(sz, align); - if (b != NULL) { - memcpy(b, d, oldsz > sz ? sz : oldsz); - free(d); - } - return b; -} -STATIC_INLINE void jl_free_aligned(void *p) JL_NOTSAFEPOINT -{ - free(p); -} -#endif - // -- typemap.c -- // // a descriptor of a jl_typemap_t that gets diff --git a/src/julia_threads.h b/src/julia_threads.h index 7dea19af4f7c6..7d47bd1ec716c 100644 --- a/src/julia_threads.h +++ b/src/julia_threads.h @@ -156,6 +156,7 @@ typedef struct { } jl_gc_mark_cache_t; typedef struct _jl_excstack_t jl_excstack_t; +struct _jl_bt_element_t; // This includes all the thread local states we care about for a thread. // Changes to TLS field types must be reflected in codegen. #define JL_MAX_BT_SIZE 80000 @@ -193,7 +194,7 @@ struct _jl_tls_states_t { // Temp storage for exception thrown in signal handler. Not rooted. struct _jl_value_t *sig_exception; // Temporary backtrace buffer. Scanned for gc roots when bt_size > 0. - uintptr_t *bt_data; // JL_MAX_BT_SIZE + 1 elements long + struct _jl_bt_element_t *bt_data; // JL_MAX_BT_SIZE + 1 elements long size_t bt_size; // Size for backtrace in transit in bt_data // Atomically set by the sender, reset by the handler. volatile sig_atomic_t signal_request; diff --git a/src/llvm-final-gc-lowering.cpp b/src/llvm-final-gc-lowering.cpp index a68b2fc193ed8..70fae580826c5 100644 --- a/src/llvm-final-gc-lowering.cpp +++ b/src/llvm-final-gc-lowering.cpp @@ -76,6 +76,7 @@ Value *FinalLowerGC::lowerNewGCFrame(CallInst *target, Function &F) T_prjlvalue, 0, ConstantInt::get(T_int32, nRoots + 2)); + gcframe->setAlignment(16); gcframe->insertAfter(target); gcframe->takeName(target); @@ -88,17 +89,20 @@ Value *FinalLowerGC::lowerNewGCFrame(CallInst *target, Function &F) Value *args[4] = { tempSlot_i8, // dest ConstantInt::get(Type::getInt8Ty(F.getContext()), 0), // val - ConstantInt::get(T_int32, sizeof(jl_value_t*)*(nRoots+2)), // len + ConstantInt::get(T_int32, sizeof(jl_value_t*) * (nRoots + 2)), // len ConstantInt::get(Type::getInt1Ty(F.getContext()), 0)}; // volatile #else Value *args[5] = { tempSlot_i8, // dest ConstantInt::get(Type::getInt8Ty(F.getContext()), 0), // val - ConstantInt::get(T_int32, sizeof(jl_value_t*)*(nRoots+2)), // len - ConstantInt::get(T_int32, 0), // align + ConstantInt::get(T_int32, sizeof(jl_value_t*) * (nRoots + 2)), // len + ConstantInt::get(T_int32, 16), // align ConstantInt::get(Type::getInt1Ty(F.getContext()), 0)}; // volatile #endif CallInst *zeroing = CallInst::Create(memset, makeArrayRef(args)); +#if JL_LLVM_VERSION >= 70000 + cast(zeroing)->setDestAlignment(16); +#endif zeroing->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe); zeroing->insertAfter(tempSlot_i8); diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index 09862e12d52e1..eeb0e714e8567 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -250,9 +250,7 @@ struct State { // Cache of numbers assigned to IR values. This includes caching of numbers // for derived values std::map AllPtrNumbering; - std::map> AllVectorNumbering; - // Numbering of pointers. This only includes Defs - std::map PtrNumbering; + std::map> AllCompositeNumbering; // The reverse of the previous maps std::map ReversePtrNumbering; // Neighbors in the coloring interference graph. I.e. for each value, the @@ -326,19 +324,24 @@ struct LateLowerGCFrame: public FunctionPass, private JuliaPassContext { void NoteUse(State &S, BBState &BBS, Value *V) { NoteUse(S, BBS, V, BBS.UpExposedUses); } - Value *MaybeExtractUnion(std::pair Val, Instruction *InsertBefore); - void LiftPhi(State &S, PHINode *Phi, SmallVector &PHINumbers); + + void LiftPhi(State &S, PHINode *Phi); bool LiftSelect(State &S, SelectInst *SI); + Value *MaybeExtractScalar(State &S, std::pair ValExpr, Instruction *InsertBefore); + std::vector MaybeExtractVector(State &S, Value *BaseVec, Instruction *InsertBefore); + Value *GetPtrForNumber(State &S, unsigned Num, Instruction *InsertBefore); + int Number(State &S, Value *V); - std::vector NumberVector(State &S, Value *Vec); - int NumberBase(State &S, Value *V, Value *Base); - std::vector NumberVectorBase(State &S, Value *Base); + int NumberBase(State &S, Value *Base); + std::vector NumberAll(State &S, Value *V); + std::vector NumberAllBase(State &S, Value *Base); + void NoteOperandUses(State &S, BBState &BBS, User &UI); State LocalScan(Function &F); void ComputeLiveness(State &S); void ComputeLiveSets(State &S); std::vector ColorRoots(const State &S); - void PlaceGCFrameStore(State &S, unsigned R, unsigned MinColorRoot, const std::vector &Colors, Value *GCFrame, Instruction *InsertionPoint); + void PlaceGCFrameStore(State &S, unsigned R, unsigned MinColorRoot, const std::vector &Colors, Value *GCFrame, Instruction *InsertBefore); void PlaceGCFrameStores(State &S, unsigned MinColorRoot, const std::vector &Colors, Value *GCFrame); void PlaceRootsAndUpdateCalls(std::vector &Colors, State &S, std::map>); bool doInitialization(Module &M) override; @@ -353,10 +356,12 @@ struct LateLowerGCFrame: public FunctionPass, private JuliaPassContext { }; static unsigned getValueAddrSpace(Value *V) { - Type *Ty = V->getType(); - if (isa(Ty)) - Ty = cast(V->getType())->getElementType(); - return cast(Ty)->getAddressSpace(); + return V->getType()->getPointerAddressSpace(); +} + +static unsigned isTrackedValue(Value *V) { + PointerType *PT = dyn_cast(V->getType()->getScalarType()); + return PT && PT->getAddressSpace() == AddressSpace::Tracked; } static bool isSpecialPtr(Type *Ty) { @@ -367,19 +372,61 @@ static bool isSpecialPtr(Type *Ty) { return AddressSpace::FirstSpecial <= AS && AS <= AddressSpace::LastSpecial; } -static bool isSpecialPtrVec(Type *Ty) { - auto *VTy = dyn_cast(Ty); - if (!VTy) - return false; - return isSpecialPtr(VTy->getElementType()); +// return how many Special pointers are in T (count > 0), +// and if there is anything else in T (all == false) +CountTrackedPointers::CountTrackedPointers(Type *T) { + if (isa(T)) { + if (isSpecialPtr(T)) { + count++; + if (T->getPointerAddressSpace() != AddressSpace::Tracked) + derived = true; + } + } else if (isa(T)) { + for (Type *ElT : T->subtypes()) { + auto sub = CountTrackedPointers(ElT); + count += sub.count; + all &= sub.all; + derived |= sub.derived; + } + if (isa(T)) + count *= cast(T)->getNumElements(); + } + if (count == 0) + all = false; +} + +unsigned getCompositeNumElements(Type *T) { + return isa(T) ? T->getStructNumElements() : cast(T)->getNumElements(); +} + +// Walk through a Type, and record the element path to every tracked value inside +void TrackCompositeType(Type *T, std::vector &Idxs, std::vector> &Numberings) { + if (isa(T)) { + if (T->getPointerAddressSpace() == AddressSpace::Tracked) + Numberings.push_back(Idxs); + } + else if (isa(T)) { + unsigned Idx, NumEl = getCompositeNumElements(T); + for (Idx = 0; Idx < NumEl; Idx++) { + Idxs.push_back(Idx); + Type *ElT = cast(T)->getTypeAtIndex(Idx); + TrackCompositeType(ElT, Idxs, Numberings); + Idxs.pop_back(); + } + } } -static bool isUnionRep(Type *Ty) { - return Ty->isStructTy() && cast(Ty)->getNumElements() == 2 && - isSpecialPtr(cast(Ty)->getTypeAtIndex((unsigned)0)); +std::vector> TrackCompositeType(Type *T) { + std::vector Idxs; + std::vector> Numberings; + TrackCompositeType(T, Idxs, Numberings); + return Numberings; } -// If the input value is a scalar (pointer), we may return a vector value as base + + +// Walk through simple expressions to until we hit something that requires root numbering +// If the input value is a scalar (pointer), we may return a composite value as base // in which case the second member of the pair is the index of the value in the vector. static std::pair FindBaseValue(const State &S, Value *V, bool UseCache = true) { Value *CurrentV = V; @@ -391,8 +438,8 @@ static std::pair FindBaseValue(const State &S, Value *V, bool UseCac if (it != S.AllPtrNumbering.end()) return std::make_pair(CurrentV, fld_idx); } else { - auto it = S.AllVectorNumbering.find(CurrentV); - if (it != S.AllVectorNumbering.end()) + auto it = S.AllCompositeNumbering.find(CurrentV); + if (it != S.AllCompositeNumbering.end()) return std::make_pair(CurrentV, fld_idx); } } @@ -405,37 +452,16 @@ static std::pair FindBaseValue(const State &S, Value *V, bool UseCac if (getValueAddrSpace(NewV) == 0) break; CurrentV = NewV; - } - else if (isa(CurrentV)) { - CurrentV = cast(CurrentV)->getOperand(0); + } else if (auto *GEP = dyn_cast(CurrentV)) { + CurrentV = GEP->getOperand(0); // GEP can make vectors from a single base pointer - if (fld_idx != -1 && !isSpecialPtrVec(CurrentV->getType())) { + if (fld_idx != -1 && !isa(CurrentV->getType())) { fld_idx = -1; } - } else if (isa(CurrentV)) { - Value *Operand = cast(CurrentV)->getOperand(0); - if (!isUnionRep(Operand->getType())) - break; - CurrentV = Operand; - } - else if (isa(CurrentV)) { - if (!isUnionRep(CurrentV->getType())) - break; - InsertValueInst *IVI = cast(CurrentV); - assert(IVI->getNumIndices() == 1); - unsigned idx = IVI->getIndices()[0]; - if (idx == 0) { - // Updating the pointer in the union. Follow the pointer. - CurrentV = IVI->getOperand(1); - } else { - // Updating which tindex is active. Follow the union. - assert(idx == 1); - CurrentV = IVI->getOperand(0); - } } else if (auto EEI = dyn_cast(CurrentV)) { assert(CurrentV->getType()->isPointerTy() && fld_idx == -1); - // For now, only support constant index. + // TODO: For now, only support constant index. auto IdxOp = cast(EEI->getIndexOperand()); fld_idx = IdxOp->getLimitedValue(INT_MAX); CurrentV = EEI->getVectorOperand(); @@ -465,255 +491,414 @@ static std::pair FindBaseValue(const State &S, Value *V, bool UseCac isa(CurrentV) || isa(CurrentV) || isa(CurrentV) || isa(CurrentV) || isa(CurrentV) || isa(CurrentV) || + isa(CurrentV) || isa(CurrentV) || isa(CurrentV) || isa(CurrentV)); return std::make_pair(CurrentV, fld_idx); } -Value *LateLowerGCFrame::MaybeExtractUnion(std::pair Val, Instruction *InsertBefore) { - if (isUnionRep(Val.first->getType())) { - assert(Val.second == -1); - return ExtractValueInst::Create(Val.first, {(unsigned)0}, "", InsertBefore); +Value *LateLowerGCFrame::MaybeExtractScalar(State &S, std::pair ValExpr, Instruction *InsertBefore) { + Value *V = ValExpr.first; + if (isa(V->getType())) { + assert(ValExpr.second == -1); + if (!isTrackedValue(V)) { + int BaseNumber = NumberBase(S, V); + if (BaseNumber >= 0) + V = GetPtrForNumber(S, BaseNumber, InsertBefore); + else + V = ConstantPointerNull::get(cast(T_prjlvalue)); + } + } + else if (ValExpr.second != -1) { + auto Tracked = TrackCompositeType(V->getType()); + auto Idxs = makeArrayRef(Tracked.at(ValExpr.second)); + auto IdxsNotVec = Idxs.slice(0, Idxs.size() - 1); + Type *FinalT = ExtractValueInst::getIndexedType(V->getType(), IdxsNotVec); + bool IsVector = isa(FinalT); + PointerType *T = cast(cast(FinalT)->getTypeAtIndex(Idxs.back())); + if (T->getAddressSpace() != AddressSpace::Tracked) { + // if V isn't tracked, get the shadow def + auto Numbers = NumberAllBase(S, V); + int BaseNumber = Numbers.at(ValExpr.second); + if (BaseNumber >= 0) + V = GetPtrForNumber(S, BaseNumber, InsertBefore); + else + V = ConstantPointerNull::get(cast(T_prjlvalue)); + return V; + } + if (Idxs.size() > IsVector) + V = ExtractValueInst::Create(V, IsVector ? IdxsNotVec : Idxs, "", InsertBefore); + if (IsVector) + V = ExtractElementInst::Create(V, + ConstantInt::get(Type::getInt32Ty(V->getContext()), Idxs.back()), + "", InsertBefore); } - else if (Val.second != -1) { - return ExtractElementInst::Create(Val.first, ConstantInt::get(T_int32, Val.second), - "", InsertBefore); + return V; +} + +std::vector LateLowerGCFrame::MaybeExtractVector(State &S, Value *BaseVec, Instruction *InsertBefore) { + auto Numbers = NumberAllBase(S, BaseVec); + std::vector V{Numbers.size()}; + Value *V_null = ConstantPointerNull::get(cast(T_prjlvalue)); + for (unsigned i = 0; i < V.size(); ++i) { + if (Numbers[i] >= 0) + V[i] = GetPtrForNumber(S, Numbers[i], InsertBefore); + else + V[i] = V_null; } - return Val.first; + return V; } -static Value *GetPtrForNumber(State &S, unsigned Num, Instruction *InsertionPoint) +Value *LateLowerGCFrame::GetPtrForNumber(State &S, unsigned Num, Instruction *InsertBefore) { Value *Val = S.ReversePtrNumbering[Num]; - if (isSpecialPtrVec(Val->getType())) { - const std::vector &AllNums = S.AllVectorNumbering[Val]; - unsigned Idx = 0; - for (; Idx < AllNums.size(); ++Idx) { + unsigned Idx = -1; + if (!isa(Val->getType())) { + const std::vector &AllNums = S.AllCompositeNumbering[Val]; + for (Idx = 0; Idx < AllNums.size(); ++Idx) { if ((unsigned)AllNums[Idx] == Num) break; } - Val = ExtractElementInst::Create(Val, ConstantInt::get( - Type::getInt32Ty(Val->getContext()), Idx), "", InsertionPoint); + assert(Idx < AllNums.size()); } - return Val; + return MaybeExtractScalar(S, std::make_pair(Val, Idx), InsertBefore); } bool LateLowerGCFrame::LiftSelect(State &S, SelectInst *SI) { - if (isSpecialPtrVec(SI->getType())) { - VectorType *VT = cast(SI->getType()); - std::vector TrueNumbers = NumberVector(S, SI->getTrueValue()); - std::vector FalseNumbers = NumberVector(S, SI->getFalseValue()); - std::vector Numbers; - for (unsigned i = 0; i < VT->getNumElements(); ++i) { - SelectInst *LSI = SelectInst::Create(SI->getCondition(), - TrueNumbers[i] < 0 ? - ConstantPointerNull::get(cast(T_prjlvalue)) : - GetPtrForNumber(S, TrueNumbers[i], SI), - FalseNumbers[i] < 0 ? - ConstantPointerNull::get(cast(T_prjlvalue)) : - GetPtrForNumber(S, FalseNumbers[i], SI), - "gclift", SI); - int Number = ++S.MaxPtrNumber; - Numbers.push_back(Number); - S.PtrNumbering[LSI] = S.AllPtrNumbering[LSI] = Number; - S.ReversePtrNumbering[Number] = LSI; - } - S.AllVectorNumbering[SI] = Numbers; - } else { - Value *TrueBase = MaybeExtractUnion(FindBaseValue(S, SI->getTrueValue(), false), SI); - Value *FalseBase = MaybeExtractUnion(FindBaseValue(S, SI->getFalseValue(), false), SI); - if (getValueAddrSpace(TrueBase) != AddressSpace::Tracked) - TrueBase = ConstantPointerNull::get(cast(FalseBase->getType())); - if (getValueAddrSpace(FalseBase) != AddressSpace::Tracked) - FalseBase = ConstantPointerNull::get(cast(TrueBase->getType())); - if (getValueAddrSpace(TrueBase) != AddressSpace::Tracked) - return false; - Value *SelectBase = SelectInst::Create(SI->getCondition(), - TrueBase, FalseBase, "gclift", SI); - int Number = ++S.MaxPtrNumber; - S.PtrNumbering[SelectBase] = S.AllPtrNumbering[SelectBase] = - S.AllPtrNumbering[SI] = Number; - S.ReversePtrNumbering[Number] = SelectBase; + if (isa(SI->getType()) ? + S.AllPtrNumbering.count(SI) : + S.AllCompositeNumbering.count(SI)) { + // already visited here--nothing to do + return true; } - return true; -} - -void LateLowerGCFrame::LiftPhi(State &S, PHINode *Phi, SmallVector &PHINumbers) -{ - if (isSpecialPtrVec(Phi->getType())) { - VectorType *VT = cast(Phi->getType()); - std::vector lifted; - for (unsigned i = 0; i < VT->getNumElements(); ++i) { - lifted.push_back(PHINode::Create(T_prjlvalue, Phi->getNumIncomingValues(), "gclift", Phi)); + std::vector Numbers; + unsigned NumRoots = 1; + if (isa(SI->getType())) + Numbers.resize(SI->getType()->getVectorNumElements(), -1); + else + assert(isa(SI->getType()) && "unimplemented"); + assert(!isTrackedValue(SI)); + // find the base root for the arguments + Value *TrueBase = MaybeExtractScalar(S, FindBaseValue(S, SI->getTrueValue(), false), SI); + Value *FalseBase = MaybeExtractScalar(S, FindBaseValue(S, SI->getFalseValue(), false), SI); + Value *V_null = ConstantPointerNull::get(cast(T_prjlvalue)); + bool didsplit = false; + if (TrueBase != V_null && FalseBase != V_null) { + std::vector TrueBases; + std::vector FalseBases; + if (!isa(TrueBase->getType())) { + TrueBases = MaybeExtractVector(S, TrueBase, SI); + assert(TrueBases.size() == Numbers.size()); + NumRoots = TrueBases.size(); + } + if (!isa(FalseBase->getType())) { + FalseBases = MaybeExtractVector(S, FalseBase, SI); + assert(FalseBases.size() == Numbers.size()); + NumRoots = FalseBases.size(); } - for (unsigned i = 0; i < Phi->getNumIncomingValues(); ++i) { - std::vector Numbers = NumberVector(S, Phi->getIncomingValue(i)); - BasicBlock *IncomingBB = Phi->getIncomingBlock(i); - Instruction *Terminator = IncomingBB->getTerminator(); - for (unsigned i = 0; i < VT->getNumElements(); ++i) { - if (Numbers[i] < 0) - lifted[i]->addIncoming(ConstantPointerNull::get(cast(T_prjlvalue)), IncomingBB); + if (isa(SI->getType()) ? + S.AllPtrNumbering.count(SI) : + S.AllCompositeNumbering.count(SI)) { + // MaybeExtractScalar or MaybeExtractVector handled this for us (recursively, though a PHINode) + return true; + } + // need to handle each element (may just be one scalar) + for (unsigned i = 0; i < NumRoots; ++i) { + Value *TrueElem; + if (isa(TrueBase->getType())) + TrueElem = TrueBase; + else + TrueElem = TrueBases[i]; + Value *FalseElem; + if (isa(FalseBase->getType())) + FalseElem = FalseBase; + else + FalseElem = FalseBases[i]; + if (TrueElem != V_null || FalseElem != V_null) { + Value *Cond = SI->getCondition(); + if (isa(Cond->getType())) { + Cond = ExtractElementInst::Create(Cond, + ConstantInt::get(Type::getInt32Ty(Cond->getContext()), i), + "", SI); + } + SelectInst *SelectBase = SelectInst::Create(Cond, TrueElem, FalseElem, "gclift", SI); + int Number = ++S.MaxPtrNumber; + S.AllPtrNumbering[SelectBase] = Number; + S.ReversePtrNumbering[Number] = SelectBase; + if (isa(SI->getType())) + S.AllPtrNumbering[SI] = Number; else - lifted[i]->addIncoming(GetPtrForNumber(S, Numbers[i], Terminator), IncomingBB); + Numbers[i] = Number; + didsplit = true; } } - std::vector Numbers; - for (unsigned i = 0; i < VT->getNumElements(); ++i) { - int Number = ++S.MaxPtrNumber; - PHINumbers.push_back(Number); - Numbers.push_back(Number); - S.PtrNumbering[lifted[i]] = S.AllPtrNumbering[lifted[i]] = Number; - S.ReversePtrNumbering[Number] = lifted[i]; + if (isa(SI->getType()) && NumRoots != Numbers.size()) { + // broadcast the scalar root number to fill the vector + assert(NumRoots == 1); + int Number = Numbers[0]; + Numbers.resize(0); + Numbers.resize(SI->getType()->getVectorNumElements(), Number); } - S.AllVectorNumbering[Phi] = Numbers; - } else { + } + if (!isa(SI->getType())) + S.AllCompositeNumbering[SI] = Numbers; + return didsplit; +} + +void LateLowerGCFrame::LiftPhi(State &S, PHINode *Phi) { + if (isa(Phi->getType()) ? + S.AllPtrNumbering.count(Phi) : + S.AllCompositeNumbering.count(Phi)) + return; + // need to handle each element (may just be one scalar) + SmallVector lifted; + std::vector Numbers; + unsigned NumRoots = 1; + if (isa(Phi->getType())) { + NumRoots = Phi->getType()->getVectorNumElements(); + Numbers.resize(NumRoots); + } + else { + assert(isa(Phi->getType()) && "unimplemented"); + } + for (unsigned i = 0; i < NumRoots; ++i) { PHINode *lift = PHINode::Create(T_prjlvalue, Phi->getNumIncomingValues(), "gclift", Phi); - for (unsigned i = 0; i < Phi->getNumIncomingValues(); ++i) { - Value *Incoming = Phi->getIncomingValue(i); - Value *Base = MaybeExtractUnion(FindBaseValue(S, Incoming, false), - Phi->getIncomingBlock(i)->getTerminator()); - if (getValueAddrSpace(Base) != AddressSpace::Tracked) - Base = ConstantPointerNull::get(cast(T_prjlvalue)); - if (Base->getType() != T_prjlvalue) - Base = new BitCastInst(Base, T_prjlvalue, "", Phi->getIncomingBlock(i)->getTerminator()); - lift->addIncoming(Base, Phi->getIncomingBlock(i)); - } int Number = ++S.MaxPtrNumber; - PHINumbers.push_back(Number); - S.PtrNumbering[lift] = S.AllPtrNumbering[lift] = - S.AllPtrNumbering[Phi] = Number; + S.AllPtrNumbering[lift] = Number; S.ReversePtrNumbering[Number] = lift; + if (!isa(Phi->getType())) + S.AllPtrNumbering[Phi] = Number; + else + Numbers[i] = Number; + lifted.push_back(lift); + } + if (!isa(Phi->getType())) + S.AllCompositeNumbering[Phi] = Numbers; + for (unsigned i = 0; i < Phi->getNumIncomingValues(); ++i) { + Value *Incoming = Phi->getIncomingValue(i); + BasicBlock *IncomingBB = Phi->getIncomingBlock(i); + Instruction *Terminator = IncomingBB->getTerminator(); + Value *Base = MaybeExtractScalar(S, FindBaseValue(S, Incoming, false), Terminator); + std::vector IncomingBases; + if (!isa(Base->getType())) { + IncomingBases = MaybeExtractVector(S, Base, Terminator); + assert(IncomingBases.size() == NumRoots); + } + for (unsigned i = 0; i < NumRoots; ++i) { + PHINode *lift = lifted[i]; + Value *BaseElem; + if (isa(Base->getType())) { + BaseElem = Base; + if (BaseElem->getType() != T_prjlvalue) + BaseElem = new BitCastInst(BaseElem, T_prjlvalue, "", Terminator); + } else { + BaseElem = IncomingBases[i]; + } + lift->addIncoming(BaseElem, IncomingBB); + } } } -int LateLowerGCFrame::NumberBase(State &S, Value *V, Value *CurrentV) +int LateLowerGCFrame::NumberBase(State &S, Value *CurrentV) { auto it = S.AllPtrNumbering.find(CurrentV); if (it != S.AllPtrNumbering.end()) return it->second; int Number; - bool isUnion = isUnionRep(CurrentV->getType()); if (isa(CurrentV)) { // Perm rooted Number = -2; - } else if (isa(CurrentV) || - ((isa(CurrentV) || isa(CurrentV)) && - getValueAddrSpace(CurrentV) != AddressSpace::Tracked)) { + } else if (isa(CurrentV) || isa(CurrentV) || + (isa(CurrentV) && !isTrackedValue(CurrentV))) { // We know this is rooted in the parent + // future note: we could chose to exclude argument of type CalleeRooted here Number = -1; - } else if (!isSpecialPtr(CurrentV->getType()) && !isUnion) { + } else if (!isSpecialPtr(CurrentV->getType())) { // Externally rooted somehow hopefully (otherwise there's a bug in the // input IR) Number = -1; - } else if (isa(CurrentV) && !isUnion && getValueAddrSpace(CurrentV) != AddressSpace::Tracked) { + } else if (isa(CurrentV) && !isTrackedValue(CurrentV)) { Number = -1; - if (LiftSelect(S, cast(CurrentV))) - Number = S.AllPtrNumbering[V] = S.AllPtrNumbering.at(CurrentV); + if (LiftSelect(S, cast(CurrentV))) // lifting a scalar pointer (if necessary) + Number = S.AllPtrNumbering.at(CurrentV); return Number; - } else if (isa(CurrentV) && !isUnion && getValueAddrSpace(CurrentV) != AddressSpace::Tracked) { - SmallVector PHINumbers; - LiftPhi(S, cast(CurrentV), PHINumbers); - Number = S.AllPtrNumbering[V] = S.AllPtrNumbering.at(CurrentV); + } else if (isa(CurrentV) && !isTrackedValue(CurrentV)) { + LiftPhi(S, cast(CurrentV)); + Number = S.AllPtrNumbering.at(CurrentV); return Number; - } else if (isa(CurrentV) && !isUnion) { - assert(false && "TODO: Extract"); - abort(); + } else if (isa(CurrentV)) { + auto Numbers = NumberAllBase(S, CurrentV); + assert(Numbers.size() == 1); + Number = Numbers[0]; } else { - assert( - (CurrentV->getType()->isPointerTy() && - getValueAddrSpace(CurrentV) == AddressSpace::Tracked) || - isUnion); + assert((CurrentV->getType()->isPointerTy() && isTrackedValue(CurrentV))); Number = ++S.MaxPtrNumber; S.ReversePtrNumbering[Number] = CurrentV; } - S.PtrNumbering[CurrentV] = S.AllPtrNumbering[CurrentV] = S.AllPtrNumbering[V] = Number; + S.AllPtrNumbering[CurrentV] = Number; return Number; } int LateLowerGCFrame::Number(State &S, Value *V) { - assert(isSpecialPtr(V->getType()) || isUnionRep(V->getType())); + assert(isSpecialPtr(V->getType())); auto CurrentV = FindBaseValue(S, V); - if (CurrentV.second == -1) - return NumberBase(S, V, CurrentV.first); - auto Numbers = NumberVectorBase(S, CurrentV.first); - auto Number = Numbers.size() == 0 ? -1 : Numbers[CurrentV.second]; - S.AllPtrNumbering[V] = Number; + int Number; + if (CurrentV.second == -1) { + Number = NumberBase(S, CurrentV.first); + } else { + auto Numbers = NumberAllBase(S, CurrentV.first); + Number = Numbers.at(CurrentV.second); + } + if (V != CurrentV.first) + S.AllPtrNumbering[V] = Number; return Number; } -std::vector LateLowerGCFrame::NumberVectorBase(State &S, Value *CurrentV) { - auto it = S.AllVectorNumbering.find(CurrentV); - if (it != S.AllVectorNumbering.end()) - return it->second; - std::vector Numbers{}; - if (isa(CurrentV) || - ((isa(CurrentV) || isa(CurrentV) || - isa(CurrentV)) && - getValueAddrSpace(CurrentV) != AddressSpace::Tracked)) { - Numbers.resize(cast(CurrentV->getType())->getNumElements(), -1); +// assign pointer numbers to a def instruction +std::vector LateLowerGCFrame::NumberAllBase(State &S, Value *CurrentV) { + if (isa(CurrentV->getType())) { + auto it = S.AllPtrNumbering.find(CurrentV); + if (it != S.AllPtrNumbering.end()) + return std::vector({it->second}); + } else { + auto it = S.AllCompositeNumbering.find(CurrentV); + if (it != S.AllCompositeNumbering.end()) + return it->second; + } + + std::vector Numbers; + auto tracked = CountTrackedPointers(CurrentV->getType()); + if (tracked.count == 0) + return Numbers; + if (isa(CurrentV) || isa(CurrentV) || isa(CurrentV) || + (isa(CurrentV) && !isTrackedValue(CurrentV))) { + Numbers.resize(tracked.count, -1); } - /* We (the frontend) don't insert either of these, but it would be legal - - though a bit strange, considering they're pointers - for the optimizer to - do so. All that's needed here is to NumberVector the previous vector/value - and lift the operation */ else if (auto *SVI = dyn_cast(CurrentV)) { - std::vector Numbers1 = NumberVectorBase(S, SVI->getOperand(0)); - std::vector Numbers2 = NumberVectorBase(S, SVI->getOperand(1)); + std::vector Numbers1 = NumberAll(S, SVI->getOperand(0)); + std::vector Numbers2 = NumberAll(S, SVI->getOperand(1)); auto Mask = SVI->getShuffleMask(); - for (unsigned idx : Mask) { - if (idx < Numbers1.size()) { - Numbers.push_back(Numbers1[idx]); + for (auto idx : Mask) { + assert(idx != -1 && "Undef tracked value is invalid"); + if ((unsigned)idx < Numbers1.size()) { + Numbers.push_back(Numbers1.at(idx)); } else { - Numbers.push_back(Numbers2[idx - Numbers1.size()]); + Numbers.push_back(Numbers2.at(idx - Numbers1.size())); } } } else if (auto *IEI = dyn_cast(CurrentV)) { + // TODO: handle non-constant: LiftInsertElement(S, IEI) unsigned idx = cast(IEI->getOperand(2))->getZExtValue(); - Numbers = NumberVectorBase(S, IEI->getOperand(0)); + Numbers = NumberAll(S, IEI->getOperand(0)); int ElNumber = Number(S, IEI->getOperand(1)); Numbers[idx] = ElNumber; - } else if (isa(CurrentV) && getValueAddrSpace(CurrentV) != AddressSpace::Tracked) { - LiftSelect(S, cast(CurrentV)); - Numbers = S.AllVectorNumbering[CurrentV]; - } else if (isa(CurrentV) && getValueAddrSpace(CurrentV) != AddressSpace::Tracked) { - SmallVector PHINumbers; - LiftPhi(S, cast(CurrentV), PHINumbers); - Numbers = S.AllVectorNumbering[CurrentV]; - } else if (isa(CurrentV) || isa(CurrentV) || isa(CurrentV) || - isa(CurrentV)) { + } else if (auto *IVI = dyn_cast(CurrentV)) { + Numbers = NumberAll(S, IVI->getAggregateOperand()); + auto Tracked = TrackCompositeType(IVI->getType()); + assert(Tracked.size() == Numbers.size()); + std::vector InsertNumbers = NumberAll(S, IVI->getInsertedValueOperand()); + auto Idxs = IVI->getIndices(); + unsigned j = 0; + for (unsigned i = 0; i < Tracked.size(); ++i) { + auto Elem = makeArrayRef(Tracked[i]); + if (Elem.size() < Idxs.size()) + continue; + if (Idxs.equals(Elem.slice(0, Idxs.size()))) // Tracked.startswith(Idxs) + Numbers[i] = InsertNumbers[j++]; + } + assert(j == InsertNumbers.size()); + } else if (auto *EVI = dyn_cast(CurrentV)) { + auto BaseNumbers = NumberAll(S, EVI->getAggregateOperand()); + auto Tracked = TrackCompositeType(EVI->getAggregateOperand()->getType()); + assert(Tracked.size() == BaseNumbers.size()); + auto Idxs = EVI->getIndices(); + for (unsigned i = 0; i < Tracked.size(); ++i) { + auto Elem = makeArrayRef(Tracked[i]); + if (Elem.size() > Idxs.size()) + continue; + if (Idxs.equals(Elem.slice(0, Idxs.size()))) // Tracked.startswith(Idxs) + Numbers.push_back(BaseNumbers[i]); + } + assert(CountTrackedPointers(EVI->getType()).count == Numbers.size()); + } else if (tracked.derived) { + if (isa(CurrentV)) { + LiftSelect(S, cast(CurrentV)); + } else if (isa(CurrentV)) { + LiftPhi(S, cast(CurrentV)); + // } else if (isa(CurrentV)) { // TODO: lifting for non constant index + } else { + CurrentV->print(errs()); + llvm_unreachable("Unexpected generating operation for derived values"); + } + if (isa(CurrentV->getType())) { + auto Number = S.AllPtrNumbering.at(CurrentV); + Numbers.resize(1, Number); + } else { + Numbers = S.AllCompositeNumbering.at(CurrentV); + } + } else { + assert((isa(CurrentV) || isa(CurrentV) || isa(CurrentV) || isa(CurrentV)) + && "unexpected def expression"); // This is simple, we can just number them sequentially - for (unsigned i = 0; i < cast(CurrentV->getType())->getNumElements(); ++i) { + for (unsigned i = 0; i < tracked.count; ++i) { int Num = ++S.MaxPtrNumber; Numbers.push_back(Num); S.ReversePtrNumbering[Num] = CurrentV; } + } + if (isa(CurrentV->getType())) { + assert(Numbers.size() == 1); + S.AllPtrNumbering[CurrentV] = Numbers[0]; } else { - assert(false && "Unexpected vector generating operation"); + S.AllCompositeNumbering[CurrentV] = Numbers; } - S.AllVectorNumbering[CurrentV] = Numbers; return Numbers; } -std::vector LateLowerGCFrame::NumberVector(State &S, Value *V) { - auto it = S.AllVectorNumbering.find(V); - if (it != S.AllVectorNumbering.end()) - return it->second; - auto CurrentV = FindBaseValue(S, V); - assert(CurrentV.second == -1); - // E.g. if this is a gep, it's possible for the base to be a single ptr - if (isSpecialPtrVec(CurrentV.first->getType())) { - auto Numbers = NumberVectorBase(S, CurrentV.first); - S.AllVectorNumbering[V] = Numbers; - return Numbers; +// gets the pointer number for every gc tracked value inside V +std::vector LateLowerGCFrame::NumberAll(State &S, Value *V) { + if (isa(V->getType())) { + auto it = S.AllPtrNumbering.find(V); + if (it != S.AllPtrNumbering.end()) + return std::vector({it->second}); } else { - std::vector Numbers{}; - Numbers.resize(cast(V->getType())->getNumElements(), - NumberBase(S, V, CurrentV.first)); + auto it = S.AllCompositeNumbering.find(V); + if (it != S.AllCompositeNumbering.end()) + return it->second; + } + std::vector Numbers; + auto tracked = CountTrackedPointers(V->getType()); + if (tracked.count == 0) return Numbers; + auto CurrentV = FindBaseValue(S, V); + int Number = -1; + if (isa(CurrentV.first->getType())) { + // Base turned out to be a single pointer--number it + assert(CurrentV.second == -1); + Number = NumberBase(S, CurrentV.first); + Numbers.resize(tracked.count, Number); + } else { + // Base turned out to be an aggregate--get all numbers for it, then sub-select + Numbers = NumberAllBase(S, CurrentV.first); + if (CurrentV.second != -1) { + Number = Numbers[CurrentV.second]; // only needed a subset of the values + Numbers.resize(tracked.count, Number); + } + else + assert(!isa(V->getType())); } + if (CurrentV.first != V) { + if (isa(V->getType())) { + S.AllPtrNumbering[V] = Number; + } else { + S.AllCompositeNumbering[V] = Numbers; + } + } + return Numbers; } + static void MaybeResize(BBState &BBS, unsigned Idx) { if (BBS.Defs.size() <= Idx) { BBS.Defs.resize(Idx + 1); @@ -741,32 +926,24 @@ static void NoteDef(State &S, BBState &BBS, int Num, const std::vector &Saf } void LateLowerGCFrame::MaybeNoteDef(State &S, BBState &BBS, Value *Def, const std::vector &SafepointsSoFar, SmallVector &&RefinedPtr) { - int Num = -1; Type *RT = Def->getType(); - if (isSpecialPtr(RT)) { - assert(getValueAddrSpace(Def) == AddressSpace::Tracked && - "Returned value of GC interest, but not tracked?"); - Num = Number(S, Def); - } - else if (isUnionRep(RT)) { - // Probably a union return. Find the extractvalue - Num = Number(S, Def); + if (isa(RT)) { + if (!isSpecialPtr(RT)) + return; + assert(isTrackedValue(Def) && "Returned value of GC interest, but not tracked?"); + int Num = Number(S, Def); + NoteDef(S, BBS, Num, SafepointsSoFar); + if (!RefinedPtr.empty()) + S.Refinements[Num] = std::move(RefinedPtr); } - else if (isSpecialPtrVec(RT)) { - std::vector Nums = NumberVector(S, Def); + else { + std::vector Nums = NumberAll(S, Def); for (int Num : Nums) { NoteDef(S, BBS, Num, SafepointsSoFar); if (!RefinedPtr.empty()) S.Refinements[Num] = RefinedPtr; } - return; } - else { - return; - } - NoteDef(S, BBS, Num, SafepointsSoFar); - if (!RefinedPtr.empty()) - S.Refinements[Num] = std::move(RefinedPtr); } static int NoteSafepoint(State &S, BBState &BBS, CallInst *CI) { @@ -786,8 +963,16 @@ void LateLowerGCFrame::NoteUse(State &S, BBState &BBS, Value *V, BitVector &Uses // Short circuit to avoid having to deal with vectors of constants, etc. if (isa(V)) return; - else if (isSpecialPtrVec(V->getType())) { - std::vector Nums = NumberVector(S, V); + if (isa(V->getType())) { + if (isSpecialPtr(V->getType())) { + int Num = Number(S, V); + if (Num < 0) + return; + MaybeResize(BBS, Num); + Uses[Num] = 1; + } + } else { + std::vector Nums = NumberAll(S, V); for (int Num : Nums) { if (Num < 0) continue; @@ -795,22 +980,11 @@ void LateLowerGCFrame::NoteUse(State &S, BBState &BBS, Value *V, BitVector &Uses Uses[Num] = 1; } } - else { - int Num = Number(S, V); - if (Num < 0) - return; - MaybeResize(BBS, Num); - Uses[Num] = 1; - } } void LateLowerGCFrame::NoteOperandUses(State &S, BBState &BBS, User &UI) { for (Use &U : UI.operands()) { - Value *V = U; - if (isSpecialPtr(V->getType()) || isSpecialPtrVec(V->getType()) || - isUnionRep(V->getType())) { - NoteUse(S, BBS, V); - } + NoteUse(S, BBS, U); } } @@ -932,15 +1106,15 @@ JL_USED_FUNC static void DumpRefinements(State *S) int Num = kv.first; if (Num < 0) continue; - jl_safe_printf("Refinements for %d -- ", Num); + dbgs() << "Refinements for " << Num << " -- "; auto V = S->ReversePtrNumbering[Num]; llvm_dump(V); for (auto refine: kv.second) { if (refine < 0) { - jl_safe_printf(" %d\n", refine); + dbgs() << " " << (int)refine; continue; } - jl_safe_printf(" %d: ", refine); + dbgs() << " " << (int)refine << ": "; auto R = S->ReversePtrNumbering[refine]; llvm_dump(R); } @@ -1098,7 +1272,7 @@ void LateLowerGCFrame::FixUpRefinements(ArrayRef PHINumbers, State &S) State LateLowerGCFrame::LocalScan(Function &F) { State S(F); - SmallVector PHINumbers; + SmallVector PHINumbers; for (BasicBlock &BB : F) { BBState &BBS = S.BBStates[&BB]; for (auto it = BB.rbegin(); it != BB.rend(); ++it) { @@ -1173,8 +1347,8 @@ State LateLowerGCFrame::LocalScan(Function &F) { if (isLoadFromImmut(LI) && isSpecialPtr(LI->getPointerOperand()->getType())) { RefinedPtr.push_back(Number(S, LI->getPointerOperand())); } else if (LI->getType()->isPointerTy() && - isSpecialPtr(LI->getType()) && - LooksLikeFrameRef(LI->getPointerOperand())) { + isSpecialPtr(LI->getType()) && + LooksLikeFrameRef(LI->getPointerOperand())) { // Loads from a jlcall argument array RefinedPtr.push_back(-1); } @@ -1184,35 +1358,17 @@ State LateLowerGCFrame::LocalScan(Function &F) { RefinedPtr.push_back(-2); } if (!LI->getType()->isPointerTy() || - cast(LI->getType())->getAddressSpace() != AddressSpace::Loaded) { + LI->getType()->getPointerAddressSpace() != AddressSpace::Loaded) { MaybeNoteDef(S, BBS, LI, BBS.Safepoints, std::move(RefinedPtr)); } NoteOperandUses(S, BBS, I); } else if (SelectInst *SI = dyn_cast(&I)) { - // We need to insert an extra select for the GC root - if (!isSpecialPtr(SI->getType()) && !isSpecialPtrVec(SI->getType()) && - !isUnionRep(SI->getType())) - continue; - if (!isUnionRep(SI->getType()) && getValueAddrSpace(SI) != AddressSpace::Tracked) { - if (isSpecialPtrVec(SI->getType()) ? - S.AllVectorNumbering.find(SI) != S.AllVectorNumbering.end() : - S.AllPtrNumbering.find(SI) != S.AllPtrNumbering.end()) - continue; - if (!LiftSelect(S, SI)) - continue; - if (!isSpecialPtrVec(SI->getType())) { - // TODO: Refinements for vector select - int Num = S.AllPtrNumbering[SI]; - if (Num < 0) - continue; - auto SelectBase = cast(S.ReversePtrNumbering[Num]); - SmallVector RefinedPtr{Number(S, SelectBase->getTrueValue()), - Number(S, SelectBase->getFalseValue())}; - S.Refinements[Num] = std::move(RefinedPtr); - } - } else { + auto tracked = CountTrackedPointers(SI->getType()); + if (tracked.count && !tracked.derived) { + // record the select definition of these values SmallVector RefinedPtr; - if (!isSpecialPtrVec(SI->getType())) { + if (isa(SI->getType())) { + // TODO: Refinements for vector select RefinedPtr = { Number(S, SI->getTrueValue()), Number(S, SI->getFalseValue()) @@ -1220,44 +1376,41 @@ State LateLowerGCFrame::LocalScan(Function &F) { } MaybeNoteDef(S, BBS, SI, BBS.Safepoints, std::move(RefinedPtr)); NoteOperandUses(S, BBS, I); + } else if (tracked.count) { + // We need to insert extra selects for the GC roots + LiftSelect(S, SI); } } else if (PHINode *Phi = dyn_cast(&I)) { - if (!isSpecialPtr(Phi->getType()) && !isSpecialPtrVec(Phi->getType()) && - !isUnionRep(Phi->getType())) { - continue; - } - auto nIncoming = Phi->getNumIncomingValues(); - // We need to insert an extra phi for the GC root - if (!isUnionRep(Phi->getType()) && getValueAddrSpace(Phi) != AddressSpace::Tracked) { - if (isSpecialPtrVec(Phi->getType()) ? - S.AllVectorNumbering.find(Phi) != S.AllVectorNumbering.end() : - S.AllPtrNumbering.find(Phi) != S.AllPtrNumbering.end()) - continue; - LiftPhi(S, Phi, PHINumbers); - } else { + auto tracked = CountTrackedPointers(Phi->getType()); + if (tracked.count && !tracked.derived) { + // record the phi definition of these values SmallVector PHIRefinements; - if (!isSpecialPtrVec(Phi->getType())) + if (isa(Phi->getType())) + // TODO: Vector refinements PHIRefinements = GetPHIRefinements(Phi, S); MaybeNoteDef(S, BBS, Phi, BBS.Safepoints, std::move(PHIRefinements)); - if (isSpecialPtrVec(Phi->getType())) { - // TODO: Vector refinements - std::vector Nums = NumberVector(S, Phi); + if (isa(Phi->getType())) { + PHINumbers.push_back(Number(S, Phi)); + } else { + std::vector Nums = NumberAll(S, Phi); for (int Num : Nums) PHINumbers.push_back(Num); - } else { - PHINumbers.push_back(Number(S, Phi)); } + unsigned nIncoming = Phi->getNumIncomingValues(); for (unsigned i = 0; i < nIncoming; ++i) { BBState &IncomingBBS = S.BBStates[Phi->getIncomingBlock(i)]; NoteUse(S, IncomingBBS, Phi->getIncomingValue(i), IncomingBBS.PhiOuts); } + } else if (tracked.count) { + // We need to insert extra phis for the GC roots + LiftPhi(S, Phi); } } else if (isa(&I)) { NoteOperandUses(S, BBS, I); } else if (isa(&I)) { NoteOperandUses(S, BBS, I); } else if (auto *ASCI = dyn_cast(&I)) { - if (getValueAddrSpace(ASCI) == AddressSpace::Tracked) { + if (isTrackedValue(ASCI)) { SmallVector RefinedPtr{}; auto origin = ASCI->getPointerOperand()->stripPointerCasts(); if (auto LI = dyn_cast(origin)) { @@ -1268,9 +1421,8 @@ State LateLowerGCFrame::LocalScan(Function &F) { MaybeNoteDef(S, BBS, ASCI, BBS.Safepoints, std::move(RefinedPtr)); } } else if (auto *AI = dyn_cast(&I)) { - if (isSpecialPtr(AI->getAllocatedType()) && !AI->isArrayAllocation() && - cast(AI->getAllocatedType())->getAddressSpace() == AddressSpace::Tracked) - { + Type *ElT = AI->getAllocatedType(); + if (AI->isStaticAlloca() && isa(ElT) && ElT->getPointerAddressSpace() == AddressSpace::Tracked) { S.Allocas.push_back(AI); } } @@ -1686,7 +1838,7 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S) { Frame = new AllocaInst(T_prjlvalue, 0, ConstantInt::get(T_int32, maxframeargs), "", StartOff); } - SmallVector write_barriers; + std::vector write_barriers; for (BasicBlock &BB : F) { for (auto it = BB.begin(); it != BB.end();) { Instruction *I = &*it; @@ -1912,23 +2064,20 @@ static void AddInPredLiveOuts(BasicBlock *BB, BitVector &LiveIn, State &S) void LateLowerGCFrame::PlaceGCFrameStore(State &S, unsigned R, unsigned MinColorRoot, const std::vector &Colors, Value *GCFrame, - Instruction *InsertionPoint) { - Value *Val = GetPtrForNumber(S, R, InsertionPoint); - + Instruction *InsertBefore) { // Get the slot address. auto slotAddress = CallInst::Create( getOrDeclare(jl_intrinsics::getGCFrameSlot), - {GCFrame, ConstantInt::get(T_int32, Colors[R] + MinColorRoot)}); + {GCFrame, ConstantInt::get(T_int32, Colors[R] + MinColorRoot)}, + "", InsertBefore); - slotAddress->insertBefore(InsertionPoint); - - Val = MaybeExtractUnion(std::make_pair(Val, -1), InsertionPoint); + Value *Val = GetPtrForNumber(S, R, InsertBefore); // Pointee types don't have semantics, so the optimizer is // free to rewrite them if convenient. We need to change // it back here for the store. if (Val->getType() != T_prjlvalue) - Val = new BitCastInst(Val, T_prjlvalue, "", InsertionPoint); - new StoreInst(Val, slotAddress, InsertionPoint); + Val = new BitCastInst(Val, T_prjlvalue, "", InsertBefore); + new StoreInst(Val, slotAddress, InsertBefore); } void LateLowerGCFrame::PlaceGCFrameStores(State &S, unsigned MinColorRoot, @@ -1962,28 +2111,32 @@ void LateLowerGCFrame::PlaceRootsAndUpdateCalls(std::vector &Colors, State for (auto C : Colors) if (C > MaxColor) MaxColor = C; + // Insert instructions for the actual gc frame - if (MaxColor != -1 || S.Allocas.size() != 0) { - unsigned NRoots = MaxColor + 1 + S.Allocas.size(); + if (MaxColor != -1 || !S.Allocas.empty()) { // Create and push a GC frame. auto gcframe = CallInst::Create( getOrDeclare(jl_intrinsics::newGCFrame), - {ConstantInt::get(T_int32, NRoots)}, + {ConstantInt::get(T_int32, 0)}, "gcframe"); gcframe->insertBefore(&*F->getEntryBlock().begin()); auto pushGcframe = CallInst::Create( getOrDeclare(jl_intrinsics::pushGCFrame), - {gcframe, ConstantInt::get(T_int32, NRoots)}); + {gcframe, ConstantInt::get(T_int32, 0)}); pushGcframe->insertAfter(ptlsStates); // Replace Allocas unsigned AllocaSlot = 0; - for (AllocaInst *AI : S.Allocas) { + auto replace_alloca = [this, gcframe, &AllocaSlot](AllocaInst *&AI) { // Pick a slot for the alloca. - auto slotAddress = CallInst::Create( + unsigned align = AI->getAlignment() / sizeof(void*); // TODO: use DataLayout pointer size + assert(align <= 16 && "Alignment exceeds llvm-final-gc-lowering abilities"); + if (align > 1) + AllocaSlot = LLT_ALIGN(AllocaSlot, align); + Instruction *slotAddress = CallInst::Create( getOrDeclare(jl_intrinsics::getGCFrameSlot), - {gcframe, ConstantInt::get(T_int32, AllocaSlot++)}); + {gcframe, ConstantInt::get(T_int32, AllocaSlot)}); slotAddress->insertAfter(gcframe); slotAddress->takeName(AI); @@ -1997,14 +2150,30 @@ void LateLowerGCFrame::PlaceRootsAndUpdateCalls(std::vector &Colors, State return; ToDelete.push_back(II); }, AI); - for (CallInst *II : ToDelete) + for (CallInst *II : ToDelete) { II->eraseFromParent(); + } + if (slotAddress->getType() != AI->getType()) { + // If we're replacing an ArrayAlloca, the pointer element type may need to be fixed up + auto BCI = new BitCastInst(slotAddress, AI->getType()); + BCI->insertAfter(slotAddress); + slotAddress = BCI; + } AI->replaceAllUsesWith(slotAddress); AI->eraseFromParent(); + AI = NULL; + }; + for (AllocaInst *AI : S.Allocas) { + auto ns = cast(AI->getArraySize())->getZExtValue(); + replace_alloca(AI); + AllocaSlot += ns; } - unsigned MinColorRoot = AllocaSlot; + auto NRoots = ConstantInt::get(T_int32, MaxColor + 1 + AllocaSlot); + gcframe->setArgOperand(0, NRoots); + pushGcframe->setArgOperand(1, NRoots); + // Insert GC frame stores - PlaceGCFrameStores(S, MinColorRoot, Colors, gcframe); + PlaceGCFrameStores(S, AllocaSlot, Colors, gcframe); // Insert GCFrame pops for(Function::iterator I = F->begin(), E = F->end(); I != E; ++I) { if (isa(I->getTerminator())) { diff --git a/src/llvm-pass-helpers.cpp b/src/llvm-pass-helpers.cpp index 7efb5394c968a..bdee45f08b2ae 100644 --- a/src/llvm-pass-helpers.cpp +++ b/src/llvm-pass-helpers.cpp @@ -1,4 +1,5 @@ // This file is a part of Julia. License is MIT: https://julialang.org/license + // // This file implements common functionality that is useful for the late GC frame // lowering and final GC intrinsic lowering passes. See the corresponding header diff --git a/src/partr.c b/src/partr.c index 720bc2b9a298d..c457643797446 100644 --- a/src/partr.c +++ b/src/partr.c @@ -373,10 +373,11 @@ JL_DLLEXPORT void jl_set_task_tid(jl_task_t *task, int tid) JL_NOTSAFEPOINT } // get the next runnable task from the multiq -static jl_task_t *get_next_task(jl_value_t *getsticky) +static jl_task_t *get_next_task(jl_value_t *trypoptask, jl_value_t *q) { jl_gc_safepoint(); - jl_task_t *task = (jl_task_t*)jl_apply(&getsticky, 1); + jl_value_t *args[2] = { trypoptask, q }; + jl_task_t *task = (jl_task_t*)jl_apply(args, 2); if (jl_typeis(task, jl_task_type)) { int self = jl_get_ptls_states()->tid; jl_set_task_tid(task, self); @@ -393,14 +394,14 @@ static int may_sleep(jl_ptls_t ptls) extern volatile unsigned _threadedregion; -JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *getsticky) +JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *trypoptask, jl_value_t *q) { jl_ptls_t ptls = jl_get_ptls_states(); uint64_t start_cycles = 0; jl_task_t *task; while (1) { - task = get_next_task(getsticky); + task = get_next_task(trypoptask, q); if (task) return task; @@ -416,7 +417,7 @@ JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *getsticky) if (!sleep_check_now(ptls->tid)) continue; jl_atomic_store(&ptls->sleep_check_state, sleeping); // acquire sleep-check lock - task = get_next_task(getsticky); + task = get_next_task(trypoptask, q); if (task) return task; // one thread should win this race and watch the event loop @@ -483,7 +484,7 @@ JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *getsticky) #ifndef JL_HAVE_ASYNCIFY // maybe check the kernel for new messages too if (jl_atomic_load(&jl_uv_n_waiters) == 0) - jl_process_events(jl_global_event_loop()); + jl_process_events(); #else // Yield back to browser event loop return ptls->root_task; diff --git a/src/rtutils.c b/src/rtutils.c index c8670b942e172..24b07169f13f7 100644 --- a/src/rtutils.c +++ b/src/rtutils.c @@ -297,7 +297,7 @@ JL_DLLEXPORT void jl_restore_excstack(size_t state) void jl_copy_excstack(jl_excstack_t *dest, jl_excstack_t *src) JL_NOTSAFEPOINT { assert(dest->reserved_size >= src->top); - memcpy(jl_excstack_raw(dest), jl_excstack_raw(src), sizeof(uintptr_t)*src->top); + memcpy(jl_excstack_raw(dest), jl_excstack_raw(src), sizeof(jl_bt_element_t)*src->top); dest->top = src->top; } @@ -317,15 +317,16 @@ void jl_reserve_excstack(jl_excstack_t **stack JL_REQUIRE_ROOTED_SLOT, } void jl_push_excstack(jl_excstack_t **stack JL_REQUIRE_ROOTED_SLOT JL_ROOTING_ARGUMENT, - jl_value_t *exception JL_ROOTED_ARGUMENT, - uintptr_t *bt_data, size_t bt_size) + jl_value_t *exception JL_ROOTED_ARGUMENT, + jl_bt_element_t *bt_data, size_t bt_size) { jl_reserve_excstack(stack, (*stack ? (*stack)->top : 0) + bt_size + 2); jl_excstack_t *s = *stack; - memcpy(jl_excstack_raw(s) + s->top, bt_data, sizeof(uintptr_t)*bt_size); + jl_bt_element_t *rawstack = jl_excstack_raw(s); + memcpy(rawstack + s->top, bt_data, sizeof(jl_bt_element_t)*bt_size); s->top += bt_size + 2; - jl_excstack_raw(s)[s->top-2] = bt_size; - jl_excstack_raw(s)[s->top-1] = (uintptr_t)exception; + rawstack[s->top-2].uintptr = bt_size; + rawstack[s->top-1].jlvalue = exception; } // conversion ----------------------------------------------------------------- @@ -385,7 +386,7 @@ JL_DLLEXPORT jl_nullable_float64_t jl_try_substrtod(char *str, size_t offset, si newstr = (char*)alloca(len + 1); } else { - newstr = tofree = (char*)malloc(len + 1); + newstr = tofree = (char*)malloc_s(len + 1); } memcpy(newstr, bstr, len); newstr[len] = 0; @@ -444,7 +445,7 @@ JL_DLLEXPORT jl_nullable_float32_t jl_try_substrtof(char *str, size_t offset, si newstr = (char*)alloca(len + 1); } else { - newstr = tofree = (char*)malloc(len + 1); + newstr = tofree = (char*)malloc_s(len + 1); } memcpy(newstr, bstr, len); newstr[len] = 0; diff --git a/src/runtime_ccall.cpp b/src/runtime_ccall.cpp index 220dfed1b2d5d..a7797c080c145 100644 --- a/src/runtime_ccall.cpp +++ b/src/runtime_ccall.cpp @@ -195,13 +195,22 @@ static void *trampoline_alloc() { const int sz = 64; // oversized for most platforms. todo: use precise value? if (!trampoline_freelist) { + int last_errno = errno; #ifdef _OS_WINDOWS_ + DWORD last_error = GetLastError(); void *mem = VirtualAlloc(NULL, jl_page_size, MEM_RESERVE | MEM_COMMIT, PAGE_EXECUTE_READWRITE); + if (mem == NULL) + jl_throw(jl_memory_exception); + SetLastError(last_error); #else void *mem = mmap(0, jl_page_size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + errno = last_errno; + if (mem == MAP_FAILED) + jl_throw(jl_memory_exception); #endif + errno = last_errno; void *next = NULL; for (size_t i = 0; i + sz <= jl_page_size; i += sz) { void **curr = (void**)((char*)mem + i); @@ -260,7 +269,7 @@ jl_value_t *jl_get_cfunction_trampoline( htable_t **cache2 = (htable_t**)ptrhash_bp(cache, (void*)vals); cache = *cache2; if (cache == HT_NOTFOUND) { - cache = htable_new((htable_t*)malloc(sizeof(htable_t)), 1); + cache = htable_new((htable_t*)malloc_s(sizeof(htable_t)), 1); *cache2 = cache; } } @@ -272,7 +281,7 @@ jl_value_t *jl_get_cfunction_trampoline( // not found, allocate a new one size_t n = jl_svec_len(fill); - void **nval = (void**)malloc(sizeof(void*) * (n + 1)); + void **nval = (void**)malloc_s(sizeof(void*) * (n + 1)); nval[0] = (void*)fobj; jl_value_t *result; JL_TRY { diff --git a/src/runtime_intrinsics.c b/src/runtime_intrinsics.c index c479aff6bb095..d0686d2213ec2 100644 --- a/src/runtime_intrinsics.c +++ b/src/runtime_intrinsics.c @@ -914,6 +914,7 @@ un_fintrinsic(floor_float,floor_llvm) un_fintrinsic(trunc_float,trunc_llvm) un_fintrinsic(rint_float,rint_llvm) un_fintrinsic(sqrt_float,sqrt_llvm) +un_fintrinsic(sqrt_float,sqrt_llvm_fast) JL_DLLEXPORT jl_value_t *jl_arraylen(jl_value_t *a) { diff --git a/src/signal-handling.c b/src/signal-handling.c index 73ca73e22d708..43d133f6e394c 100644 --- a/src/signal-handling.c +++ b/src/signal-handling.c @@ -18,7 +18,7 @@ extern "C" { #include // Profiler control variables // -static volatile intptr_t *bt_data_prof = NULL; +static volatile jl_bt_element_t *bt_data_prof = NULL; static volatile size_t bt_size_max = 0; static volatile size_t bt_size_cur = 0; static volatile uint64_t nsecprof = 0; @@ -221,7 +221,7 @@ void jl_show_sigill(void *_ctx) } // what to do on a critical error -void jl_critical_error(int sig, bt_context_t *context, uintptr_t *bt_data, size_t *bt_size) +void jl_critical_error(int sig, bt_context_t *context, jl_bt_element_t *bt_data, size_t *bt_size) { // This function is not allowed to reference any TLS variables. // We need to explicitly pass in the TLS buffer pointer when @@ -230,10 +230,14 @@ void jl_critical_error(int sig, bt_context_t *context, uintptr_t *bt_data, size_ if (sig) jl_safe_printf("\nsignal (%d): %s\n", sig, strsignal(sig)); jl_safe_printf("in expression starting at %s:%d\n", jl_filename, jl_lineno); - if (context) + if (context) { + // Must avoid extended backtrace frames here unless we're sure bt_data + // is properly rooted. *bt_size = n = rec_backtrace_ctx(bt_data, JL_MAX_BT_SIZE, context, 0); - for (i = 0; i < n; i++) - jl_gdblookup(bt_data[i] - 1); + } + for (i = 0; i < n; i += jl_bt_entry_size(bt_data + i)) { + jl_print_bt_entry_codeloc(bt_data + i); + } gc_debug_print_status(); gc_debug_critical_error(); } @@ -247,7 +251,7 @@ JL_DLLEXPORT int jl_profile_init(size_t maxsize, uint64_t delay_nsec) nsecprof = delay_nsec; if (bt_data_prof != NULL) free((void*)bt_data_prof); - bt_data_prof = (intptr_t*) calloc(maxsize, sizeof(intptr_t)); + bt_data_prof = (jl_bt_element_t*) calloc(maxsize, sizeof(jl_bt_element_t)); if (bt_data_prof == NULL && maxsize > 0) return -1; bt_size_cur = 0; diff --git a/src/signals-mach.c b/src/signals-mach.c index 09caa0a874ec4..24b02ead82970 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -412,7 +412,7 @@ void *mach_profile_listener(void *arg) #ifdef LIBOSXUNWIND mach_profiler_thread = mach_thread_self(); #endif - mig_reply_error_t *bufRequest = (mig_reply_error_t *) malloc(max_size); + mig_reply_error_t *bufRequest = (mig_reply_error_t*)malloc_s(max_size); while (1) { kern_return_t ret = mach_msg(&bufRequest->Head, MACH_RCV_MSG, 0, max_size, profile_port, @@ -448,10 +448,10 @@ void *mach_profile_listener(void *arg) if (forceDwarf == 0) { // Save the backtrace - bt_size_cur += rec_backtrace_ctx((uintptr_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc, 0); + bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc, 0); } else if (forceDwarf == 1) { - bt_size_cur += rec_backtrace_ctx_dwarf((uintptr_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc, 0); + bt_size_cur += rec_backtrace_ctx_dwarf((jl_bt_element_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc, 0); } else if (forceDwarf == -1) { jl_safe_printf("WARNING: profiler attempt to access an invalid memory location\n"); @@ -459,11 +459,11 @@ void *mach_profile_listener(void *arg) forceDwarf = -2; #else - bt_size_cur += rec_backtrace_ctx((uintptr_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc, 0); + bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, uc, 0); #endif // Mark the end of this block with 0 - bt_data_prof[bt_size_cur++] = 0; + bt_data_prof[bt_size_cur++].uintptr = 0; // Reset the alarm kern_return_t ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port); diff --git a/src/signals-unix.c b/src/signals-unix.c index fa234c337714c..5737d64248be9 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -551,7 +551,7 @@ static void kqueue_signal(int *sigqueue, struct kevent *ev, int sig) static void *signal_listener(void *arg) { - static uintptr_t bt_data[JL_MAX_BT_SIZE + 1]; + static jl_bt_element_t bt_data[JL_MAX_BT_SIZE + 1]; static size_t bt_size = 0; sigset_t sset; int sig, critical, profile; @@ -669,7 +669,7 @@ static void *signal_listener(void *arg) bt_size += rec_backtrace_ctx(bt_data + bt_size, JL_MAX_BT_SIZE / jl_n_threads - 1, signal_context, 0); - bt_data[bt_size++] = 0; + bt_data[bt_size++].uintptr = 0; } // do backtrace for profiler @@ -686,13 +686,13 @@ static void *signal_listener(void *arg) jl_safe_printf("WARNING: profiler attempt to access an invalid memory location\n"); } else { // Get backtrace data - bt_size_cur += rec_backtrace_ctx((uintptr_t*)bt_data_prof + bt_size_cur, + bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, signal_context, 0); } ptls->safe_restore = old_buf; // Mark the end of this block with 0 - bt_data_prof[bt_size_cur++] = 0; + bt_data_prof[bt_size_cur++].uintptr = 0; } if (bt_size_cur >= bt_size_max - 1) { // Buffer full: Delete the timer diff --git a/src/signals-win.c b/src/signals-win.c index 49ea19780c0b5..fb913293d45c8 100644 --- a/src/signals-win.c +++ b/src/signals-win.c @@ -297,7 +297,7 @@ LONG WINAPI jl_exception_handler(struct _EXCEPTION_POINTERS *ExceptionInfo) jl_safe_printf("UNKNOWN"); break; } jl_safe_printf(" at 0x%Ix -- ", (size_t)ExceptionInfo->ExceptionRecord->ExceptionAddress); - jl_gdblookup((uintptr_t)ExceptionInfo->ExceptionRecord->ExceptionAddress); + jl_print_native_codeloc((uintptr_t)ExceptionInfo->ExceptionRecord->ExceptionAddress); jl_critical_error(0, ExceptionInfo->ContextRecord, ptls->bt_data, &ptls->bt_size); @@ -344,10 +344,10 @@ static DWORD WINAPI profile_bt( LPVOID lparam ) break; } // Get backtrace data - bt_size_cur += rec_backtrace_ctx((uintptr_t*)bt_data_prof + bt_size_cur, + bt_size_cur += rec_backtrace_ctx((jl_bt_element_t*)bt_data_prof + bt_size_cur, bt_size_max - bt_size_cur - 1, &ctxThread, 0); // Mark the end of this block with 0 - bt_data_prof[bt_size_cur] = 0; + bt_data_prof[bt_size_cur].uintptr = 0; bt_size_cur++; } if ((DWORD)-1 == ResumeThread(hMainThread)) { diff --git a/src/stackwalk.c b/src/stackwalk.c index b403a5e46e1a8..f088f1b7b69a2 100644 --- a/src/stackwalk.c +++ b/src/stackwalk.c @@ -45,11 +45,10 @@ static int jl_unw_step(bt_cursor_t *cursor, uintptr_t *ip, uintptr_t *sp, uintpt // // jl_unw_stepn will return 1 if there are more frames to come. The number of // elements written to bt_data (and sp if non-NULL) are returned in bt_size. -int jl_unw_stepn(bt_cursor_t *cursor, uintptr_t *bt_data, size_t *bt_size, +int jl_unw_stepn(bt_cursor_t *cursor, jl_bt_element_t *bt_data, size_t *bt_size, uintptr_t *sp, size_t maxsize, int skip, int add_interp_frames, int from_signal_handler) JL_NOTSAFEPOINT { - jl_ptls_t ptls = jl_get_ptls_states(); volatile size_t n = 0; volatile int need_more_space = 0; uintptr_t return_ip = 0; @@ -65,6 +64,7 @@ int jl_unw_stepn(bt_cursor_t *cursor, uintptr_t *bt_data, size_t *bt_size, } #endif #if !defined(_OS_WINDOWS_) + jl_ptls_t ptls = jl_get_ptls_states(); jl_jmp_buf *old_buf = ptls->safe_restore; jl_jmp_buf buf; if (!jl_setjmp(buf, 0)) { @@ -117,17 +117,17 @@ int jl_unw_stepn(bt_cursor_t *cursor, uintptr_t *bt_data, size_t *bt_size, // normal frame call_ip -= 1; } - if (call_ip == JL_BT_INTERP_FRAME) { + if (call_ip == JL_BT_NON_PTR_ENTRY) { // Never leave special marker in the bt data as it can corrupt the GC. call_ip = 0; } - uintptr_t *bt_entry = bt_data + n; + jl_bt_element_t *bt_entry = bt_data + n; size_t entry_sz = 0; if (add_interp_frames && jl_is_enter_interpreter_frame(call_ip) && (entry_sz = jl_capture_interp_frame(bt_entry, thesp, thefp, maxsize-n)) != 0) { n += entry_sz; } else { - *bt_entry = call_ip; + bt_entry->uintptr = call_ip; n++; } } @@ -149,7 +149,7 @@ int jl_unw_stepn(bt_cursor_t *cursor, uintptr_t *bt_data, size_t *bt_size, return need_more_space; } -NOINLINE size_t rec_backtrace_ctx(uintptr_t *bt_data, size_t maxsize, +NOINLINE size_t rec_backtrace_ctx(jl_bt_element_t *bt_data, size_t maxsize, bt_context_t *context, int add_interp_frames) JL_NOTSAFEPOINT { bt_cursor_t cursor; @@ -165,7 +165,7 @@ NOINLINE size_t rec_backtrace_ctx(uintptr_t *bt_data, size_t maxsize, // // The first `skip` frames are omitted, in addition to omitting the frame from // `rec_backtrace` itself. -NOINLINE size_t rec_backtrace(uintptr_t *bt_data, size_t maxsize, int skip) +NOINLINE size_t rec_backtrace(jl_bt_element_t *bt_data, size_t maxsize, int skip) { bt_context_t context; memset(&context, 0, sizeof(context)); @@ -217,7 +217,7 @@ JL_DLLEXPORT jl_value_t *jl_backtrace_from_here(int returnsp, int skip) jl_array_grow_end(sp, maxincr); } size_t size_incr = 0; - have_more_frames = jl_unw_stepn(&cursor, (uintptr_t*)jl_array_data(ip) + offset, + have_more_frames = jl_unw_stepn(&cursor, (jl_bt_element_t*)jl_array_data(ip) + offset, &size_incr, sp_ptr, maxincr, skip, 1, 0); skip = 0; offset += size_incr; @@ -227,12 +227,18 @@ JL_DLLEXPORT jl_value_t *jl_backtrace_from_here(int returnsp, int skip) jl_array_del_end(sp, jl_array_len(sp) - offset); size_t n = 0; + jl_bt_element_t *bt_data = (jl_bt_element_t*)jl_array_data(ip); while (n < jl_array_len(ip)) { - if ((uintptr_t)jl_array_ptr_ref(ip, n) == JL_BT_INTERP_FRAME) { - jl_array_ptr_1d_push(bt2, jl_array_ptr_ref(ip, n+1)); - n += 2; + jl_bt_element_t *bt_entry = bt_data + n; + if (!jl_bt_is_native(bt_entry)) { + size_t njlvals = jl_bt_num_jlvals(bt_entry); + for (size_t j = 0; j < njlvals; j++) { + jl_value_t *v = jl_bt_entry_jlvalue(bt_entry, j); + JL_GC_PROMISE_ROOTED(v); + jl_array_ptr_1d_push(bt2, v); + } } - n++; + n += jl_bt_entry_size(bt_entry); } } jl_value_t *bt = returnsp ? (jl_value_t*)jl_svec(3, ip, bt2, sp) : (jl_value_t*)jl_svec(2, ip, bt2); @@ -240,42 +246,48 @@ JL_DLLEXPORT jl_value_t *jl_backtrace_from_here(int returnsp, int skip) return bt; } -void decode_backtrace(uintptr_t *bt_data, size_t bt_size, - jl_array_t **btout, jl_array_t **bt2out) +void decode_backtrace(jl_bt_element_t *bt_data, size_t bt_size, + jl_array_t **btout JL_REQUIRE_ROOTED_SLOT, + jl_array_t **bt2out JL_REQUIRE_ROOTED_SLOT) { - jl_array_t *bt = NULL; - jl_array_t *bt2 = NULL; - JL_GC_PUSH2(&bt, &bt2); + jl_array_t *bt, *bt2; if (array_ptr_void_type == NULL) { array_ptr_void_type = jl_apply_type2((jl_value_t*)jl_array_type, (jl_value_t*)jl_voidpointer_type, jl_box_long(1)); } - bt = jl_alloc_array_1d(array_ptr_void_type, bt_size); - memcpy(bt->data, bt_data, bt_size * sizeof(void*)); - bt2 = jl_alloc_array_1d(jl_array_any_type, 0); - // Scan the stack for any interpreter frames - size_t n = 0; - while (n < bt_size) { - if (bt_data[n] == JL_BT_INTERP_FRAME) { - jl_array_ptr_1d_push(bt2, (jl_value_t*)bt_data[n+1]); - n += 2; + bt = *btout = jl_alloc_array_1d(array_ptr_void_type, bt_size); + static_assert(sizeof(jl_bt_element_t) == sizeof(void*), + "jl_bt_element_t is presented as Ptr{Cvoid} on julia side"); + memcpy(bt->data, bt_data, bt_size * sizeof(jl_bt_element_t)); + bt2 = *bt2out = jl_alloc_array_1d(jl_array_any_type, 0); + // Scan the backtrace buffer for any gc-managed values + for (size_t i = 0; i < bt_size; i += jl_bt_entry_size(bt_data + i)) { + jl_bt_element_t* bt_entry = bt_data + i; + if (jl_bt_is_native(bt_entry)) + continue; + size_t njlvals = jl_bt_num_jlvals(bt_entry); + for (size_t j = 0; j < njlvals; j++) { + jl_value_t *v = jl_bt_entry_jlvalue(bt_entry, j); + JL_GC_PROMISE_ROOTED(v); + jl_array_ptr_1d_push(bt2, v); } - n++; } - *btout = bt; - *bt2out = bt2; - JL_GC_POP(); } -JL_DLLEXPORT void jl_get_backtrace(jl_array_t **btout, jl_array_t **bt2out) +JL_DLLEXPORT jl_value_t *jl_get_backtrace(void) { jl_excstack_t *s = jl_get_ptls_states()->current_task->excstack; - uintptr_t *bt_data = NULL; + jl_bt_element_t *bt_data = NULL; size_t bt_size = 0; if (s && s->top) { bt_data = jl_excstack_bt_data(s, s->top); bt_size = jl_excstack_bt_size(s, s->top); } - decode_backtrace(bt_data, bt_size, btout, bt2out); + jl_array_t *bt = NULL, *bt2 = NULL; + JL_GC_PUSH2(&bt, &bt2); + decode_backtrace(bt_data, bt_size, &bt, &bt2); + jl_svec_t *pair = jl_svec2(bt, bt2); + JL_GC_POP(); + return (jl_value_t*)pair; } // Return data from the exception stack for `task` as an array of Any, starting @@ -518,7 +530,7 @@ static int jl_unw_step(bt_cursor_t *cursor, uintptr_t *ip, uintptr_t *sp, uintpt } #ifdef LIBOSXUNWIND -NOINLINE size_t rec_backtrace_ctx_dwarf(uintptr_t *bt_data, size_t maxsize, +NOINLINE size_t rec_backtrace_ctx_dwarf(jl_bt_element_t *bt_data, size_t maxsize, bt_context_t *context, int add_interp_frames) { size_t bt_size = 0; @@ -576,8 +588,22 @@ JL_DLLEXPORT jl_value_t *jl_lookup_code_address(void *ip, int skipC) return rs; } -//for looking up functions from gdb: -JL_DLLEXPORT void jl_gdblookup(uintptr_t ip) +void jl_safe_print_codeloc(const char* func_name, const char* file_name, + int line, int inlined) JL_NOTSAFEPOINT +{ + const char *inlined_str = inlined ? " [inlined]" : ""; + if (line != -1) { + jl_safe_printf("%s at %s:%d%s\n", func_name, file_name, line, inlined_str); + } + else { + jl_safe_printf("%s at %s (unknown line)%s\n", func_name, file_name, inlined_str); + } +} + +// Print function, file and line containing native instruction pointer `ip` by +// looking up debug info. Prints multiple such frames when `ip` points to +// inlined code. +void jl_print_native_codeloc(uintptr_t ip) JL_NOTSAFEPOINT { // This function is not allowed to reference any TLS variables since // it can be called from an unmanaged thread on OSX. @@ -592,15 +618,7 @@ JL_DLLEXPORT void jl_gdblookup(uintptr_t ip) jl_safe_printf("unknown function (ip: %p)\n", (void*)ip); } else { - const char *inlined = frame.inlined ? " [inlined]" : ""; - if (frame.line != -1) { - jl_safe_printf("%s at %s:%" PRIuPTR "%s\n", frame.func_name, - frame.file_name, (uintptr_t)frame.line, inlined); - } - else { - jl_safe_printf("%s at %s (unknown line)%s\n", frame.func_name, - frame.file_name, inlined); - } + jl_safe_print_codeloc(frame.func_name, frame.file_name, frame.line, frame.inlined); free(frame.func_name); free(frame.file_name); } @@ -608,22 +626,70 @@ JL_DLLEXPORT void jl_gdblookup(uintptr_t ip) free(frames); } +// Print code location for backtrace buffer entry at *bt_entry +void jl_print_bt_entry_codeloc(jl_bt_element_t *bt_entry) JL_NOTSAFEPOINT +{ + if (jl_bt_is_native(bt_entry)) { + jl_print_native_codeloc(bt_entry[0].uintptr); + } + else if (jl_bt_entry_tag(bt_entry) == JL_BT_INTERP_FRAME_TAG) { + size_t ip = jl_bt_entry_header(bt_entry); + jl_value_t *code = jl_bt_entry_jlvalue(bt_entry, 0); + if (jl_is_method_instance(code)) { + // When interpreting a method instance, need to unwrap to find the code info + code = ((jl_method_instance_t*)code)->uninferred; + } + if (jl_is_code_info(code)) { + jl_code_info_t *src = (jl_code_info_t*)code; + // See also the debug info handling in codegen.cpp. + // NB: debuginfoloc is 1-based! + intptr_t debuginfoloc = ((int32_t*)jl_array_data(src->codelocs))[ip]; + while (debuginfoloc != 0) { + jl_line_info_node_t *locinfo = (jl_line_info_node_t*) + jl_array_ptr_ref(src->linetable, debuginfoloc - 1); + assert(jl_typeis(locinfo, jl_lineinfonode_type)); + jl_value_t *method = locinfo->method; + if (jl_is_method_instance(method)) { + method = ((jl_method_instance_t*)method)->def.value; + if (jl_is_method(method)) + method = (jl_value_t*)((jl_method_t*)method)->name; + } + const char *func_name = jl_is_symbol(method) ? + jl_symbol_name((jl_sym_t*)method) : "Unknown"; + jl_safe_print_codeloc(func_name, jl_symbol_name(locinfo->file), + locinfo->line, locinfo->inlined_at); + debuginfoloc = locinfo->inlined_at; + } + } + else { + // If we're using this function something bad has already happened; + // be a bit defensive to avoid crashing while reporting the crash. + jl_safe_printf("No code info - unknown interpreter state!\n"); + } + } + else { + jl_safe_printf("Non-native bt entry with tag and header bits 0x%" PRIxPTR "\n", + bt_entry[1].uintptr); + } +} + +//-------------------------------------------------- +// Tools for interactive debugging in gdb +JL_DLLEXPORT void jl_gdblookup(void* ip) +{ + jl_print_native_codeloc((uintptr_t)ip); +} + +// Print backtrace for current exception in catch block JL_DLLEXPORT void jlbacktrace(void) JL_NOTSAFEPOINT { jl_excstack_t *s = jl_get_ptls_states()->current_task->excstack; if (!s) return; size_t bt_size = jl_excstack_bt_size(s, s->top); - uintptr_t *bt_data = jl_excstack_bt_data(s, s->top); - for (size_t i = 0; i < bt_size; ) { - if (bt_data[i] == JL_BT_INTERP_FRAME) { - jl_safe_printf("Interpreter frame (ip: %d)\n", (int)bt_data[i+2]); - jl_static_show(JL_STDERR, (jl_value_t*)bt_data[i+1]); - i += 3; - } else { - jl_gdblookup(bt_data[i] - 1); - i += 1; - } + jl_bt_element_t *bt_data = jl_excstack_bt_data(s, s->top); + for (size_t i = 0; i < bt_size; i += jl_bt_entry_size(bt_data + i)) { + jl_print_bt_entry_codeloc(bt_data + i); } } diff --git a/src/staticdata.c b/src/staticdata.c index b500752ac2f77..f9e0171c61405 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -76,6 +76,7 @@ static void *const _tags[] = { // some Core.Builtin Functions that we want to be able to reference: &jl_builtin_throw, &jl_builtin_is, &jl_builtin_typeof, &jl_builtin_sizeof, &jl_builtin_issubtype, &jl_builtin_isa, &jl_builtin_typeassert, &jl_builtin__apply, + &jl_builtin__apply_iterate, &jl_builtin_isdefined, &jl_builtin_nfields, &jl_builtin_tuple, &jl_builtin_svec, &jl_builtin_getfield, &jl_builtin_setfield, &jl_builtin_fieldtype, &jl_builtin_arrayref, &jl_builtin_const_arrayref, &jl_builtin_arrayset, &jl_builtin_arraysize, @@ -109,7 +110,7 @@ static htable_t fptr_to_id; // This is a manually constructed dual of the fvars array, which would be produced by codegen for Julia code, for C. static const jl_fptr_args_t id_to_fptrs[] = { &jl_f_throw, &jl_f_is, &jl_f_typeof, &jl_f_issubtype, &jl_f_isa, - &jl_f_typeassert, &jl_f__apply, &jl_f__apply_pure, &jl_f__apply_latest, &jl_f_isdefined, + &jl_f_typeassert, &jl_f__apply, &jl_f__apply_iterate, &jl_f__apply_pure, &jl_f__apply_latest, &jl_f_isdefined, &jl_f_tuple, &jl_f_svec, &jl_f_intrinsic_call, &jl_f_invoke_kwsorter, &jl_f_getfield, &jl_f_setfield, &jl_f_fieldtype, &jl_f_nfields, &jl_f_arrayref, &jl_f_const_arrayref, &jl_f_arrayset, &jl_f_arraysize, &jl_f_apply_type, @@ -606,6 +607,7 @@ static void jl_write_values(jl_serializer_state *s) for (i = 0, len = backref_table_numel * 2; i < len; i += 2) { jl_value_t *v = (jl_value_t*)objects_list.items[i]; + JL_GC_PROMISE_ROOTED(v); uintptr_t item = (uintptr_t)objects_list.items[i + 1]; jl_datatype_t *t = (jl_datatype_t*)jl_typeof(v); assert((t->instance == NULL || t->instance == v) && "detected singleton construction corruption"); @@ -807,24 +809,12 @@ static void jl_write_values(jl_serializer_state *s) newdt->ditype = NULL; if (dt->layout != NULL) { size_t nf = dt->layout->nfields; + size_t np = dt->layout->npointers; size_t fieldsize = jl_fielddesc_size(dt->layout->fielddesc_type); - int has_padding = dt->layout->npointers && nf; char *flddesc = (char*)dt->layout; - size_t fldsize = sizeof(jl_datatype_layout_t) + nf * fieldsize; - uintptr_t layout_unaligned = LLT_ALIGN(ios_pos(s->const_data), sizeof(uint32_t)); + size_t fldsize = sizeof(jl_datatype_layout_t) + nf * fieldsize + (np << dt->layout->fielddesc_type); uintptr_t layout = LLT_ALIGN(ios_pos(s->const_data), sizeof(void*)); - if (has_padding) { - if (layout == layout_unaligned) { - layout += sizeof(void*); - layout_unaligned = layout - sizeof(uint32_t); - } - flddesc -= sizeof(uint32_t); - fldsize += sizeof(uint32_t); - write_padding(s->const_data, layout_unaligned - ios_pos(s->const_data)); // realign stream - } - else { - write_padding(s->const_data, layout - ios_pos(s->const_data)); // realign stream - } + write_padding(s->const_data, layout - ios_pos(s->const_data)); // realign stream newdt->layout = NULL; // relocation offset layout /= sizeof(void*); arraylist_push(&s->relocs_list, (void*)(reloc_offset + offsetof(jl_datatype_t, layout))); // relocation location @@ -1396,7 +1386,7 @@ static void jl_save_system_image_to_stream(ios_t *f) JL_DLLEXPORT ios_t *jl_create_system_image(void) { - ios_t *f = (ios_t*)malloc(sizeof(ios_t)); + ios_t *f = (ios_t*)malloc_s(sizeof(ios_t)); ios_mem(f, 0); jl_save_system_image_to_stream(f); return f; diff --git a/src/subtype.c b/src/subtype.c index 5b963dfb6f4e8..3e86bf3d3f2c5 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -145,11 +145,11 @@ static void save_env(jl_stenv_t *e, jl_value_t **root, jl_savedenv_t *se) len++; v = v->prev; } - *root = (jl_value_t*)jl_alloc_svec(len*3); - se->buf = (int8_t*)(len ? malloc(len*2) : NULL); + *root = (jl_value_t*)jl_alloc_svec(len * 3); + se->buf = (int8_t*)(len ? malloc_s(len * 2) : NULL); #ifdef __clang_analyzer__ if (len) - memset(se->buf, 0, len*2); + memset(se->buf, 0, len * 2); #endif int i=0, j=0; v = e->vars; while (v != NULL) { @@ -294,7 +294,7 @@ int jl_obviously_unequal(jl_value_t *a, jl_value_t *b) return obviously_unequal(a, b); } -static int in_union(jl_value_t *u, jl_value_t *x) +static int in_union(jl_value_t *u, jl_value_t *x) JL_NOTSAFEPOINT { if (u == x) return 1; if (!jl_is_uniontype(u)) return 0; @@ -800,6 +800,7 @@ static jl_unionall_t *unalias_unionall(jl_unionall_t *u, jl_stenv_t *e) jl_varbinding_t *btemp = e->vars; // if the var for this unionall (based on identity) already appears somewhere // in the environment, rename to get a fresh var. + JL_GC_PUSH1(&u); while (btemp != NULL) { if (btemp->var == u->var || // outer var can only refer to inner var if bounds changed @@ -810,6 +811,7 @@ static jl_unionall_t *unalias_unionall(jl_unionall_t *u, jl_stenv_t *e) } btemp = btemp->prev; } + JL_GC_POP(); return u; } @@ -819,7 +821,8 @@ struct subtype_unionall_env { }; static int subtype_unionall_callback(struct subtype_unionall_env *env, int8_t R, jl_stenv_t *s, int param) { - JL_GC_PROMISE_ROOTED(env->t); JL_GC_PROMISE_ROOTED(env->ubody); + JL_GC_PROMISE_ROOTED(env->t); + JL_GC_PROMISE_ROOTED(env->ubody); if (R) { return subtype(env->t, env->ubody, s, param); } @@ -833,7 +836,10 @@ static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8 { u = unalias_unionall(u, e); struct subtype_unionall_env env = {t, u->body}; - return with_tvar((tvar_callback)subtype_unionall_callback, (void*)&env, u, R, e, param); + JL_GC_PUSH1(&u); + int res = with_tvar((tvar_callback)subtype_unionall_callback, (void*)&env, u, R, e, param); + JL_GC_POP(); + return res; } // unwrap <=1 layers of UnionAlls, leaving the var in *p1 the body @@ -998,7 +1004,10 @@ static int subtype_tuple_tail(struct subtype_tuple_env *env, int8_t R, jl_stenv_ u = unalias_unionall(u, e); env->vtx = (jl_value_t*)u; // goto loop, but with the tvar introduced - return with_tvar((tvar_callback)subtype_tuple_tail, env, u, 0, e, param); + JL_GC_PUSH1(&u); + int res = with_tvar((tvar_callback)subtype_tuple_tail, env, u, 0, e, param); + JL_GC_POP(); + return res; } env->vtx = xi; } @@ -1018,7 +1027,10 @@ static int subtype_tuple_tail(struct subtype_tuple_env *env, int8_t R, jl_stenv_ u = unalias_unionall(u, e); env->vty = (jl_value_t*)u; // goto loop, but with the tvar introduced - return with_tvar((tvar_callback)subtype_tuple_tail, env, u, 1, e, param); + JL_GC_PUSH1(&u); + int res = with_tvar((tvar_callback)subtype_tuple_tail, env, u, 1, e, param); + JL_GC_POP(); + return res; } env->vty = yi; } @@ -2096,7 +2108,7 @@ static int try_subtype_in_env(jl_value_t *a, jl_value_t *b, jl_stenv_t *e, int R return ret; } -static void set_bound(jl_value_t **bound, jl_value_t *val, jl_tvar_t *v, jl_stenv_t *e) +static void set_bound(jl_value_t **bound, jl_value_t *val, jl_tvar_t *v, jl_stenv_t *e) JL_NOTSAFEPOINT { if (in_union(val, (jl_value_t*)v)) return; @@ -2121,7 +2133,7 @@ static int subtype_in_env_existential(jl_value_t *x, jl_value_t *y, jl_stenv_t * len++; v = v->prev; } - int8_t *rs = (int8_t*)malloc(len); + int8_t *rs = (int8_t*)malloc_s(len); int n = 0; v = e->vars; while (n < len) { @@ -2202,15 +2214,16 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int } else if (bb->concrete || bb->constraintkind == 1) { jl_value_t *ub = R ? intersect_aside(a, bb->ub, e, 1, d) : intersect_aside(bb->ub, a, e, 0, d); + if (ub == jl_bottom_type) + return jl_bottom_type; JL_GC_PUSH1(&ub); - if (ub == jl_bottom_type || + if (!R && !subtype_bounds_in_env(bb->lb, a, e, 0, d)) { // this fixes issue #30122. TODO: better fix for R flag. - (!R && !subtype_bounds_in_env(bb->lb, a, e, 0, d))) { JL_GC_POP(); return jl_bottom_type; } - set_bound(&bb->ub, ub, b, e); JL_GC_POP(); + set_bound(&bb->ub, ub, b, e); return (jl_value_t*)b; } else if (bb->constraintkind == 2) { @@ -2233,7 +2246,7 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int return (jl_value_t*)b; if (ub == a) { if (bb->lb == jl_bottom_type) { - set_bound(&bb->ub, ub, b, e); + set_bound(&bb->ub, a, b, e); return (jl_value_t*)b; } return ub; diff --git a/src/support/dtypes.h b/src/support/dtypes.h index 916a90928cf7d..11f8864e1953e 100644 --- a/src/support/dtypes.h +++ b/src/support/dtypes.h @@ -7,6 +7,9 @@ #include // double include of stddef.h fixes #3421 #include #include // memcpy +#include +#include +#include #if defined(_COMPILER_INTEL_) #include #else @@ -344,5 +347,42 @@ STATIC_INLINE void jl_store_unaligned_i16(void *ptr, uint16_t val) JL_NOTSAFEPOI memcpy(ptr, &val, 2); } +#ifdef _OS_WINDOWS_ +#include +#endif + +STATIC_INLINE void *malloc_s(size_t sz) JL_NOTSAFEPOINT { + int last_errno = errno; +#ifdef _OS_WINDOWS_ + DWORD last_error = GetLastError(); +#endif + void *p = malloc(sz); + if (p == NULL) { + perror("(julia) malloc"); + abort(); + } +#ifdef _OS_WINDOWS_ + SetLastError(last_error); +#endif + errno = last_errno; + return p; +} + +STATIC_INLINE void *realloc_s(void *p, size_t sz) JL_NOTSAFEPOINT { + int last_errno = errno; +#ifdef _OS_WINDOWS_ + DWORD last_error = GetLastError(); +#endif + p = realloc(p, sz); + if (p == NULL) { + perror("(julia) realloc"); + abort(); + } +#ifdef _OS_WINDOWS_ + SetLastError(last_error); +#endif + errno = last_errno; + return p; +} #endif /* DTYPES_H */ diff --git a/src/support/ios.c b/src/support/ios.c index 8cb621604c5d4..464672fafd427 100644 --- a/src/support/ios.c +++ b/src/support/ios.c @@ -537,10 +537,6 @@ int64_t ios_pos(ios_t *s) return fdpos; } -#if defined(_OS_WINDOWS) -#include -#endif /* _OS_WINDOWS_ */ - int ios_trunc(ios_t *s, size_t size) { if (s->bm == bm_mem) { @@ -1022,14 +1018,14 @@ ios_t *ios_stderr = NULL; void ios_init_stdstreams(void) { - ios_stdin = (ios_t*)malloc(sizeof(ios_t)); + ios_stdin = (ios_t*)malloc_s(sizeof(ios_t)); ios_fd(ios_stdin, STDIN_FILENO, 0, 0); - ios_stdout = (ios_t*)malloc(sizeof(ios_t)); + ios_stdout = (ios_t*)malloc_s(sizeof(ios_t)); ios_fd(ios_stdout, STDOUT_FILENO, 0, 0); ios_stdout->bm = bm_line; - ios_stderr = (ios_t*)malloc(sizeof(ios_t)); + ios_stderr = (ios_t*)malloc_s(sizeof(ios_t)); ios_fd(ios_stderr, STDERR_FILENO, 0, 0); ios_stderr->bm = bm_none; } diff --git a/src/support/strtod.c b/src/support/strtod.c index 225f2dbd4c933..318cc3ad1b975 100644 --- a/src/support/strtod.c +++ b/src/support/strtod.c @@ -223,12 +223,11 @@ JL_DLLEXPORT double jl_strtod_c(const char *nptr, char **endptr) char *copy, *c; /* Create a copy of the input, with the '.' converted to the locale-specific decimal point */ - copy = (char *)malloc(end - digits_pos + - 1 + decimal_point_len); + copy = (char *)malloc(end - digits_pos + 1 + decimal_point_len); if (copy == NULL) { *endptr = (char *)nptr; errno = ENOMEM; - return val; + return -1.0; } c = copy; diff --git a/src/support/utf8.c b/src/support/utf8.c index ea7e970be6b51..42a420fb0c499 100644 --- a/src/support/utf8.c +++ b/src/support/utf8.c @@ -509,7 +509,7 @@ size_t u8_vprintf(const char *fmt, va_list ap) if ((intptr_t)cnt < 0) return 0; if (cnt >= sz) { - buf = (char*)malloc(cnt + 1); + buf = (char*)malloc_s(cnt+1); needfree = 1; vsnprintf(buf, cnt+1, fmt, ap); } @@ -517,7 +517,8 @@ size_t u8_vprintf(const char *fmt, va_list ap) nc = u8_toucs(wcs, cnt+1, buf, cnt); wcs[nc] = 0; printf("%ls", (wchar_t*)wcs); - if (needfree) free(buf); + if (needfree) + free(buf); return nc; } diff --git a/src/symbol.c b/src/symbol.c index 533c54c48b7a2..e76f5b5ed2a30 100644 --- a/src/symbol.c +++ b/src/symbol.c @@ -130,16 +130,19 @@ JL_DLLEXPORT jl_sym_t *jl_tagged_gensym(const char *str, int32_t len) char gs_name[14]; if (memchr(str, 0, len)) jl_exceptionf(jl_argumenterror_type, "Symbol name may not contain \\0"); - char *name = (char*) (len >= 256 ? malloc(sizeof(gs_name)+len+3) : - alloca(sizeof(gs_name)+len+3)); + char *name = (char*) (len >= 256 ? malloc_s(sizeof(gs_name) + len + 3) : + alloca(sizeof(gs_name) + len + 3)); char *n; - name[0] = '#'; name[1] = '#'; name[2+len] = '#'; - memcpy(name+2, str, len); + name[0] = '#'; + name[1] = '#'; + name[2 + len] = '#'; + memcpy(name + 2, str, len); uint32_t ctr = jl_atomic_fetch_add(&gs_ctr, 1); n = uint2str(gs_name, sizeof(gs_name), ctr, 10); - memcpy(name+3+len, n, sizeof(gs_name)-(n-gs_name)); - jl_sym_t *sym = _jl_symbol(name, len+3+sizeof(gs_name)-(n-gs_name)-1); - if (len >= 256) free(name); + memcpy(name + 3 + len, n, sizeof(gs_name) - (n - gs_name)); + jl_sym_t *sym = _jl_symbol(name, len + 3 + sizeof(gs_name) - (n - gs_name)- 1); + if (len >= 256) + free(name); return sym; } diff --git a/src/sys.c b/src/sys.c index 52f2397347b9e..74cfff8fde7a9 100644 --- a/src/sys.c +++ b/src/sys.c @@ -549,7 +549,7 @@ JL_DLLEXPORT const char *jl_pathname_for_handle(void *handle) #elif defined(_OS_WINDOWS_) - wchar_t *pth16 = (wchar_t*)malloc(32768 * sizeof(*pth16)); // max long path length + wchar_t *pth16 = (wchar_t*)malloc_s(32768 * sizeof(*pth16)); // max long path length DWORD n16 = GetModuleFileNameW((HMODULE)handle,pth16,32768); if (n16 <= 0) { free(pth16); @@ -561,7 +561,7 @@ JL_DLLEXPORT const char *jl_pathname_for_handle(void *handle) free(pth16); return NULL; } - char *filepath = (char*)malloc(++n8); + char *filepath = (char*)malloc_s(++n8); if (!WideCharToMultiByte(CP_UTF8, 0, pth16, -1, filepath, n8, NULL, NULL)) { free(pth16); free(filepath); diff --git a/src/task.c b/src/task.c index aeaa8af71e8ed..7cb6bc86f0fc6 100644 --- a/src/task.c +++ b/src/task.c @@ -55,7 +55,6 @@ static inline void sanitizer_finish_switch_fiber(void) {} #if defined(_OS_WINDOWS_) volatile int jl_in_stackwalk = 0; #else -#include // mmap #ifdef JL_HAVE_UCONTEXT #include #endif @@ -148,8 +147,9 @@ static void NOINLINE JL_NORETURN restore_stack(jl_task_t *t, jl_ptls_t ptls, cha } restore_stack(t, ptls, p); // pass p to ensure the compiler can't tailcall this or avoid the alloca } - assert(t->stkbuf != NULL); - memcpy_a16((uint64_t*)_x, (uint64_t*)t->stkbuf, nb); // destroys all but the current stackframe + void *_y = t->stkbuf; + assert(_x != NULL && _y != NULL); + memcpy_a16((uint64_t*)_x, (uint64_t*)_y, nb); // destroys all but the current stackframe sanitizer_start_switch_fiber(t->stkbuf, t->bufsz); jl_set_fiber(&t->ctx); @@ -159,8 +159,9 @@ static void restore_stack2(jl_task_t *t, jl_ptls_t ptls, jl_task_t *lastt) { size_t nb = t->copy_stack; char *_x = (char*)ptls->stackbase - nb; - assert(t->stkbuf != NULL); - memcpy_a16((uint64_t*)_x, (uint64_t*)t->stkbuf, nb); // destroys all but the current stackframe + void *_y = t->stkbuf; + assert(_x != NULL && _y != NULL); + memcpy_a16((uint64_t*)_x, (uint64_t*)_y, nb); // destroys all but the current stackframe sanitizer_start_switch_fiber(t->stkbuf, t->bufsz); jl_swap_fiber(&lastt->ctx, &t->ctx); sanitizer_finish_switch_fiber(); @@ -262,9 +263,8 @@ static void ctx_switch(jl_ptls_t ptls, jl_task_t **pt) arraylist_new(locks, 0); } - int started = t->started; int killed = (lastt->state == done_sym || lastt->state == failed_sym); - if (!started && !t->copy_stack) { + if (!t->started && !t->copy_stack) { // may need to allocate the stack if (t->stkbuf == NULL) { t->stkbuf = jl_alloc_fiber(&t->ctx, &t->bufsz, t); @@ -325,7 +325,7 @@ static void ctx_switch(jl_ptls_t ptls, jl_task_t **pt) // after restoring the stack lastt_ctx = NULL; #endif - if (started) { + if (t->started) { #ifdef COPY_STACKS if (t->copy_stack) { if (lastt_ctx) @@ -518,7 +518,7 @@ JL_DLLEXPORT void jl_rethrow_other(jl_value_t *e JL_MAYBE_UNROOTED) if (!excstack || excstack->top == 0) jl_error("rethrow(exc) not allowed outside a catch block"); // overwrite exception on top of stack. see jl_excstack_exception - jl_excstack_raw(excstack)[excstack->top-1] = (uintptr_t)e; + jl_excstack_raw(excstack)[excstack->top-1].jlvalue = e; JL_GC_PROMISE_ROOTED(e); throw_internal(NULL); } @@ -673,7 +673,7 @@ STATIC_OR_JS void NOINLINE JL_NORETURN start_task(void) if (t->exception != jl_nothing) { record_backtrace(ptls, 0); jl_push_excstack(&t->excstack, t->exception, - ptls->bt_data, ptls->bt_size); + ptls->bt_data, ptls->bt_size); res = t->exception; } else { diff --git a/src/threading.c b/src/threading.c index b92ce7776be71..2ec6d9ddf2444 100644 --- a/src/threading.c +++ b/src/threading.c @@ -71,7 +71,7 @@ JL_DLLEXPORT JL_CONST_FUNC jl_ptls_t (jl_get_ptls_states)(void) JL_GLOBALLY_ROOT } // This is only used after the tls is already initialized on the thread -static JL_CONST_FUNC jl_ptls_t jl_get_ptls_states_fast(void) +static JL_CONST_FUNC jl_ptls_t jl_get_ptls_states_fast(void) JL_NOTSAFEPOINT { return (jl_ptls_t)pthread_getspecific(jl_tls_key); } @@ -113,7 +113,24 @@ BOOLEAN WINAPI DllMain(IN HINSTANCE hDllHandle, IN DWORD nReason, JL_DLLEXPORT JL_CONST_FUNC jl_ptls_t (jl_get_ptls_states)(void) JL_GLOBALLY_ROOTED { - return (jl_ptls_t)TlsGetValue(jl_tls_key); +#if defined(_CPU_X86_64_) + DWORD *plast_error = (DWORD*)(__readgsqword(0x30) + 0x68); + DWORD last_error = *plast_error; +#elif defined(_CPU_X86_) + DWORD *plast_error = (DWORD*)(__readfsdword(0x18) + 0x34); + DWORD last_error = *plast_error; +#else + DWORD last_error = GetLastError(); +#endif + jl_ptls_t state = (jl_ptls_t)TlsGetValue(jl_tls_key); +#if defined(_CPU_X86_64_) + *plast_error = last_error; +#elif defined(_CPU_X86_) + *plast_error = last_error; +#else + SetLastError(last_error); +#endif + return state; } jl_get_ptls_states_func jl_get_ptls_states_getter(void) @@ -262,14 +279,10 @@ void jl_init_threadtls(int16_t tid) sizeof(size_t)); } ptls->defer_signal = 0; - void *bt_data = malloc(sizeof(uintptr_t) * (JL_MAX_BT_SIZE + 1)); - if (bt_data == NULL) { - jl_printf(JL_STDERR, "could not allocate backtrace buffer\n"); - gc_debug_critical_error(); - abort(); - } - memset(bt_data, 0, sizeof(uintptr_t) * (JL_MAX_BT_SIZE + 1)); - ptls->bt_data = (uintptr_t*)bt_data; + jl_bt_element_t *bt_data = (jl_bt_element_t*) + malloc_s(sizeof(jl_bt_element_t) * (JL_MAX_BT_SIZE + 1)); + memset(bt_data, 0, sizeof(jl_bt_element_t) * (JL_MAX_BT_SIZE + 1)); + ptls->bt_data = bt_data; ptls->sig_exception = NULL; ptls->previous_exception = NULL; #ifdef _OS_WINDOWS_ @@ -447,7 +460,7 @@ void jl_start_threads(void) uv_barrier_init(&thread_init_done, nthreads); for (i = 1; i < nthreads; ++i) { - jl_threadarg_t *t = (jl_threadarg_t*)malloc(sizeof(jl_threadarg_t)); // ownership will be passed to the thread + jl_threadarg_t *t = (jl_threadarg_t*)malloc_s(sizeof(jl_threadarg_t)); // ownership will be passed to the thread t->tid = i; t->barrier = &thread_init_done; uv_thread_create(&uvtid, jl_threadfun, t); diff --git a/src/timing.c b/src/timing.c index 56c40d9da38d9..248b9e87a8b23 100644 --- a/src/timing.c +++ b/src/timing.c @@ -36,7 +36,7 @@ void jl_print_timings(void) void jl_init_timing(void) { - jl_root_timing = (jl_timing_block_t*)malloc(sizeof(jl_timing_block_t)); + jl_root_timing = (jl_timing_block_t*)malloc_s(sizeof(jl_timing_block_t)); _jl_timing_block_init(jl_root_timing, JL_TIMING_ROOT); jl_root_timing->prev = NULL; } diff --git a/stdlib/.gitignore b/stdlib/.gitignore index a55686cd4ef25..2f7f49e24be1a 100644 --- a/stdlib/.gitignore +++ b/stdlib/.gitignore @@ -1,3 +1,5 @@ /srccache /Pkg-* /Pkg +/Statistics-* +/Statistics diff --git a/stdlib/Dates/src/periods.jl b/stdlib/Dates/src/periods.jl index 5b6d609ec7936..db2b773ed661d 100644 --- a/stdlib/Dates/src/periods.jl +++ b/stdlib/Dates/src/periods.jl @@ -24,7 +24,7 @@ for period in (:Year, :Month, :Week, :Day, :Hour, :Minute, :Second, :Millisecond # Period accessors typs = period in (:Microsecond, :Nanosecond) ? ["Time"] : period in (:Hour, :Minute, :Second, :Millisecond) ? ["Time", "DateTime"] : ["Date", "DateTime"] - reference = period == :Week ? " For details see [`$accessor_str(::Union{Date, DateTime})`](@ref)." : "" + reference = period === :Week ? " For details see [`$accessor_str(::Union{Date, DateTime})`](@ref)." : "" for typ_str in typs @eval begin @doc """ diff --git a/stdlib/DelimitedFiles/src/DelimitedFiles.jl b/stdlib/DelimitedFiles/src/DelimitedFiles.jl index 857066cbe1cb4..38dffbff17632 100644 --- a/stdlib/DelimitedFiles/src/DelimitedFiles.jl +++ b/stdlib/DelimitedFiles/src/DelimitedFiles.jl @@ -455,7 +455,7 @@ function readdlm_string(sbuff::String, dlm::AbstractChar, T::Type, eol::Abstract dims = dlm_parse(sbuff, eol, dlm, '"', comment_char, ign_empty, quotes, comments, skipstart, skipblanks, offset_handler) break catch ex - if isa(ex, TypeError) && (ex.func == :store_cell) + if isa(ex, TypeError) && (ex.func === :store_cell) T = ex.expected else rethrow() @@ -510,7 +510,7 @@ function dlm_fill(T::DataType, offarr::Vector{Vector{Int}}, dims::NTuple{2,Integ end return result(dh) catch ex - isa(ex, TypeError) && (ex.func == :store_cell) && (return dlm_fill(ex.expected, offarr, dims, has_header, sbuff, auto, eol)) + isa(ex, TypeError) && (ex.func === :store_cell) && (return dlm_fill(ex.expected, offarr, dims, has_header, sbuff, auto, eol)) error("at row $row, column $col : $ex") end end @@ -712,7 +712,7 @@ function dlm_parse(dbuff::String, eol::D, dlm::D, qchar::D, cchar::D, end end catch ex - if isa(ex, TypeError) && (ex.func == :store_cell) + if isa(ex, TypeError) && (ex.func === :store_cell) rethrow() else error("at row $(nrows+1), column $col : $ex)") diff --git a/stdlib/Distributed/src/Distributed.jl b/stdlib/Distributed/src/Distributed.jl index 504d21f97406f..dbeaf6282f920 100644 --- a/stdlib/Distributed/src/Distributed.jl +++ b/stdlib/Distributed/src/Distributed.jl @@ -10,7 +10,7 @@ import Base: getindex, wait, put!, take!, fetch, isready, push!, length, hash, ==, kill, close, isopen, showerror # imports for use -using Base: Process, Semaphore, JLOptions, AnyDict, buffer_writes, @sync_add, +using Base: Process, Semaphore, JLOptions, buffer_writes, @sync_add, VERSION_STRING, binding_module, atexit, julia_exename, julia_cmd, AsyncGenerator, acquire, release, invokelatest, shell_escape_posixly, uv_error, something, notnothing, isbuffered @@ -91,7 +91,6 @@ include("macros.jl") # @spawn and friends include("workerpool.jl") include("pmap.jl") include("managers.jl") # LocalManager and SSHManager -include("precompile.jl") function __init__() init_parallel() diff --git a/stdlib/Distributed/src/cluster.jl b/stdlib/Distributed/src/cluster.jl index 47b2fc2f3a082..e7b3086362e67 100644 --- a/stdlib/Distributed/src/cluster.jl +++ b/stdlib/Distributed/src/cluster.jl @@ -146,7 +146,7 @@ end function check_worker_state(w::Worker) if w.state == W_CREATED if !isclusterlazy() - if PGRP.topology == :all_to_all + if PGRP.topology === :all_to_all # Since higher pids connect with lower pids, the remote worker # may not have connected to us yet. Wait for some time. wait_for_conn(w) @@ -445,10 +445,10 @@ function addprocs(manager::ClusterManager; kwargs...) end function addprocs_locked(manager::ClusterManager; kwargs...) - params = merge(default_addprocs_params(), AnyDict(kwargs)) + params = merge(default_addprocs_params(), Dict{Symbol,Any}(kwargs)) topology(Symbol(params[:topology])) - if PGRP.topology != :all_to_all + if PGRP.topology !== :all_to_all params[:lazy] = false end @@ -510,7 +510,7 @@ function set_valid_processes(plist::Array{Int}) end end -default_addprocs_params() = AnyDict( +default_addprocs_params() = Dict{Symbol,Any}( :topology => :all_to_all, :dir => pwd(), :exename => joinpath(Sys.BINDIR, julia_exename()), @@ -616,7 +616,7 @@ function create_worker(manager, wconfig) # - On master, receiving a JoinCompleteMsg triggers rr_ntfy_join (signifies that worker setup is complete) join_list = [] - if PGRP.topology == :all_to_all + if PGRP.topology === :all_to_all # need to wait for lower worker pids to have completed connecting, since the numerical value # of pids is relevant to the connection process, i.e., higher pids connect to lower pids and they # require the value of config.connect_at which is set only upon connection completion @@ -627,7 +627,7 @@ function create_worker(manager, wconfig) end end - elseif PGRP.topology == :custom + elseif PGRP.topology === :custom # wait for requested workers to be up before connecting to them. filterfunc(x) = (x.id != 1) && isdefined(x, :config) && (notnothing(x.config.ident) in something(wconfig.connect_idents, [])) @@ -832,7 +832,7 @@ julia> workers() ``` """ function nprocs() - if myid() == 1 || (PGRP.topology == :all_to_all && !isclusterlazy()) + if myid() == 1 || (PGRP.topology === :all_to_all && !isclusterlazy()) n = length(PGRP.workers) # filter out workers in the process of being setup/shutdown. for jw in PGRP.workers @@ -885,7 +885,7 @@ julia> procs() ``` """ function procs() - if myid() == 1 || (PGRP.topology == :all_to_all && !isclusterlazy()) + if myid() == 1 || (PGRP.topology === :all_to_all && !isclusterlazy()) # filter out workers in the process of being setup/shutdown. return Int[x.id for x in PGRP.workers if isa(x, LocalProcess) || (x.state == W_CONNECTED)] else @@ -894,7 +894,7 @@ function procs() end function id_in_procs(id) # faster version of `id in procs()` - if myid() == 1 || (PGRP.topology == :all_to_all && !isclusterlazy()) + if myid() == 1 || (PGRP.topology === :all_to_all && !isclusterlazy()) for x in PGRP.workers if (x.id::Int) == id && (isa(x, LocalProcess) || (x::Worker).state == W_CONNECTED) return true @@ -1120,7 +1120,7 @@ function deregister_worker(pg, pid) if myid() == 1 && (myrole() === :master) && isdefined(w, :config) # Notify the cluster manager of this workers death manage(w.manager, w.id, w.config, :deregister) - if PGRP.topology != :all_to_all || isclusterlazy() + if PGRP.topology !== :all_to_all || isclusterlazy() for rpid in workers() try remote_do(deregister_worker, rpid, pid) diff --git a/stdlib/Distributed/src/managers.jl b/stdlib/Distributed/src/managers.jl index 62141b1fd1474..1a05f957dca9f 100644 --- a/stdlib/Distributed/src/managers.jl +++ b/stdlib/Distributed/src/managers.jl @@ -226,7 +226,7 @@ end function manage(manager::SSHManager, id::Integer, config::WorkerConfig, op::Symbol) - if op == :interrupt + if op === :interrupt ospid = config.ospid if ospid !== nothing host = notnothing(config.host) @@ -340,7 +340,7 @@ function launch(manager::LocalManager, params::Dict, launched::Array, c::Conditi end function manage(manager::LocalManager, id::Integer, config::WorkerConfig, op::Symbol) - if op == :interrupt + if op === :interrupt kill(config.process, 2) end end diff --git a/stdlib/Distributed/src/precompile.jl b/stdlib/Distributed/src/precompile.jl deleted file mode 100644 index 6b3cb367efc92..0000000000000 --- a/stdlib/Distributed/src/precompile.jl +++ /dev/null @@ -1,191 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -precompile(Tuple{typeof(Base.empty!), Base.Dict{Int64, Union{Distributed.Worker, Distributed.LocalProcess}}}) -precompile(Tuple{typeof(Core.Compiler.isbits), Distributed.DefaultClusterManager}) -precompile(Tuple{typeof(Distributed.init_worker), String, Distributed.DefaultClusterManager}) -precompile(Tuple{typeof(Distributed.local_remotecall_thunk), typeof(Distributed.set_valid_processes), Tuple{Array{Int64, 1}}, Array{Any, 1}}) -precompile(Tuple{typeof(Distributed.remote_do), typeof(Distributed.set_valid_processes), Distributed.Worker, Array{Int64, 1}}) -precompile(Tuple{typeof(Distributed.remote_do), typeof(Distributed.set_valid_processes), Distributed.LocalProcess, Array{Int64, 1}}) -precompile(Tuple{typeof(Distributed.default_addprocs_params)}) -precompile(Tuple{typeof(Distributed.topology), Symbol}) -precompile(Tuple{typeof(Base.popfirst!), Array{Distributed.WorkerConfig, 1}}) -precompile(Tuple{typeof(Distributed.workers)}) -precompile(Tuple{typeof(Distributed.check_addprocs_args), Array{Any, 1}}) -precompile(Tuple{Type{Distributed.SSHManager}, Array{Any, 1}}) -precompile(Tuple{typeof(Distributed.check_master_connect)}) -precompile(Tuple{typeof(Distributed.terminate_all_workers)}) -precompile(Tuple{typeof(Distributed.local_remotecall_thunk), typeof(Base.exit), Tuple{}, Array{Any, 1}}) -precompile(Tuple{typeof(Distributed.remote_do), typeof(Base.exit), Distributed.Worker}) -precompile(Tuple{typeof(Distributed.remote_do), typeof(Base.exit), Distributed.LocalProcess}) -precompile(Tuple{typeof(Distributed.set_worker_state), Distributed.Worker, Distributed.WorkerState}) -precompile(Tuple{typeof(Distributed.set_worker_state), Distributed.LocalProcess, Distributed.WorkerState}) -precompile(Tuple{typeof(Distributed.interrupt), Array{Int64, 1}}) -precompile(Tuple{typeof(Distributed.flush_gc_msgs)}) -precompile(Tuple{typeof(Distributed.addprocs), Int64}) -precompile(Tuple{Type{Distributed.WorkerConfig}}) -precompile(Tuple{typeof(Distributed.launch), Distributed.LocalManager, Base.Dict{Any, Any}, Array{Distributed.WorkerConfig, 1}, Base.Condition}) -precompile(Tuple{typeof(Distributed.start_worker), Base.PipeEndpoint, String}) -precompile(Tuple{typeof(Distributed.socket_reuse_port)}) -precompile(Tuple{typeof(Distributed.flush_gc_msgs)}) -precompile(Tuple{typeof(Sockets.nagle), Sockets.TCPServer, Bool}) -precompile(Tuple{typeof(Sockets.quickack), Sockets.TCPServer, Bool}) -precompile(Tuple{typeof(Distributed.next_tunnel_port)}) -precompile(Tuple{typeof(Base._delete!), Base.Dict{Int64, Union{Distributed.Worker, Distributed.LocalProcess}}, Int64}) -precompile(Tuple{typeof(Distributed.send_msg_), Distributed.Worker, Distributed.MsgHeader, Distributed.JoinPGRPMsg, Bool}) -precompile(Tuple{typeof(Distributed.send_msg_now), Distributed.Worker, Distributed.MsgHeader, Distributed.JoinPGRPMsg}) -precompile(Tuple{typeof(Distributed.connect_w2w), Int64, Distributed.WorkerConfig}) -precompile(Tuple{typeof(Distributed.create_worker), Distributed.LocalManager, Distributed.WorkerConfig}) -precompile(Tuple{typeof(Distributed.setup_launched_worker), Distributed.LocalManager, Distributed.WorkerConfig, Array{Int64, 1}}) -precompile(Tuple{typeof(Sockets.connect), Distributed.LocalManager, Int64, Distributed.WorkerConfig}) -precompile(Tuple{typeof(Distributed.read_worker_host_port), Base.Pipe}) -precompile(Tuple{typeof(Distributed.parse_connection_info), String}) -precompile(Tuple{typeof(Distributed.connect_to_worker), Base.SubString{String}, Int16}) -precompile(Tuple{typeof(Distributed.process_messages), Sockets.TCPSocket, Sockets.TCPSocket, Bool}) -precompile(Tuple{typeof(Distributed.worker_id_from_socket), Sockets.TCPSocket}) -precompile(Tuple{Type{Distributed.ClusterSerializer{Sockets.TCPSocket}}, Sockets.TCPSocket}) -precompile(Tuple{typeof(Distributed.send_msg_), Distributed.Worker, Distributed.MsgHeader, Distributed.ResultMsg, Bool}) -precompile(Tuple{typeof(Distributed.send_msg_now), Distributed.Worker, Distributed.MsgHeader, Distributed.ResultMsg}) -precompile(Tuple{typeof(Distributed.register_worker_streams), Distributed.Worker}) -precompile(Tuple{typeof(Distributed.register_worker_streams), Distributed.LocalProcess}) -precompile(Tuple{Type{Distributed.ClusterSerializer{Sockets.TCPSocket}}, Sockets.TCPSocket}) -precompile(Tuple{typeof(Distributed.worker_id_from_socket), Sockets.TCPSocket}) -precompile(Tuple{typeof(Base.convert), Type{Distributed.ClusterSerializer{I} where I<:IO}, Distributed.ClusterSerializer{Sockets.TCPSocket}}) -precompile(Tuple{typeof(Base.convert), Type{Distributed.ClusterManager}, Distributed.LocalManager}) -precompile(Tuple{typeof(Base.convert), Type{Distributed.WorkerConfig}, Distributed.WorkerConfig}) -precompile(Tuple{typeof(Base.get), Base.Dict{Any, Any}, Distributed.RRID, Bool}) -precompile(Tuple{typeof(Base.ht_keyindex), Base.Dict{Any, Any}, Distributed.RRID}) -precompile(Tuple{typeof(Distributed.local_remotecall_thunk), typeof(Distributed.rmprocs), Tuple{Int64}, Array{Any, 1}}) -precompile(Tuple{typeof(Distributed.remote_do), typeof(Distributed.rmprocs), Distributed.Worker, Int64}) -precompile(Tuple{typeof(Distributed.remote_do), typeof(Distributed.rmprocs), Distributed.LocalProcess, Int64}) -precompile(Tuple{Type{Distributed.ResultMsg}, Distributed.RemoteException}) -precompile(Tuple{Type{Distributed.ResultMsg}, Symbol}) -precompile(Tuple{typeof(Distributed.send_msg_now), Sockets.TCPSocket, Distributed.MsgHeader, Distributed.ResultMsg}) -precompile(Tuple{typeof(Base._delete!), Base.Dict{Int64, Union{Distributed.Worker, Distributed.LocalProcess}}, Int64}) -precompile(Tuple{typeof(Distributed.def_rv_channel)}) -precompile(Tuple{typeof(Base.setindex!), Base.Dict{Any, Any}, Distributed.RemoteValue, Distributed.RRID}) -precompile(Tuple{typeof(Base.ht_keyindex2!), Base.Dict{Any, Any}, Distributed.RRID}) -precompile(Tuple{typeof(Base._setindex!), Base.Dict{Any, Any}, Distributed.RemoteValue, Distributed.RRID, Int64}) -precompile(Tuple{typeof(Base.notify), Base.Condition, Distributed.ProcessExitedException, Bool, Bool}) -precompile(Tuple{typeof(Distributed.process_messages), Sockets.TCPSocket, Sockets.TCPSocket, Bool}) -precompile(Tuple{typeof(Base.pop!), Base.Dict{Int64, Union{Distributed.Worker, Distributed.LocalProcess}}, Int64, Nothing}) -precompile(Tuple{typeof(Distributed.send_connection_hdr), Distributed.Worker, Bool}) -precompile(Tuple{typeof(Distributed.deregister_worker), Distributed.ProcessGroup, Int64}) -precompile(Tuple{typeof(Distributed.process_hdr), Sockets.TCPSocket, Bool}) -precompile(Tuple{typeof(Distributed.deserialize_msg), Distributed.ClusterSerializer{Sockets.TCPSocket}}) -precompile(Tuple{typeof(Distributed.null_id), Distributed.RRID}) -precompile(Tuple{typeof(Distributed.deliver_result), Sockets.TCPSocket, Symbol, Distributed.RRID, Distributed.RemoteException}) -precompile(Tuple{typeof(Sockets.nagle), Sockets.TCPSocket, Bool}) -precompile(Tuple{typeof(Sockets.quickack), Sockets.TCPSocket, Bool}) -precompile(Tuple{typeof(Distributed.message_handler_loop), Sockets.TCPSocket, Sockets.TCPSocket, Bool}) -precompile(Tuple{typeof(Distributed.process_tcp_streams), Sockets.TCPSocket, Sockets.TCPSocket, Bool}) -precompile(Tuple{Type{Distributed.JoinPGRPMsg}, Int64, Array{Union{Tuple{Any, Int64}, Tuple{Tuple{}, Any, Bool}}, 1}, Symbol, Bool}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Distributed.JoinPGRPMsg}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Int64}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Array{Union{Tuple{Any, Int64}, Tuple{Tuple{}, Any, Bool}}, 1}}) -precompile(Tuple{typeof(Serialization.should_send_whole_type), Distributed.ClusterSerializer{Sockets.TCPSocket}, Type{Any}}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Core.SimpleVector}) -precompile(Tuple{typeof(Serialization.serialize_type_data), Distributed.ClusterSerializer{Sockets.TCPSocket}, Type{Any}, Bool}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Type{Any}}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Tuple{Int64}}) -precompile(Tuple{typeof(Serialization.serialize_cycle), Distributed.ClusterSerializer{Sockets.TCPSocket}, Type{Union{Tuple{Any, Int64}, Tuple{Tuple{}, Any, Bool}}}}) -precompile(Tuple{typeof(Serialization.serialize_type), Distributed.ClusterSerializer{Sockets.TCPSocket}, DataType}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Bool}) -precompile(Tuple{typeof(Distributed.serialize_global_from_main), Distributed.ClusterSerializer{Sockets.TCPSocket}, Symbol}) -precompile(Tuple{typeof(Serialization.serialize_mod_names), Distributed.ClusterSerializer{Sockets.TCPSocket}, Module}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Symbol}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Module}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Array{Any, 1}}) -precompile(Tuple{typeof(Serialization.serialize_cycle), Distributed.ClusterSerializer{Sockets.TCPSocket}, Core.TypeName}) -precompile(Tuple{typeof(Serialization.serialize_typename), Distributed.ClusterSerializer{Sockets.TCPSocket}, Core.TypeName}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Array{Symbol, 1}}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Core.TypeName}) -precompile(Tuple{typeof(Serialization.serialize_cycle_header), Distributed.ClusterSerializer{Sockets.TCPSocket}, Type{Union{Tuple{Any, Int64}, Tuple{Tuple{}, Any, Bool}}}}) -precompile(Tuple{typeof(Serialization.serialize_any), Distributed.ClusterSerializer{Sockets.TCPSocket}, Type{Union{Tuple{Any, Int64}, Tuple{Tuple{}, Any, Bool}}}}) -precompile(Tuple{typeof(Distributed.send_msg_), Distributed.Worker, Distributed.MsgHeader, Distributed.ResultMsg, Bool}) -precompile(Tuple{typeof(Distributed.send_msg_now), Distributed.Worker, Distributed.MsgHeader, Distributed.ResultMsg}) -precompile(Tuple{typeof(Distributed.local_remotecall_thunk), typeof(Distributed.rmprocs), Tuple{Int64}, Array{Any, 1}}) -precompile(Tuple{typeof(Distributed.remote_do), typeof(Distributed.rmprocs), Distributed.Worker, Int64}) -precompile(Tuple{typeof(Distributed.remote_do), typeof(Distributed.rmprocs), Distributed.LocalProcess, Int64}) -precompile(Tuple{Type{Distributed.ResultMsg}, Distributed.RemoteException}) -precompile(Tuple{Type{Distributed.ResultMsg}, Symbol}) -precompile(Tuple{typeof(Distributed.send_msg_now), Sockets.TCPSocket, Distributed.MsgHeader, Distributed.ResultMsg}) -precompile(Tuple{typeof(Base.pop!), Base.Dict{Int64, Union{Distributed.Worker, Distributed.LocalProcess}}, Int64, Nothing}) -precompile(Tuple{typeof(Distributed.deregister_worker), Distributed.ProcessGroup, Int64}) -precompile(Tuple{typeof(Distributed.process_hdr), Sockets.TCPSocket, Bool}) -precompile(Tuple{typeof(Distributed.null_id), Distributed.RRID}) -precompile(Tuple{typeof(Distributed.deliver_result), Sockets.TCPSocket, Symbol, Distributed.RRID, Distributed.RemoteException}) -precompile(Tuple{typeof(Distributed.message_handler_loop), Sockets.TCPSocket, Sockets.TCPSocket, Bool}) -precompile(Tuple{typeof(Distributed.process_tcp_streams), Sockets.TCPSocket, Sockets.TCPSocket, Bool}) -precompile(Tuple{typeof(Serialization.deserialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Type{Union}}) -precompile(Tuple{typeof(Serialization.deserialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Type{Module}}) -precompile(Tuple{typeof(Serialization.deserialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Type{Core.SimpleVector}}) -precompile(Tuple{Type{Distributed.JoinPGRPMsg}, Int64, Array{Union{Tuple{Any, Int64}, Tuple{Tuple{}, Any, Bool}}, 1}, Symbol, Bool}) -precompile(Tuple{typeof(Distributed.handle_msg), Distributed.JoinPGRPMsg, Distributed.MsgHeader, Sockets.TCPSocket, Sockets.TCPSocket, Base.VersionNumber}) -precompile(Tuple{Type{Distributed.WorkerConfig}}) -precompile(Tuple{typeof(Distributed.send_msg_), Distributed.Worker, Distributed.MsgHeader, Distributed.JoinCompleteMsg, Bool}) -precompile(Tuple{typeof(Distributed.send_msg_now), Distributed.Worker, Distributed.MsgHeader, Distributed.JoinCompleteMsg}) -precompile(Tuple{typeof(Distributed.register_worker_streams), Distributed.Worker}) -precompile(Tuple{typeof(Distributed.register_worker_streams), Distributed.LocalProcess}) -precompile(Tuple{typeof(Base.convert), Type{Distributed.ClusterSerializer{I} where I<:IO}, Distributed.ClusterSerializer{Sockets.TCPSocket}}) -precompile(Tuple{typeof(Base.convert), Type{Distributed.ClusterManager}, Distributed.DefaultClusterManager}) -precompile(Tuple{typeof(Base.convert), Type{Distributed.WorkerConfig}, Distributed.WorkerConfig}) -precompile(Tuple{typeof(Distributed.send_connection_hdr), Distributed.Worker, Bool}) -precompile(Tuple{typeof(Distributed.manage), Distributed.LocalManager, Int64, Distributed.WorkerConfig, Symbol}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Distributed.JoinCompleteMsg}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Int64}) -precompile(Tuple{typeof(Serialization.deserialize), Distributed.ClusterSerializer{Sockets.TCPSocket}}) -precompile(Tuple{typeof(Serialization.deserialize_cycle), Distributed.ClusterSerializer{Sockets.TCPSocket}, Expr}) -precompile(Tuple{typeof(Serialization.handle_deserialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Int32}) -precompile(Tuple{typeof(Serialization.deserialize_array), Distributed.ClusterSerializer{Sockets.TCPSocket}}) -precompile(Tuple{typeof(Serialization.deserialize_datatype), Distributed.ClusterSerializer{Sockets.TCPSocket}}) -precompile(Tuple{typeof(Serialization.deserialize_expr), Distributed.ClusterSerializer{Sockets.TCPSocket}, Int64}) -precompile(Tuple{typeof(Serialization.deserialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Type{Int64}}) -precompile(Tuple{Type{Distributed.JoinCompleteMsg}, Int64, Int64}) -precompile(Tuple{typeof(Distributed.handle_msg), Distributed.JoinCompleteMsg, Distributed.MsgHeader, Sockets.TCPSocket, Sockets.TCPSocket, Base.VersionNumber}) -precompile(Tuple{typeof(Base.hash), Distributed.RemoteChannel{Base.Channel{Any}}, UInt64}) -precompile(Tuple{typeof(Base.ht_keyindex), Base.Dict{WeakRef, Nothing}, Distributed.RemoteChannel{Base.Channel{Any}}}) -precompile(Tuple{typeof(Distributed.remotecall_fetch), typeof(Distributed.put_ref), Distributed.Worker, Distributed.RRID, Distributed.WorkerPool}) -precompile(Tuple{typeof(Distributed.remotecall_fetch), typeof(Distributed.put_ref), Distributed.LocalProcess, Distributed.RRID, Distributed.WorkerPool}) -precompile(Tuple{typeof(Base.finalizer), Distributed.RemoteChannel{Base.Channel{Any}}, typeof(Distributed.finalize_ref)}) -precompile(Tuple{typeof(Distributed.test_existing_ref), Distributed.RemoteChannel{Base.Channel{Any}}}) -precompile(Tuple{Type{Distributed.RemoteChannel{T} where T<:Base.AbstractChannel}, Int64}) -precompile(Tuple{Type{Distributed.WorkerPool}}) -precompile(Tuple{typeof(Distributed.default_worker_pool)}) -precompile(Tuple{typeof(Distributed.call_on_owner), typeof(Distributed.put_ref), Distributed.RemoteChannel{Base.Channel{Any}}, Distributed.WorkerPool}) -precompile(Tuple{typeof(Distributed.put_ref), Distributed.RRID, Distributed.WorkerPool}) -precompile(Tuple{typeof(Base.put!), Distributed.RemoteValue, Distributed.WorkerPool}) -precompile(Tuple{typeof(Base.put!), Base.Channel{Any}, Distributed.WorkerPool}) -precompile(Tuple{typeof(Base.put_buffered), Base.Channel{Any}, Distributed.WorkerPool}) -precompile(Tuple{typeof(Base.put_unbuffered), Base.Channel{Any}, Distributed.WorkerPool}) -precompile(Tuple{typeof(Base.push!), Distributed.WorkerPool, Int64}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Distributed.RemoteDoMsg}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, typeof(Distributed.set_valid_processes)}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Tuple{Array{Int64, 1}}}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Array{Int64, 1}}) -precompile(Tuple{typeof(Serialization.deserialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Type{typeof(Distributed.set_valid_processes)}}) -precompile(Tuple{typeof(Distributed.handle_msg), Distributed.RemoteDoMsg, Distributed.MsgHeader, Sockets.TCPSocket, Sockets.TCPSocket, Base.VersionNumber}) -precompile(Tuple{typeof(Distributed.set_valid_processes), Array{Int64, 1}}) -precompile(Tuple{typeof(Distributed._rmprocs), Array{Int64, 1}, Float64}) -precompile(Tuple{typeof(Base.kill), Distributed.LocalManager, Int64, Distributed.WorkerConfig}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, typeof(Base.exit)}) -precompile(Tuple{typeof(Serialization.serialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Tuple{}}) -precompile(Tuple{typeof(Serialization.deserialize), Distributed.ClusterSerializer{Sockets.TCPSocket}, Type{typeof(Base.exit)}}) -precompile(Tuple{typeof(Distributed.finalize_ref), Distributed.RemoteChannel{Base.Channel{Any}}}) -precompile(Tuple{typeof(Distributed.send_del_client), Distributed.RemoteChannel{Base.Channel{Any}}}) -precompile(Tuple{typeof(Base.:(==)), Distributed.RemoteChannel{Base.Channel{Any}}, Distributed.RemoteChannel{Base.Channel{Any}}}) -precompile(Tuple{Type{Distributed.Future}, Int64}) -precompile(Tuple{typeof(Distributed.flush_gc_msgs), Distributed.Worker}) -precompile(Tuple{typeof(Distributed.remote_do), typeof(Base.exit), Distributed.Worker}) -precompile(Tuple{typeof(Distributed.send_del_client), Distributed.Future}) -precompile(Tuple{typeof(Distributed.send_msg), Distributed.Worker, Distributed.MsgHeader, Distributed.CallMsg{:call}}) -precompile(Tuple{typeof(Distributed.send_msg_), Distributed.Worker, Distributed.MsgHeader, Distributed.CallMsg{:call}, Bool}) -precompile(Tuple{typeof(Distributed.send_msg), Distributed.Worker, Distributed.MsgHeader, Distributed.CallMsg{:call_fetch}}) -precompile(Tuple{typeof(Distributed.send_msg_), Distributed.Worker, Distributed.MsgHeader, Distributed.CallMsg{:call_fetch}, Bool}) -precompile(Tuple{typeof(Distributed.send_msg), Distributed.Worker, Distributed.MsgHeader, Distributed.RemoteDoMsg}) -precompile(Tuple{typeof(Distributed.send_msg_), Distributed.Worker, Distributed.MsgHeader, Distributed.RemoteDoMsg, Bool}) -precompile(Tuple{typeof(Distributed.terminate_all_workers)}) -precompile(Tuple{typeof(Distributed.test_existing_ref), Distributed.Future}) -precompile(Tuple{typeof(Base.finalizer), Distributed.Future, typeof(Distributed.finalize_ref)}) -precompile(Tuple{typeof(Base.hash), Distributed.Future, UInt64}) -precompile(Tuple{typeof(Base.ht_keyindex), Base.Dict{WeakRef, Nothing}, Distributed.Future}) -precompile(Tuple{Type{Union{}}, Distributed.RRID}) diff --git a/stdlib/Distributed/src/remotecall.jl b/stdlib/Distributed/src/remotecall.jl index 4fe2e1c26d199..0c8246d2bc5a7 100644 --- a/stdlib/Distributed/src/remotecall.jl +++ b/stdlib/Distributed/src/remotecall.jl @@ -1,5 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license +abstract type AbstractRemoteRef end + """ client_refs @@ -8,9 +10,7 @@ Tracks whether a particular `AbstractRemoteRef` The `client_refs` lock is also used to synchronize access to `.refs` and associated `clientset` state. """ -const client_refs = WeakKeyDict{Any, Nothing}() # used as a WeakKeySet - -abstract type AbstractRemoteRef end +const client_refs = WeakKeyDict{AbstractRemoteRef, Nothing}() # used as a WeakKeySet """ Future(w::Int, rrid::RRID, v::Union{Some, Nothing}=nothing) diff --git a/stdlib/Distributed/test/distributed_exec.jl b/stdlib/Distributed/test/distributed_exec.jl index d3b0361ced3ae..f4c903e6b719b 100644 --- a/stdlib/Distributed/test/distributed_exec.jl +++ b/stdlib/Distributed/test/distributed_exec.jl @@ -1566,10 +1566,11 @@ end # Issue # 22865 # Must be run on a new cluster, i.e., all workers must be in the same state. -rmprocs(workers()) +@assert nprocs() == 1 p1,p2 = addprocs_with_testenv(2) @everywhere f22865(p) = remotecall_fetch(x->x.*2, p, fill(1.,2)) @test fill(2.,2) == remotecall_fetch(f22865, p1, p2) +rmprocs(p1, p2) function reuseport_tests() # Run the test on all processes. @@ -1606,9 +1607,9 @@ end # Test that the client port is reused. SO_REUSEPORT may not be supported on # all UNIX platforms, Linux kernels prior to 3.9 and older versions of OSX +@assert nprocs() == 1 +addprocs_with_testenv(4; lazy=false) if ccall(:jl_has_so_reuseport, Int32, ()) == 1 - rmprocs(workers()) - addprocs_with_testenv(4; lazy=false) reuseport_tests() else @info "SO_REUSEPORT is unsupported, skipping reuseport tests" diff --git a/stdlib/FileWatching/src/FileWatching.jl b/stdlib/FileWatching/src/FileWatching.jl index 13a8343441f8b..6f316e5a772f0 100644 --- a/stdlib/FileWatching/src/FileWatching.jl +++ b/stdlib/FileWatching/src/FileWatching.jl @@ -620,7 +620,7 @@ function wait(m::FolderMonitor) take!(m.notify) catch ex unpreserve_handle(m) - if ex isa InvalidStateException && ex.state == :closed + if ex isa InvalidStateException && ex.state === :closed rethrow(EOFError()) # `wait(::Channel)` throws the wrong exception end rethrow() diff --git a/stdlib/InteractiveUtils/src/clipboard.jl b/stdlib/InteractiveUtils/src/clipboard.jl index ac95bfeb84ca4..c824e6a090311 100644 --- a/stdlib/InteractiveUtils/src/clipboard.jl +++ b/stdlib/InteractiveUtils/src/clipboard.jl @@ -11,7 +11,7 @@ if Sys.isapple() # with clipboards. Luckily, the `reattach-to-user-namespace` utility # dodges these issues quite nicely, so we automatically utilize it if # it is installed. - if Sys.which("reattach-to-user-namespace") != nothing + if Sys.which("reattach-to-user-namespace") !== nothing pbcopy_cmd = `reattach-to-user-namespace pbcopy` end @@ -23,7 +23,7 @@ if Sys.isapple() pbpaste_cmd = `pbpaste` # See above comment in `clipboard(x)` - if Sys.which("reattach-to-user-namespace") != nothing + if Sys.which("reattach-to-user-namespace") !== nothing pbcopy_cmd = `reattach-to-user-namespace pbpaste` end read(pbpaste_cmd, String) diff --git a/stdlib/InteractiveUtils/src/codeview.jl b/stdlib/InteractiveUtils/src/codeview.jl index cec5d4e3904e1..6f3032e264b88 100644 --- a/stdlib/InteractiveUtils/src/codeview.jl +++ b/stdlib/InteractiveUtils/src/codeview.jl @@ -88,12 +88,12 @@ function _dump_function_linfo(linfo::Core.MethodInstance, world::UInt, native::B strip_ir_metadata::Bool, dump_module::Bool, syntax::Symbol, optimize::Bool, debuginfo::Symbol=:default, params::CodegenParams=CodegenParams()) - if syntax != :att && syntax != :intel + if syntax !== :att && syntax !== :intel throw(ArgumentError("'syntax' must be either :intel or :att")) end - if debuginfo == :default + if debuginfo === :default debuginfo = :source - elseif debuginfo != :source && debuginfo != :none + elseif debuginfo !== :source && debuginfo !== :none throw(ArgumentError("'debuginfo' must be either :source or :none")) end if native diff --git a/stdlib/InteractiveUtils/src/macros.jl b/stdlib/InteractiveUtils/src/macros.jl index 53ca014fdace2..a3a0bd9929c2c 100644 --- a/stdlib/InteractiveUtils/src/macros.jl +++ b/stdlib/InteractiveUtils/src/macros.jl @@ -99,7 +99,7 @@ function gen_call_with_extracted_types_and_kwargs(__module__, fcn, ex0) arg = ex0[end] # Mandatory argument for i in 1:length(ex0)-1 x = ex0[i] - if x isa Expr && x.head == :(=) # Keyword given of the form "foo=bar" + if x isa Expr && x.head === :(=) # Keyword given of the form "foo=bar" push!(kwargs, x.args) else return Expr(:call, :error, "@$fcn expects only one non-keyword argument") diff --git a/stdlib/InteractiveUtils/test/runtests.jl b/stdlib/InteractiveUtils/test/runtests.jl index 3e522ce24a54e..afe63cd0d5949 100644 --- a/stdlib/InteractiveUtils/test/runtests.jl +++ b/stdlib/InteractiveUtils/test/runtests.jl @@ -389,3 +389,7 @@ if Sys.iswindows() || Sys.isapple() @test clipboard() == str end end + +# buildbot path updating +file, ln = functionloc(versioninfo, Tuple{}) +@test isfile(file) diff --git a/stdlib/LibGit2/src/gitcredential.jl b/stdlib/LibGit2/src/gitcredential.jl index a0504b1c3dd85..f01da3a7ab37e 100644 --- a/stdlib/LibGit2/src/gitcredential.jl +++ b/stdlib/LibGit2/src/gitcredential.jl @@ -122,7 +122,7 @@ function Base.read!(io::IO, cred::GitCredential) end elseif key in GIT_CRED_ATTRIBUTES field = getproperty(cred, Symbol(key)) - field !== nothing && Symbol(key) == :password && Base.shred!(field) + field !== nothing && Symbol(key) === :password && Base.shred!(field) setproperty!(cred, Symbol(key), value) elseif !all(isspace, key) @warn "Unknown git credential attribute found: $(repr(key))" diff --git a/stdlib/LibGit2/src/types.jl b/stdlib/LibGit2/src/types.jl index cfbc37672d044..6ffbe67ea2775 100644 --- a/stdlib/LibGit2/src/types.jl +++ b/stdlib/LibGit2/src/types.jl @@ -1021,7 +1021,7 @@ for (typ, owntyp, sup, cname) in [ return obj end end - if isa(owntyp, Expr) && owntyp.args[1] == :Union && owntyp.args[3] == :Nothing + if isa(owntyp, Expr) && owntyp.args[1] === :Union && owntyp.args[3] === :Nothing @eval begin $typ(ptr::Ptr{Cvoid}, fin::Bool=true) = $typ(nothing, ptr, fin) end @@ -1205,7 +1205,7 @@ mutable struct UserPasswordCredential <: AbstractCredential end function Base.setproperty!(cred::UserPasswordCredential, name::Symbol, value) - if name == :pass + if name === :pass field = getfield(cred, name) Base.shred!(field) end @@ -1240,7 +1240,7 @@ mutable struct SSHCredential <: AbstractCredential end function Base.setproperty!(cred::SSHCredential, name::Symbol, value) - if name == :pass + if name === :pass field = getfield(cred, name) Base.shred!(field) end diff --git a/stdlib/LinearAlgebra/docs/src/index.md b/stdlib/LinearAlgebra/docs/src/index.md index d341eb7f22e84..b78ed785080e0 100644 --- a/stdlib/LinearAlgebra/docs/src/index.md +++ b/stdlib/LinearAlgebra/docs/src/index.md @@ -284,7 +284,7 @@ compute the factorization of a matrix into a product of matrices, and are one of in linear algebra. The following table summarizes the types of matrix factorizations that have been implemented in -Julia. Details of their associated methods can be found in the [Standard Functions](@ref) section +Julia. Details of their associated methods can be found in the [Standard functions](@ref) section of the Linear Algebra documentation. | Type | Description | @@ -308,7 +308,7 @@ of the Linear Algebra documentation. -## Standard Functions +## Standard functions Linear algebra functions in Julia are largely implemented by calling functions from [LAPACK](http://www.netlib.org/lapack/). Sparse factorizations call functions from [SuiteSparse](http://faculty.cse.tamu.edu/davis/suitesparse.html). @@ -334,6 +334,7 @@ LinearAlgebra.UnitLowerTriangular LinearAlgebra.UnitUpperTriangular LinearAlgebra.UpperHessenberg LinearAlgebra.UniformScaling +LinearAlgebra.I LinearAlgebra.Factorization LinearAlgebra.LU LinearAlgebra.lu @@ -475,7 +476,7 @@ LinearAlgebra.ldiv! LinearAlgebra.rdiv! ``` -## BLAS Functions +## BLAS functions In Julia (as in much of scientific computation), dense linear-algebra operations are based on the [LAPACK library](http://www.netlib.org/lapack/), which in turn is built on top of basic linear-algebra @@ -487,33 +488,33 @@ linear algebra routines it is useful to call the BLAS functions directly. that overwrite one of the input arrays have names ending in `'!'`. Usually, a BLAS function has four methods defined, for [`Float64`](@ref), [`Float32`](@ref), `ComplexF64`, and `ComplexF32` arrays. -### [BLAS Character Arguments](@id stdlib-blas-chars) +### [BLAS character arguments](@id stdlib-blas-chars) Many BLAS functions accept arguments that determine whether to transpose an argument (`trans`), which triangle of a matrix to reference (`uplo` or `ul`), whether the diagonal of a triangular matrix can be assumed to be all ones (`dA`) or which side of a matrix multiplication the input argument belongs on (`side`). The possibilities are: -#### [Multplication Order](@id stdlib-blas-side) +#### [Multiplication order](@id stdlib-blas-side) | `side` | Meaning | |:-------|:--------------------------------------------------------------------| | `'L'` | The argument goes on the *left* side of a matrix-matrix operation. | | `'R'` | The argument goes on the *right* side of a matrix-matrix operation. | -#### [Triangle Referencing](@id stdlib-blas-uplo) +#### [Triangle referencing](@id stdlib-blas-uplo) | `uplo`/`ul` | Meaning | |:------------|:------------------------------------------------------| | `'U'` | Only the *upper* triangle of the matrix will be used. | | `'L'` | Only the *lower* triangle of the matrix will be used. | -#### [Transposition Operation](@id stdlib-blas-trans) +#### [Transposition operation](@id stdlib-blas-trans) | `trans`/`tX` | Meaning | |:-------------|:--------------------------------------------------------| | `'N'` | The input matrix `X` is not transposed or conjugated. | | `'T'` | The input matrix `X` will be transposed. | | `'C'` | The input matrix `X` will be conjugated and transposed. | -#### [Unit Diagonal](@id stdlib-blas-diag) +#### [Unit diagonal](@id stdlib-blas-diag) | `diag`/`dX` | Meaning | |:------------|:----------------------------------------------------------| | `'N'` | The diagonal values of the matrix `X` will be read. | @@ -536,9 +537,13 @@ LinearAlgebra.BLAS.ger! LinearAlgebra.BLAS.syr! LinearAlgebra.BLAS.syrk! LinearAlgebra.BLAS.syrk +LinearAlgebra.BLAS.syr2k! +LinearAlgebra.BLAS.syr2k LinearAlgebra.BLAS.her! LinearAlgebra.BLAS.herk! LinearAlgebra.BLAS.herk +LinearAlgebra.BLAS.her2k! +LinearAlgebra.BLAS.her2k LinearAlgebra.BLAS.gbmv! LinearAlgebra.BLAS.gbmv LinearAlgebra.BLAS.sbmv! @@ -556,6 +561,12 @@ LinearAlgebra.BLAS.symm(::Any, ::Any, ::Any, ::Any) LinearAlgebra.BLAS.symv! LinearAlgebra.BLAS.symv(::Any, ::Any, ::Any, ::Any) LinearAlgebra.BLAS.symv(::Any, ::Any, ::Any) +LinearAlgebra.BLAS.hemm! +LinearAlgebra.BLAS.hemm(::Any, ::Any, ::Any, ::Any, ::Any) +LinearAlgebra.BLAS.hemm(::Any, ::Any, ::Any, ::Any) +LinearAlgebra.BLAS.hemv! +LinearAlgebra.BLAS.hemv(::Any, ::Any, ::Any, ::Any) +LinearAlgebra.BLAS.hemv(::Any, ::Any, ::Any) LinearAlgebra.BLAS.trmm! LinearAlgebra.BLAS.trmm LinearAlgebra.BLAS.trsm! @@ -565,10 +576,9 @@ LinearAlgebra.BLAS.trmv LinearAlgebra.BLAS.trsv! LinearAlgebra.BLAS.trsv LinearAlgebra.BLAS.set_num_threads -LinearAlgebra.I ``` -## LAPACK Functions +## LAPACK functions `LinearAlgebra.LAPACK` provides wrappers for some of the LAPACK functions for linear algebra. Those functions that overwrite one of the input arrays have names ending in `'!'`. diff --git a/stdlib/LinearAlgebra/src/LinearAlgebra.jl b/stdlib/LinearAlgebra/src/LinearAlgebra.jl index dee9bd21ee0d6..30002837f8398 100644 --- a/stdlib/LinearAlgebra/src/LinearAlgebra.jl +++ b/stdlib/LinearAlgebra/src/LinearAlgebra.jl @@ -232,9 +232,9 @@ function checksquare(A...) end function char_uplo(uplo::Symbol) - if uplo == :U + if uplo === :U return 'U' - elseif uplo == :L + elseif uplo === :L return 'L' else throw_uplo() @@ -422,7 +422,7 @@ end function versioninfo(io::IO=stdout) - if Base.libblas_name == "libopenblas" || BLAS.vendor() == :openblas || BLAS.vendor() == :openblas64 + if Base.libblas_name == "libopenblas" || BLAS.vendor() === :openblas || BLAS.vendor() === :openblas64 openblas_config = BLAS.openblas_get_config() println(io, "BLAS: libopenblas (", openblas_config, ")") else @@ -434,7 +434,7 @@ end function __init__() try BLAS.check() - if BLAS.vendor() == :mkl + if BLAS.vendor() === :mkl ccall((:MKL_Set_Interface_Layer, Base.libblas_name), Cvoid, (Cint,), USE_BLAS64 ? 1 : 0) end Threads.resize_nthreads!(Abuf) diff --git a/stdlib/LinearAlgebra/src/adjtrans.jl b/stdlib/LinearAlgebra/src/adjtrans.jl index c980e500adde0..b822be0c6e36d 100644 --- a/stdlib/LinearAlgebra/src/adjtrans.jl +++ b/stdlib/LinearAlgebra/src/adjtrans.jl @@ -281,3 +281,7 @@ pinv(v::TransposeAbsVec, tol::Real = 0) = pinv(conj(v.parent)).parent /(u::TransposeAbsVec, A::AbstractMatrix) = transpose(transpose(A) \ u.parent) /(u::AdjointAbsVec, A::Transpose{<:Any,<:AbstractMatrix}) = adjoint(conj(A.parent) \ u.parent) # technically should be adjoint(copy(adjoint(copy(A))) \ u.parent) /(u::TransposeAbsVec, A::Adjoint{<:Any,<:AbstractMatrix}) = transpose(conj(A.parent) \ u.parent) # technically should be transpose(copy(transpose(copy(A))) \ u.parent) + +## complex conjugate +conj(A::Transpose) = adjoint(A.parent) +conj(A::Adjoint) = transpose(A.parent) diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 8e707c4516308..faa32aee15fa6 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -99,7 +99,7 @@ julia> Bidiagonal(A, :L) # contains the main diagonal and first subdiagonal of A ``` """ function Bidiagonal(A::AbstractMatrix, uplo::Symbol) - Bidiagonal(diag(A, 0), diag(A, uplo == :U ? 1 : -1), uplo) + Bidiagonal(diag(A, 0), diag(A, uplo === :U ? 1 : -1), uplo) end Bidiagonal(A::Bidiagonal) = A @@ -586,7 +586,12 @@ function *(A::AbstractTriangular, B::Union{SymTridiagonal, Tridiagonal}) A_mul_B_td!(zeros(TS, size(A)...), A, B) end -function *(A::UpperTriangular, B::Bidiagonal) +const UpperOrUnitUpperTriangular = Union{UpperTriangular, UnitUpperTriangular} +const LowerOrUnitLowerTriangular = Union{LowerTriangular, UnitLowerTriangular} +const AdjOrTransUpperOrUnitUpperTriangular = Union{Adjoint{<:Any, <:UpperOrUnitUpperTriangular}, Transpose{<:Any, <:UpperOrUnitUpperTriangular}} +const AdjOrTransLowerOrUnitLowerTriangular = Union{Adjoint{<:Any, <:LowerOrUnitLowerTriangular}, Transpose{<:Any, <:LowerOrUnitLowerTriangular}} + +function *(A::UpperOrUnitUpperTriangular, B::Bidiagonal) TS = promote_op(matprod, eltype(A), eltype(B)) if B.uplo == 'U' A_mul_B_td!(UpperTriangular(zeros(TS, size(A)...)), A, B) @@ -595,7 +600,16 @@ function *(A::UpperTriangular, B::Bidiagonal) end end -function *(A::LowerTriangular, B::Bidiagonal) +function *(A::AdjOrTransUpperOrUnitUpperTriangular, B::Bidiagonal) + TS = promote_op(matprod, eltype(A), eltype(B)) + if B.uplo == 'L' + A_mul_B_td!(LowerTriangular(zeros(TS, size(A)...)), A, B) + else + A_mul_B_td!(zeros(TS, size(A)...), A, B) + end +end + +function *(A::LowerOrUnitLowerTriangular, B::Bidiagonal) TS = promote_op(matprod, eltype(A), eltype(B)) if B.uplo == 'L' A_mul_B_td!(LowerTriangular(zeros(TS, size(A)...)), A, B) @@ -604,12 +618,21 @@ function *(A::LowerTriangular, B::Bidiagonal) end end +function *(A::AdjOrTransLowerOrUnitLowerTriangular, B::Bidiagonal) + TS = promote_op(matprod, eltype(A), eltype(B)) + if B.uplo == 'U' + A_mul_B_td!(UpperTriangular(zeros(TS, size(A)...)), A, B) + else + A_mul_B_td!(zeros(TS, size(A)...), A, B) + end +end + function *(A::Union{SymTridiagonal, Tridiagonal}, B::AbstractTriangular) TS = promote_op(matprod, eltype(A), eltype(B)) A_mul_B_td!(zeros(TS, size(A)...), A, B) end -function *(A::Bidiagonal, B::UpperTriangular) +function *(A::Bidiagonal, B::UpperOrUnitUpperTriangular) TS = promote_op(matprod, eltype(A), eltype(B)) if A.uplo == 'U' A_mul_B_td!(UpperTriangular(zeros(TS, size(A)...)), A, B) @@ -618,7 +641,7 @@ function *(A::Bidiagonal, B::UpperTriangular) end end -function *(A::Bidiagonal, B::LowerTriangular) +function *(A::Bidiagonal, B::AdjOrTransUpperOrUnitUpperTriangular) TS = promote_op(matprod, eltype(A), eltype(B)) if A.uplo == 'L' A_mul_B_td!(LowerTriangular(zeros(TS, size(A)...)), A, B) @@ -627,6 +650,24 @@ function *(A::Bidiagonal, B::LowerTriangular) end end +function *(A::Bidiagonal, B::LowerOrUnitLowerTriangular) + TS = promote_op(matprod, eltype(A), eltype(B)) + if A.uplo == 'L' + A_mul_B_td!(LowerTriangular(zeros(TS, size(A)...)), A, B) + else + A_mul_B_td!(zeros(TS, size(A)...), A, B) + end +end + +function *(A::Bidiagonal, B::AdjOrTransLowerOrUnitLowerTriangular) + TS = promote_op(matprod, eltype(A), eltype(B)) + if A.uplo == 'U' + A_mul_B_td!(UpperTriangular(zeros(TS, size(A)...)), A, B) + else + A_mul_B_td!(zeros(TS, size(A)...), A, B) + end +end + function *(A::BiTri, B::Diagonal) TS = promote_op(matprod, eltype(A), eltype(B)) A_mul_B_td!(similar(A, TS), A, B) diff --git a/stdlib/LinearAlgebra/src/blas.jl b/stdlib/LinearAlgebra/src/blas.jl index be1ad686cbc6b..c44305bd8b6a8 100644 --- a/stdlib/LinearAlgebra/src/blas.jl +++ b/stdlib/LinearAlgebra/src/blas.jl @@ -91,7 +91,7 @@ end const _vendor = determine_vendor() vendor() = _vendor -if vendor() == :openblas64 +if vendor() === :openblas64 macro blasfunc(x) return Expr(:quote, Symbol(x, "64_")) end @@ -110,11 +110,11 @@ Set the number of threads the BLAS library should use. """ function set_num_threads(n::Integer) blas = vendor() - if blas == :openblas + if blas === :openblas return ccall((:openblas_set_num_threads, libblas), Cvoid, (Int32,), n) - elseif blas == :openblas64 + elseif blas === :openblas64 return ccall((:openblas_set_num_threads64_, libblas), Cvoid, (Int32,), n) - elseif blas == :mkl + elseif blas === :mkl # MKL may let us set the number of threads in several ways return ccall((:MKL_Set_Num_Threads, libblas), Cvoid, (Cint,), n) end @@ -130,7 +130,7 @@ end const _testmat = [1.0 0.0; 0.0 -1.0] function check() blas = vendor() - if blas == :openblas || blas == :openblas64 + if blas === :openblas || blas === :openblas64 openblas_config = openblas_get_config() openblas64 = occursin(r".*USE64BITINT.*", openblas_config) if Base.USE_BLAS64 != openblas64 @@ -148,7 +148,7 @@ function check() println("Quitting.") exit() end - elseif blas == :mkl + elseif blas === :mkl if Base.USE_BLAS64 ENV["MKL_INTERFACE_LAYER"] = "ILP64" end @@ -422,7 +422,7 @@ asum(x::Union{AbstractVector,DenseArray}) = GC.@preserve x asum(length(x), point """ axpy!(a, X, Y) -Overwrite `Y` with `a*X + Y`, where `a` is a scalar. Return `Y`. +Overwrite `Y` with `X*a + Y`, where `a` is a scalar. Return `Y`. # Examples ```jldoctest @@ -760,6 +760,15 @@ Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. symv(ul, A, x) ### hemv +""" + hemv!(ul, alpha, A, x, beta, y) + +Update the vector `y` as `alpha*A*x + beta*y`. `A` is assumed to be Hermitian. +Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. +`alpha` and `beta` are scalars. Return the updated `y`. +""" +function hemv! end + for (fname, elty) in ((:zhemv_,:ComplexF64), (:chemv_,:ComplexF32)) @eval begin @@ -797,6 +806,23 @@ for (fname, elty) in ((:zhemv_,:ComplexF64), end end +""" + hemv(ul, alpha, A, x) + +Return `alpha*A*x`. `A` is assumed to be Hermitian. +Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. +`alpha` is a scalar. +""" +hemv(ul, alpha, A, x) + +""" + hemv(ul, A, x) + +Return `A*x`. `A` is assumed to be Hermitian. +Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. +""" +hemv(ul, A, x) + ### sbmv, (SB) symmetric banded matrix-vector multiplication for (fname, elty) in ((:dsbmv_,:Float64), (:ssbmv_,:Float32)) @@ -1290,13 +1316,39 @@ for (mfname, elty) in ((:zhemm_,:ComplexF64), end end +""" + hemm(side, ul, alpha, A, B) + +Return `alpha*A*B` or `alpha*B*A` according to [`side`](@ref stdlib-blas-side). +`A` is assumed to be Hermitian. Only the [`ul`](@ref stdlib-blas-uplo) triangle +of `A` is used. +""" +hemm(side, ul, alpha, A, B) + +""" + hemm(side, ul, A, B) + +Return `A*B` or `B*A` according to [`side`](@ref stdlib-blas-side). `A` is assumed +to be Hermitian. Only the [`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. +""" +hemm(side, ul, A, B) + +""" + hemm!(side, ul, alpha, A, B, beta, C) + +Update `C` as `alpha*A*B + beta*C` or `alpha*B*A + beta*C` according to +[`side`](@ref stdlib-blas-side). `A` is assumed to be Hermitian. Only the +[`ul`](@ref stdlib-blas-uplo) triangle of `A` is used. Return the updated `C`. +""" +hemm! + ## syrk """ syrk!(uplo, trans, alpha, A, beta, C) -Rank-k update of the symmetric matrix `C` as `alpha*A*transpose(A) + beta*C` or `alpha*transpose(A)*A + -beta*C` according to [`trans`](@ref stdlib-blas-trans). +Rank-k update of the symmetric matrix `C` as `alpha*A*transpose(A) + beta*C` or +`alpha*transpose(A)*A + beta*C` according to [`trans`](@ref stdlib-blas-trans). Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `C` is used. Returns `C`. """ function syrk! end @@ -1354,19 +1406,17 @@ syrk(uplo::AbstractChar, trans::AbstractChar, A::AbstractVecOrMat) = syrk(uplo, """ herk!(uplo, trans, alpha, A, beta, C) -Methods for complex arrays only. Rank-k update of the Hermitian matrix `C` as `alpha*A*A' + -beta*C` or `alpha*A'*A + beta*C` according to [`trans`](@ref stdlib-blas-trans). -Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `C` is updated. -Returns `C`. +Methods for complex arrays only. Rank-k update of the Hermitian matrix `C` as +`alpha*A*A' + beta*C` or `alpha*A'*A + beta*C` according to [`trans`](@ref stdlib-blas-trans). +Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `C` is updated. Returns `C`. """ function herk! end """ herk(uplo, trans, alpha, A) -Methods for complex arrays only. -Returns the [`uplo`](@ref stdlib-blas-uplo) triangle of `alpha*A*A'` or `alpha*A'*A`, -according to [`trans`](@ref stdlib-blas-trans). +Methods for complex arrays only. Returns the [`uplo`](@ref stdlib-blas-uplo) +triangle of `alpha*A*A'` or `alpha*A'*A`, according to [`trans`](@ref stdlib-blas-trans). """ function herk end @@ -1447,11 +1497,37 @@ for (fname, elty) in ((:dsyr2k_,:Float64), end end end + +""" + syr2k!(uplo, trans, alpha, A, B, beta, C) + +Rank-2k update of the symmetric matrix `C` as +`alpha*A*transpose(B) + alpha*B*transpose(A) + beta*C` or +`alpha*transpose(A)*B + alpha*transpose(B)*A + beta*C` +according to [`trans`](@ref stdlib-blas-trans). +Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `C` is used. Returns `C`. +""" +function syr2k! end + +""" + syr2k(uplo, trans, alpha, A, B) + +Returns the [`uplo`](@ref stdlib-blas-uplo) triangle of +`alpha*A*transpose(B) + alpha*B*transpose(A)` or +`alpha*transpose(A)*B + alpha*transpose(B)*A`, +according to [`trans`](@ref stdlib-blas-trans). +""" function syr2k(uplo::AbstractChar, trans::AbstractChar, alpha::Number, A::AbstractVecOrMat, B::AbstractVecOrMat) T = eltype(A) n = size(A, trans == 'N' ? 1 : 2) syr2k!(uplo, trans, convert(T,alpha), A, B, zero(T), similar(A, T, (n, n))) end +""" + syr2k(uplo, trans, A, B) + +Returns the [`uplo`](@ref stdlib-blas-uplo) triangle of `A*transpose(B) + B*transpose(A)` +or `transpose(A)*B + transpose(B)*A`, according to [`trans`](@ref stdlib-blas-trans). +""" syr2k(uplo::AbstractChar, trans::AbstractChar, A::AbstractVecOrMat, B::AbstractVecOrMat) = syr2k(uplo, trans, one(eltype(A)), A, B) for (fname, elty1, elty2) in ((:zher2k_,:ComplexF64,:Float64), (:cher2k_,:ComplexF32,:Float32)) @@ -1494,6 +1570,32 @@ for (fname, elty1, elty2) in ((:zher2k_,:ComplexF64,:Float64), (:cher2k_,:Comple end end +""" + her2k!(uplo, trans, alpha, A, B, beta, C) + +Rank-2k update of the Hermitian matrix `C` as +`alpha*A*B' + alpha*B*A' + beta*C` or `alpha*A'*B + alpha*B'*A + beta*C` +according to [`trans`](@ref stdlib-blas-trans). The scalar `beta` has to be real. +Only the [`uplo`](@ref stdlib-blas-uplo) triangle of `C` is used. Returns `C`. +""" +function her2k! end + +""" + her2k(uplo, trans, alpha, A, B) + +Returns the [`uplo`](@ref stdlib-blas-uplo) triangle of `alpha*A*B' + alpha*B*A'` +or `alpha*A'*B + alpha*B'*A`, according to [`trans`](@ref stdlib-blas-trans). +""" +her2k(uplo, trans, alpha, A, B) + +""" + her2k(uplo, trans, A, B) + +Returns the [`uplo`](@ref stdlib-blas-uplo) triangle of `A*B' + B*A'` +or `A'*B + B'*A`, according to [`trans`](@ref stdlib-blas-trans). +""" +her2k(uplo, trans, A, B) + ## (TR) Triangular matrix and vector multiplication and solution """ diff --git a/stdlib/LinearAlgebra/src/bunchkaufman.jl b/stdlib/LinearAlgebra/src/bunchkaufman.jl index acbd57c1f8554..c57dedc66776e 100644 --- a/stdlib/LinearAlgebra/src/bunchkaufman.jl +++ b/stdlib/LinearAlgebra/src/bunchkaufman.jl @@ -284,17 +284,17 @@ julia> F.U*F.D*F.U' - F.P*A*F.P' """ function getproperty(B::BunchKaufman{T}, d::Symbol) where {T<:BlasFloat} n = size(B, 1) - if d == :p + if d === :p return _ipiv2perm_bk(getfield(B, :ipiv), n, getfield(B, :uplo), B.rook) - elseif d == :P + elseif d === :P return Matrix{T}(I, n, n)[:,invperm(B.p)] - elseif d == :L || d == :U || d == :D + elseif d === :L || d === :U || d === :D if getfield(B, :rook) LUD, od = LAPACK.syconvf_rook!(getfield(B, :uplo), 'C', copy(getfield(B, :LD)), getfield(B, :ipiv)) else LUD, od = LAPACK.syconv!(getfield(B, :uplo), copy(getfield(B, :LD)), getfield(B, :ipiv)) end - if d == :D + if d === :D if getfield(B, :uplo) == 'L' odl = od[1:n - 1] return Tridiagonal(odl, diag(LUD), getfield(B, :symmetric) ? odl : conj.(odl)) @@ -302,7 +302,7 @@ function getproperty(B::BunchKaufman{T}, d::Symbol) where {T<:BlasFloat} odu = od[2:n] return Tridiagonal(getfield(B, :symmetric) ? odu : conj.(odu), diag(LUD), odu) end - elseif d == :L + elseif d === :L if getfield(B, :uplo) == 'L' return UnitLowerTriangular(LUD) else diff --git a/stdlib/LinearAlgebra/src/cholesky.jl b/stdlib/LinearAlgebra/src/cholesky.jl index 65540e2004155..bd28a2e1f351e 100644 --- a/stdlib/LinearAlgebra/src/cholesky.jl +++ b/stdlib/LinearAlgebra/src/cholesky.jl @@ -396,7 +396,8 @@ Array(C::Cholesky) = Matrix(C) function AbstractMatrix(F::CholeskyPivoted) ip = invperm(F.p) - (F.L * F.U)[ip,ip] + U = F.U[1:F.rank,ip] + U'U end AbstractArray(F::CholeskyPivoted) = AbstractMatrix(F) Matrix(F::CholeskyPivoted) = Array(AbstractArray(F)) @@ -411,12 +412,11 @@ size(C::Union{Cholesky, CholeskyPivoted}, d::Integer) = size(C.factors, d) function getproperty(C::Cholesky, d::Symbol) Cfactors = getfield(C, :factors) Cuplo = getfield(C, :uplo) - info = getfield(C, :info) - if d == :U + if d === :U return UpperTriangular(Cuplo === char_uplo(d) ? Cfactors : copy(Cfactors')) - elseif d == :L + elseif d === :L return LowerTriangular(Cuplo === char_uplo(d) ? Cfactors : copy(Cfactors')) - elseif d == :UL + elseif d === :UL return (Cuplo === 'U' ? UpperTriangular(Cfactors) : LowerTriangular(Cfactors)) else return getfield(C, d) @@ -428,13 +428,13 @@ Base.propertynames(F::Cholesky, private::Bool=false) = function getproperty(C::CholeskyPivoted{T}, d::Symbol) where T<:BlasFloat Cfactors = getfield(C, :factors) Cuplo = getfield(C, :uplo) - if d == :U + if d === :U return UpperTriangular(sym_uplo(Cuplo) == d ? Cfactors : copy(Cfactors')) - elseif d == :L + elseif d === :L return LowerTriangular(sym_uplo(Cuplo) == d ? Cfactors : copy(Cfactors')) - elseif d == :p + elseif d === :p return getfield(C, :piv) - elseif d == :P + elseif d === :P n = size(C, 1) P = zeros(T, n, n) for i = 1:n diff --git a/stdlib/LinearAlgebra/src/dense.jl b/stdlib/LinearAlgebra/src/dense.jl index f76440b8e4c33..5d69e48ad9e26 100644 --- a/stdlib/LinearAlgebra/src/dense.jl +++ b/stdlib/LinearAlgebra/src/dense.jl @@ -1363,8 +1363,8 @@ end nullspace(M, rtol::Real) = nullspace(M; rtol=rtol) # to be deprecated in Julia 2.0 Computes a basis for the nullspace of `M` by including the singular -vectors of A whose singular have magnitude are greater than `max(atol, rtol*σ₁)`, -where `σ₁` is `M`'s largest singularvalue. +vectors of `M` whose singular values have magnitudes greater than `max(atol, rtol*σ₁)`, +where `σ₁` is `M`'s largest singular value. By default, the relative tolerance `rtol` is `n*ϵ`, where `n` is the size of the smallest dimension of `M`, and `ϵ` is the [`eps`](@ref) of @@ -1418,15 +1418,22 @@ function cond(A::AbstractMatrix, p::Real=2) if p == 2 v = svdvals(A) maxv = maximum(v) - return maxv == 0.0 ? oftype(real(A[1,1]),Inf) : maxv / minimum(v) + return iszero(maxv) ? oftype(real(maxv), Inf) : maxv / minimum(v) elseif p == 1 || p == Inf checksquare(A) - return _cond1Inf(A, p) + try + Ainv = inv(A) + return opnorm(A, p)*opnorm(Ainv, p) + catch e + if isa(e, LAPACKException) || isa(e, SingularException) + return convert(float(real(eltype(A))), Inf) + else + rethrow() + end + end end throw(ArgumentError("p-norm must be 1, 2 or Inf, got $p")) end -_cond1Inf(A::StridedMatrix{<:BlasFloat}, p::Real) = _cond1Inf(lu(A), p, opnorm(A, p)) -_cond1Inf(A::AbstractMatrix, p::Real) = opnorm(A, p)*opnorm(inv(A), p) ## Lyapunov and Sylvester equation diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index b9792a2ae5a31..b0f756a476539 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -654,11 +654,9 @@ end function cholesky!(A::Diagonal, ::Val{false} = Val(false); check::Bool = true) info = 0 - diagonal = A.diag - for i in axes(diagonal, 1) - d = diagonal[i] - if !(d == 0 || (isreal(d) && d < 0)) - diagonal[i] = √d + for (i, di) in enumerate(A.diag) + if isreal(di) && real(di) > 0 + A.diag[i] = √di elseif check throw(PosDefException(i)) else @@ -672,6 +670,15 @@ end cholesky(A::Diagonal, ::Val{false} = Val(false); check::Bool = true) = cholesky!(cholcopy(A), Val(false); check = check) +function getproperty(C::Cholesky{<:Any,<:Diagonal}, d::Symbol) + Cfactors = getfield(C, :factors) + if d in (:U, :L, :UL) + return Cfactors + else + return getfield(C, d) + end +end + Base._sum(A::Diagonal, ::Colon) = sum(A.diag) function logabsdet(A::Diagonal) diff --git a/stdlib/LinearAlgebra/src/generic.jl b/stdlib/LinearAlgebra/src/generic.jl index b5d0a05907cd0..45efc1e1ed529 100644 --- a/stdlib/LinearAlgebra/src/generic.jl +++ b/stdlib/LinearAlgebra/src/generic.jl @@ -75,6 +75,9 @@ julia> C end @inline function _rmul_or_fill!(C::AbstractArray, beta::Number) + if isempty(C) + return C + end if iszero(beta) fill!(C, zero(eltype(C))) else diff --git a/stdlib/LinearAlgebra/src/hessenberg.jl b/stdlib/LinearAlgebra/src/hessenberg.jl index 5f9b2be1d34a2..c64c2f55669cd 100644 --- a/stdlib/LinearAlgebra/src/hessenberg.jl +++ b/stdlib/LinearAlgebra/src/hessenberg.jl @@ -453,7 +453,7 @@ HessenbergQ(F::Hessenberg{<:Any,<:UpperHessenberg,S,W}) where {S,W} = Hessenberg HessenbergQ(F::Hessenberg{<:Any,<:SymTridiagonal,S,W}) where {S,W} = HessenbergQ{eltype(F.factors),S,W,true}(F.uplo, F.factors, F.τ) function getproperty(F::Hessenberg, d::Symbol) - d == :Q && return HessenbergQ(F) + d === :Q && return HessenbergQ(F) return getfield(F, d) end diff --git a/stdlib/LinearAlgebra/src/ldlt.jl b/stdlib/LinearAlgebra/src/ldlt.jl index ae0d020a298b1..de3716948f65d 100644 --- a/stdlib/LinearAlgebra/src/ldlt.jl +++ b/stdlib/LinearAlgebra/src/ldlt.jl @@ -64,13 +64,13 @@ Factorization{T}(F::LDLt) where {T} = LDLt{T}(F) function getproperty(F::LDLt, d::Symbol) Fdata = getfield(F, :data) - if d == :d + if d === :d return Fdata.dv - elseif d == :D + elseif d === :D return Diagonal(Fdata.dv) - elseif d == :L + elseif d === :L return UnitLowerTriangular(Fdata) - elseif d == :Lt + elseif d === :Lt return UnitUpperTriangular(Fdata) else return getfield(F, d) diff --git a/stdlib/LinearAlgebra/src/lq.jl b/stdlib/LinearAlgebra/src/lq.jl index 7b3829023b4ca..8006fc5509765 100644 --- a/stdlib/LinearAlgebra/src/lq.jl +++ b/stdlib/LinearAlgebra/src/lq.jl @@ -124,9 +124,9 @@ Base.copy(F::Adjoint{T,<:LQ{T}}) where {T} = function getproperty(F::LQ, d::Symbol) m, n = size(F) - if d == :L + if d === :L return tril!(getfield(F, :factors)[1:m, 1:min(m,n)]) - elseif d == :Q + elseif d === :Q return LQPackedQ(getfield(F, :factors), getfield(F, :τ)) else return getfield(F, d) diff --git a/stdlib/LinearAlgebra/src/lu.jl b/stdlib/LinearAlgebra/src/lu.jl index c82eae2cbb49b..af2669855ac91 100644 --- a/stdlib/LinearAlgebra/src/lu.jl +++ b/stdlib/LinearAlgebra/src/lu.jl @@ -304,15 +304,15 @@ end function getproperty(F::LU{T,<:StridedMatrix}, d::Symbol) where T m, n = size(F) - if d == :L + if d === :L L = tril!(getfield(F, :factors)[1:m, 1:min(m,n)]) for i = 1:min(m,n); L[i,i] = one(T); end return L - elseif d == :U + elseif d === :U return triu!(getfield(F, :factors)[1:min(m,n), 1:n]) - elseif d == :p + elseif d === :p return ipiv2perm(getfield(F, :ipiv), m) - elseif d == :P + elseif d === :P return Matrix{T}(I, m, m)[:,invperm(F.p)] else getfield(F, d) @@ -478,13 +478,6 @@ inv!(A::LU{T,<:StridedMatrix}) where {T} = ldiv!(A.factors, copy(A), Matrix{T}(I, size(A, 1), size(A, 1))) inv(A::LU{<:BlasFloat,<:StridedMatrix}) = inv!(copy(A)) -function _cond1Inf(A::LU{<:BlasFloat,<:StridedMatrix}, p::Number, normA::Real) - if p != 1 && p != Inf - throw(ArgumentError("p must be either 1 or Inf")) - end - return inv(LAPACK.gecon!(p == 1 ? '1' : 'I', A.factors, normA)) -end - # Tridiagonal # See dgttrf.f @@ -560,7 +553,7 @@ factorize(A::Tridiagonal) = lu(A) function getproperty(F::LU{T,Tridiagonal{T,V}}, d::Symbol) where {T,V} m, n = size(F) - if d == :L + if d === :L dl = getfield(getfield(F, :factors), :dl) L = Array(Bidiagonal(fill!(similar(dl, n), one(T)), dl, d)) for i = 2:n @@ -569,15 +562,15 @@ function getproperty(F::LU{T,Tridiagonal{T,V}}, d::Symbol) where {T,V} L[i, 1:i - 1] = tmp end return L - elseif d == :U + elseif d === :U U = Array(Bidiagonal(getfield(getfield(F, :factors), :d), getfield(getfield(F, :factors), :du), d)) for i = 1:n - 2 U[i,i + 2] = getfield(getfield(F, :factors), :du2)[i] end return U - elseif d == :p + elseif d === :p return ipiv2perm(getfield(F, :ipiv), m) - elseif d == :P + elseif d === :P return Matrix{T}(I, m, m)[:,invperm(F.p)] end return getfield(F, d) diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index a453078711d1a..1bbd2230c315d 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -63,15 +63,15 @@ end (*)(a::AbstractVector, B::AbstractMatrix) = reshape(a,length(a),1)*B @inline mul!(y::StridedVector{T}, A::StridedVecOrMat{T}, x::StridedVector{T}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasFloat} = + alpha::Number, beta::Number) where {T<:BlasFloat} = gemv!(y, 'N', A, x, alpha, beta) # Complex matrix times real vector. Reinterpret the matrix as a real matrix and do real matvec compuation. for elty in (Float32,Float64) @eval begin @inline function mul!(y::StridedVector{Complex{$elty}}, A::StridedVecOrMat{Complex{$elty}}, x::StridedVector{$elty}, - alpha::Union{$elty, Bool}, beta::Union{$elty, Bool}) - Afl = reinterpret($elty,A) - yfl = reinterpret($elty,y) + alpha::Real, beta::Real) + Afl = reinterpret($elty, A) + yfl = reinterpret($elty, y) mul!(yfl, Afl, x, alpha, beta) return y end @@ -92,7 +92,7 @@ function *(transA::Transpose{<:Any,<:AbstractMatrix{T}}, x::AbstractVector{S}) w mul!(similar(x,TS,size(A,2)), transpose(A), x) end @inline function mul!(y::StridedVector{T}, transA::Transpose{<:Any,<:StridedVecOrMat{T}}, x::StridedVector{T}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasFloat} + alpha::Number, beta::Number) where {T<:BlasFloat} A = transA.parent return gemv!(y, 'T', A, x, alpha, beta) end @@ -114,12 +114,12 @@ function *(adjA::Adjoint{<:Any,<:AbstractMatrix{T}}, x::AbstractVector{S}) where end @inline function mul!(y::StridedVector{T}, adjA::Adjoint{<:Any,<:StridedVecOrMat{T}}, x::StridedVector{T}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasReal} + alpha::Number, beta::Number) where {T<:BlasReal} A = adjA.parent return mul!(y, transpose(A), x, alpha, beta) end @inline function mul!(y::StridedVector{T}, adjA::Adjoint{<:Any,<:StridedVecOrMat{T}}, x::StridedVector{T}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasComplex} + alpha::Number, beta::Number) where {T<:BlasComplex} A = adjA.parent return gemv!(y, 'C', A, x, alpha, beta) end @@ -152,17 +152,22 @@ function (*)(A::AbstractMatrix, B::AbstractMatrix) TS = promote_op(matprod, eltype(A), eltype(B)) mul!(similar(B, TS, (size(A,1), size(B,2))), A, B) end +# optimization for dispatching to BLAS, e.g. *(::Matrix{Float32}, ::Matrix{Float64}) +# but avoiding the case *(::Matrix{<:BlasComplex}, ::Matrix{<:BlasReal}) +# which is better handled by reinterpreting rather than promotion +function (*)(A::StridedMatrix{<:BlasReal}, B::StridedMatrix{<:BlasFloat}) + TS = promote_type(eltype(A), eltype(B)) + mul!(similar(B, TS, (size(A,1), size(B,2))), convert(AbstractArray{TS}, A), convert(AbstractArray{TS}, B)) +end +function (*)(A::StridedMatrix{<:BlasComplex}, B::StridedMatrix{<:BlasComplex}) + TS = promote_type(eltype(A), eltype(B)) + mul!(similar(B, TS, (size(A,1), size(B,2))), convert(AbstractArray{TS}, A), convert(AbstractArray{TS}, B)) +end @inline function mul!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}, - α::Number, β::Number) where {T<:BlasFloat} - alpha, beta = promote(α, β, zero(T)) - if alpha isa T && beta isa T - return gemm_wrapper!(C, 'N', 'N', A, B, MulAddMul(alpha, beta)) - else - return generic_matmatmul!(C, 'N', 'N', A, B, MulAddMul(α, β)) - end + alpha::Number, beta::Number) where {T<:BlasFloat} + return gemm_wrapper!(C, 'N', 'N', A, B, alpha, beta) end - # Complex Matrix times real matrix: We use that it is generally faster to reinterpret the # first matrix as a real matrix and carry out real matrix matrix multiply for elty in (Float32,Float64) @@ -297,12 +302,12 @@ julia> lmul!(F.Q, B) lmul!(A, B) @inline function mul!(C::StridedMatrix{T}, transA::Transpose{<:Any,<:StridedVecOrMat{T}}, B::StridedVecOrMat{T}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasFloat} + alpha::Number, beta::Number) where {T<:BlasFloat} A = transA.parent if A===B - return syrk_wrapper!(C, 'T', A, MulAddMul(alpha, beta)) + return syrk_wrapper!(C, 'T', A, alpha, beta) else - return gemm_wrapper!(C, 'T', 'N', A, B, MulAddMul(alpha, beta)) + return gemm_wrapper!(C, 'T', 'N', A, B, alpha, beta) end end @inline function mul!(C::AbstractMatrix, transA::Transpose{<:Any,<:AbstractVecOrMat}, B::AbstractVecOrMat, @@ -312,19 +317,19 @@ end end @inline function mul!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, transB::Transpose{<:Any,<:StridedVecOrMat{T}}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasFloat} + alpha::Number, beta::Number) where {T<:BlasFloat} B = transB.parent if A===B - return syrk_wrapper!(C, 'N', A, MulAddMul(alpha, beta)) + return syrk_wrapper!(C, 'N', A, alpha, beta) else - return gemm_wrapper!(C, 'N', 'T', A, B, MulAddMul(alpha, beta)) + return gemm_wrapper!(C, 'N', 'T', A, B, alpha, beta) end end # Complex matrix times transposed real matrix. Reinterpret the first matrix to real for efficiency. for elty in (Float32,Float64) @eval begin @inline function mul!(C::StridedMatrix{Complex{$elty}}, A::StridedVecOrMat{Complex{$elty}}, transB::Transpose{<:Any,<:StridedVecOrMat{$elty}}, - alpha::Union{$elty, Bool}, beta::Union{$elty, Bool}) + alpha::Real, beta::Real) Afl = reinterpret($elty, A) Cfl = reinterpret($elty, C) mul!(Cfl, Afl, transB, alpha, beta) @@ -344,7 +349,7 @@ end alpha::Number, beta::Number) where {T<:BlasFloat} A = transA.parent B = transB.parent - return gemm_wrapper!(C, 'T', 'T', A, B, MulAddMul(alpha, beta)) + return gemm_wrapper!(C, 'T', 'T', A, B, alpha, beta) end @inline function mul!(C::AbstractMatrix, transA::Transpose{<:Any,<:AbstractVecOrMat}, transB::Transpose{<:Any,<:AbstractVecOrMat}, alpha::Number, beta::Number) @@ -354,10 +359,10 @@ end end @inline function mul!(C::StridedMatrix{T}, transA::Transpose{<:Any,<:StridedVecOrMat{T}}, transB::Adjoint{<:Any,<:StridedVecOrMat{T}}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasFloat} + alpha::Number, beta::Number) where {T<:BlasFloat} A = transA.parent B = transB.parent - return gemm_wrapper!(C, 'T', 'C', A, B, MulAddMul(alpha, beta)) + return gemm_wrapper!(C, 'T', 'C', A, B, alpha, beta) end @inline function mul!(C::AbstractMatrix, transA::Transpose{<:Any,<:AbstractVecOrMat}, transB::Adjoint{<:Any,<:AbstractVecOrMat}, alpha::Number, beta::Number) @@ -367,17 +372,17 @@ end end @inline function mul!(C::StridedMatrix{T}, adjA::Adjoint{<:Any,<:StridedVecOrMat{T}}, B::StridedVecOrMat{T}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasReal} + alpha::Real, beta::Real) where {T<:BlasReal} A = adjA.parent return mul!(C, transpose(A), B, alpha, beta) end @inline function mul!(C::StridedMatrix{T}, adjA::Adjoint{<:Any,<:StridedVecOrMat{T}}, B::StridedVecOrMat{T}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasComplex} + alpha::Number, beta::Number) where {T<:BlasComplex} A = adjA.parent if A===B - return herk_wrapper!(C, 'C', A, MulAddMul(alpha, beta)) + return herk_wrapper!(C, 'C', A, alpha, beta) else - return gemm_wrapper!(C, 'C', 'N', A, B, MulAddMul(alpha, beta)) + return gemm_wrapper!(C, 'C', 'N', A, B, alpha, beta) end end @inline function mul!(C::AbstractMatrix, adjA::Adjoint{<:Any,<:AbstractVecOrMat}, B::AbstractVecOrMat, @@ -392,12 +397,12 @@ end return mul!(C, A, transpose(B), alpha, beta) end @inline function mul!(C::StridedMatrix{T}, A::StridedVecOrMat{T}, adjB::Adjoint{<:Any,<:StridedVecOrMat{T}}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasComplex} + alpha::Number, beta::Number) where {T<:BlasComplex} B = adjB.parent if A === B - return herk_wrapper!(C, 'N', A, MulAddMul(alpha, beta)) + return herk_wrapper!(C, 'N', A, alpha, beta) else - return gemm_wrapper!(C, 'N', 'C', A, B, MulAddMul(alpha, beta)) + return gemm_wrapper!(C, 'N', 'C', A, B, alpha, beta) end end @inline function mul!(C::AbstractMatrix, A::AbstractVecOrMat, adjB::Adjoint{<:Any,<:AbstractVecOrMat}, @@ -407,10 +412,10 @@ end end @inline function mul!(C::StridedMatrix{T}, adjA::Adjoint{<:Any,<:StridedVecOrMat{T}}, adjB::Adjoint{<:Any,<:StridedVecOrMat{T}}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasFloat} + alpha::Number, beta::Number) where {T<:BlasFloat} A = adjA.parent B = adjB.parent - return gemm_wrapper!(C, 'C', 'C', A, B, MulAddMul(alpha, beta)) + return gemm_wrapper!(C, 'C', 'C', A, B, alpha, beta) end @inline function mul!(C::AbstractMatrix, adjA::Adjoint{<:Any,<:AbstractVecOrMat}, adjB::Adjoint{<:Any,<:AbstractVecOrMat}, alpha::Number, beta::Number) @@ -445,7 +450,7 @@ end end function gemv!(y::StridedVector{T}, tA::AbstractChar, A::StridedVecOrMat{T}, x::StridedVector{T}, - alpha::Union{T, Bool} = true, beta::Union{T, Bool} = false) where T<:BlasFloat + α::Number=true, β::Number=false) where {T<:BlasFloat} mA, nA = lapack_size(tA, A) if nA != length(x) throw(DimensionMismatch("second dimension of A, $nA, does not match length of x, $(length(x))")) @@ -457,16 +462,19 @@ function gemv!(y::StridedVector{T}, tA::AbstractChar, A::StridedVecOrMat{T}, x:: return y end if nA == 0 - return _rmul_or_fill!(y, beta) + return _rmul_or_fill!(y, β) end - if stride(A, 1) == 1 && stride(A, 2) >= size(A, 1) + + alpha, beta = promote(α, β, zero(T)) + if alpha isa Union{Bool,T} && beta isa Union{Bool,T} && stride(A, 1) == 1 && stride(A, 2) >= size(A, 1) return BLAS.gemv!(tA, alpha, A, x, beta, y) + else + return generic_matvecmul!(y, tA, A, x, MulAddMul(α, β)) end - return generic_matvecmul!(y, tA, A, x, MulAddMul(alpha, beta)) end function syrk_wrapper!(C::StridedMatrix{T}, tA::AbstractChar, A::StridedVecOrMat{T}, - _add::MulAddMul = MulAddMul()) where T<:BlasFloat + α::Number=true, β::Number=false) where {T<:BlasFloat} nC = checksquare(C) if tA == 'T' (nA, mA) = size(A,1), size(A,2) @@ -478,24 +486,33 @@ function syrk_wrapper!(C::StridedMatrix{T}, tA::AbstractChar, A::StridedVecOrMat if nC != mA throw(DimensionMismatch("output matrix has size: $(nC), but should have size $(mA)")) end - if mA == 0 || nA == 0 || iszero(_add.alpha) - return _rmul_or_fill!(C, _add.beta) + if mA == 0 || nA == 0 || iszero(α) + return _rmul_or_fill!(C, β) end if mA == 2 && nA == 2 - return matmul2x2!(C, tA, tAt, A, A, _add) + return matmul2x2!(C, tA, tAt, A, A, MulAddMul(α, β)) end if mA == 3 && nA == 3 - return matmul3x3!(C, tA, tAt, A, A, _add) - end - - if stride(A, 1) == stride(C, 1) == 1 && stride(A, 2) >= size(A, 1) && stride(C, 2) >= size(C, 1) - return copytri!(BLAS.syrk!('U', tA, _add.alpha, A, _add.beta, C), 'U') + return matmul3x3!(C, tA, tAt, A, A, MulAddMul(α, β)) + end + + # BLAS.syrk! only updates symmetric C + # alternatively, make non-zero β a show-stopper for BLAS.syrk! + if iszero(β) || issymmetric(C) + alpha, beta = promote(α, β, zero(T)) + if (alpha isa Union{Bool,T} && + beta isa Union{Bool,T} && + stride(A, 1) == stride(C, 1) == 1 && + stride(A, 2) >= size(A, 1) && + stride(C, 2) >= size(C, 1)) + return copytri!(BLAS.syrk!('U', tA, alpha, A, beta, C), 'U') + end end - return generic_matmatmul!(C, tA, tAt, A, A, _add) + return gemm_wrapper!(C, tA, tAt, A, A, α, β) end function herk_wrapper!(C::Union{StridedMatrix{T}, StridedMatrix{Complex{T}}}, tA::AbstractChar, A::Union{StridedVecOrMat{T}, StridedVecOrMat{Complex{T}}}, - _add::MulAddMul = MulAddMul()) where T<:BlasReal + α::Number=true, β::Number=false) where {T<:BlasReal} nC = checksquare(C) if tA == 'C' (nA, mA) = size(A,1), size(A,2) @@ -508,27 +525,34 @@ function herk_wrapper!(C::Union{StridedMatrix{T}, StridedMatrix{Complex{T}}}, tA throw(DimensionMismatch("output matrix has size: $(nC), but should have size $(mA)")) end if mA == 0 || nA == 0 - return _rmul_or_fill!(C, _add.beta) + return _rmul_or_fill!(C, β) end if mA == 2 && nA == 2 - return matmul2x2!(C, tA, tAt, A, A, _add) + return matmul2x2!(C, tA, tAt, A, A, MulAddMul(α, β)) end if mA == 3 && nA == 3 - return matmul3x3!(C, tA, tAt, A, A, _add) + return matmul3x3!(C, tA, tAt, A, A, MulAddMul(α, β)) end # Result array does not need to be initialized as long as beta==0 # C = Matrix{T}(undef, mA, mA) - if stride(A, 1) == stride(C, 1) == 1 && stride(A, 2) >= size(A, 1) && stride(C, 2) >= size(C, 1) - return copytri!(BLAS.herk!('U', tA, _add.alpha, A, _add.beta, C), 'U', true) + if iszero(β) || issymmetric(C) + alpha, beta = promote(α, β, zero(T)) + if (alpha isa Union{Bool,T} && + beta isa Union{Bool,T} && + stride(A, 1) == stride(C, 1) == 1 && + stride(A, 2) >= size(A, 1) && + stride(C, 2) >= size(C, 1)) + return copytri!(BLAS.herk!('U', tA, alpha, A, beta, C), 'U', true) + end end - return generic_matmatmul!(C, tA, tAt, A, A, _add) + return gemm_wrapper!(C, tA, tAt, A, A, α, β) end function gemm_wrapper(tA::AbstractChar, tB::AbstractChar, A::StridedVecOrMat{T}, - B::StridedVecOrMat{T}) where T<:BlasFloat + B::StridedVecOrMat{T}) where {T<:BlasFloat} mA, nA = lapack_size(tA, A) mB, nB = lapack_size(tB, B) C = similar(B, T, mA, nB) @@ -537,7 +561,7 @@ end function gemm_wrapper!(C::StridedVecOrMat{T}, tA::AbstractChar, tB::AbstractChar, A::StridedVecOrMat{T}, B::StridedVecOrMat{T}, - _add::MulAddMul = MulAddMul()) where T<:BlasFloat + α::Number=true, β::Number=false) where {T<:BlasFloat} mA, nA = lapack_size(tA, A) mB, nB = lapack_size(tB, B) @@ -549,24 +573,30 @@ function gemm_wrapper!(C::StridedVecOrMat{T}, tA::AbstractChar, tB::AbstractChar throw(ArgumentError("output matrix must not be aliased with input matrix")) end - if mA == 0 || nA == 0 || nB == 0 || iszero(_add.alpha) + if mA == 0 || nA == 0 || nB == 0 || iszero(α) if size(C) != (mA, nB) throw(DimensionMismatch("C has dimensions $(size(C)), should have ($mA,$nB)")) end - return _rmul_or_fill!(C, _add.beta) + return _rmul_or_fill!(C, β) end if mA == 2 && nA == 2 && nB == 2 - return matmul2x2!(C, tA, tB, A, B, _add) + return matmul2x2!(C, tA, tB, A, B, MulAddMul(α, β)) end if mA == 3 && nA == 3 && nB == 3 - return matmul3x3!(C, tA, tB, A, B, _add) + return matmul3x3!(C, tA, tB, A, B, MulAddMul(α, β)) end - if stride(A, 1) == stride(B, 1) == stride(C, 1) == 1 && stride(A, 2) >= size(A, 1) && stride(B, 2) >= size(B, 1) && stride(C, 2) >= size(C, 1) - return BLAS.gemm!(tA, tB, _add.alpha, A, B, _add.beta, C) + alpha, beta = promote(α, β, zero(T)) + if (alpha isa Union{Bool,T} && + beta isa Union{Bool,T} && + stride(A, 1) == stride(B, 1) == stride(C, 1) == 1 && + stride(A, 2) >= size(A, 1) && + stride(B, 2) >= size(B, 1) && + stride(C, 2) >= size(C, 1)) + return BLAS.gemm!(tA, tB, alpha, A, B, beta, C) end - generic_matmatmul!(C, tA, tB, A, B, _add) + generic_matmatmul!(C, tA, tB, A, B, MulAddMul(α, β)) end # blas.jl defines matmul for floats; other integer and mixed precision @@ -677,7 +707,7 @@ const Bbuf = [Vector{UInt8}(undef, tilebufsize)] const Cbuf = [Vector{UInt8}(undef, tilebufsize)] function generic_matmatmul!(C::AbstractMatrix, tA, tB, A::AbstractMatrix, B::AbstractMatrix, - _add::MulAddMul = MulAddMul()) + _add::MulAddMul=MulAddMul()) mA, nA = lapack_size(tA, A) mB, nB = lapack_size(tB, B) mC, nC = size(C) @@ -708,7 +738,8 @@ function _generic_matmatmul!(C::AbstractVecOrMat{R}, tA, tB, A::AbstractVecOrMat if size(C,1) != mA || size(C,2) != nB throw(DimensionMismatch("result C has dimensions $(size(C)), needs ($mA,$nB)")) end - if isempty(A) || isempty(B) || iszero(_add.alpha) + + if iszero(_add.alpha) || isempty(A) || isempty(B) return _rmul_or_fill!(C, _add.beta) end diff --git a/stdlib/LinearAlgebra/src/qr.jl b/stdlib/LinearAlgebra/src/qr.jl index 48949aa393284..15c6bf2d5bc0a 100644 --- a/stdlib/LinearAlgebra/src/qr.jl +++ b/stdlib/LinearAlgebra/src/qr.jl @@ -421,9 +421,9 @@ end function getproperty(F::QR, d::Symbol) m, n = size(F) - if d == :R + if d === :R return triu!(getfield(F, :factors)[1:min(m,n), 1:n]) - elseif d == :Q + elseif d === :Q return QRPackedQ(getfield(F, :factors), F.τ) else getfield(F, d) @@ -431,9 +431,9 @@ function getproperty(F::QR, d::Symbol) end function getproperty(F::QRCompactWY, d::Symbol) m, n = size(F) - if d == :R + if d === :R return triu!(getfield(F, :factors)[1:min(m,n), 1:n]) - elseif d == :Q + elseif d === :Q return QRCompactWYQ(getfield(F, :factors), F.T) else getfield(F, d) @@ -444,13 +444,13 @@ Base.propertynames(F::Union{QR,QRCompactWY}, private::Bool=false) = function getproperty(F::QRPivoted{T}, d::Symbol) where T m, n = size(F) - if d == :R + if d === :R return triu!(getfield(F, :factors)[1:min(m,n), 1:n]) - elseif d == :Q + elseif d === :Q return QRPackedQ(getfield(F, :factors), F.τ) - elseif d == :p + elseif d === :p return getfield(F, :jpvt) - elseif d == :P + elseif d === :P p = F.p n = length(p) P = zeros(T, n, n) diff --git a/stdlib/LinearAlgebra/src/schur.jl b/stdlib/LinearAlgebra/src/schur.jl index 9457f71b415af..9ea03b0bf12a3 100644 --- a/stdlib/LinearAlgebra/src/schur.jl +++ b/stdlib/LinearAlgebra/src/schur.jl @@ -149,9 +149,9 @@ schur(A::LowerTriangular) = schur(copyto!(similar(parent(A)), A)) schur(A::Tridiagonal) = schur(Matrix(A)) function getproperty(F::Schur, d::Symbol) - if d == :Schur + if d === :Schur return getfield(F, :T) - elseif d == :vectors + elseif d === :vectors return getfield(F, :Z) else getfield(F, d) @@ -305,15 +305,15 @@ ordschur(gschur::GeneralizedSchur, select::Union{Vector{Bool},BitVector}) = GeneralizedSchur(_ordschur(gschur.S, gschur.T, gschur.Q, gschur.Z, select)...) function getproperty(F::GeneralizedSchur, d::Symbol) - if d == :values + if d === :values return getfield(F, :α) ./ getfield(F, :β) - elseif d == :alpha + elseif d === :alpha return getfield(F, :α) - elseif d == :beta + elseif d === :beta return getfield(F, :β) - elseif d == :left + elseif d === :left return getfield(F, :Q) - elseif d == :right + elseif d === :right return getfield(F, :Z) else getfield(F, d) diff --git a/stdlib/LinearAlgebra/src/structuredbroadcast.jl b/stdlib/LinearAlgebra/src/structuredbroadcast.jl index d449e66da74bf..220608c85236a 100644 --- a/stdlib/LinearAlgebra/src/structuredbroadcast.jl +++ b/stdlib/LinearAlgebra/src/structuredbroadcast.jl @@ -40,6 +40,17 @@ Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:UnitLowerTriangular}, ::Struc Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:UnitUpperTriangular}, ::StructuredMatrixStyle{<:Union{Diagonal,UpperTriangular,UnitUpperTriangular}}) = StructuredMatrixStyle{UpperTriangular}() +Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Union{LowerTriangular,UnitLowerTriangular}}, ::StructuredMatrixStyle{<:Union{UpperTriangular,UnitUpperTriangular}}) = + StructuredMatrixStyle{Matrix}() +Broadcast.BroadcastStyle(::StructuredMatrixStyle{<:Union{UpperTriangular,UnitUpperTriangular}}, ::StructuredMatrixStyle{<:Union{LowerTriangular,UnitLowerTriangular}}) = + StructuredMatrixStyle{Matrix}() + +# Make sure that `StructuredMatrixStyle{<:Matrix}` doesn't ever end up falling +# through and give back `DefaultArrayStyle{2}` +Broadcast.BroadcastStyle(T::StructuredMatrixStyle{<:Matrix}, ::StructuredMatrixStyle) = T +Broadcast.BroadcastStyle(::StructuredMatrixStyle, T::StructuredMatrixStyle{<:Matrix}) = T +Broadcast.BroadcastStyle(T::StructuredMatrixStyle{<:Matrix}, ::StructuredMatrixStyle{<:Matrix}) = T + # All other combinations fall back to the default style Broadcast.BroadcastStyle(::StructuredMatrixStyle, ::StructuredMatrixStyle) = DefaultArrayStyle{2}() @@ -69,6 +80,8 @@ structured_broadcast_alloc(bc, ::Type{<:UnitLowerTriangular}, ::Type{ElType}, n) UnitLowerTriangular(Array{ElType}(undef, n, n)) structured_broadcast_alloc(bc, ::Type{<:UnitUpperTriangular}, ::Type{ElType}, n) where {ElType} = UnitUpperTriangular(Array{ElType}(undef, n, n)) +structured_broadcast_alloc(bc, ::Type{<:Matrix}, ::Type{ElType}, n) where {ElType} = + Matrix(Array{ElType}(undef, n, n)) # A _very_ limited list of structure-preserving functions known at compile-time. This list is # derived from the formerly-implemented `broadcast` methods in 0.6. Note that this must diff --git a/stdlib/LinearAlgebra/src/svd.jl b/stdlib/LinearAlgebra/src/svd.jl index 53cdc2441f5ba..843235a615d3b 100644 --- a/stdlib/LinearAlgebra/src/svd.jl +++ b/stdlib/LinearAlgebra/src/svd.jl @@ -202,7 +202,7 @@ function svd(A::Transpose; full::Bool = false, alg::Algorithm = default_svd_alg( end function getproperty(F::SVD, d::Symbol) - if d == :V + if d === :V return getfield(F, :Vt)' else return getfield(F, d) @@ -339,7 +339,32 @@ julia> B = [0. 1.; 1. 0.] 0.0 1.0 1.0 0.0 -julia> F = svd(A, B); +julia> F = svd(A, B) +GeneralizedSVD{Float64,Array{Float64,2}} +U factor: +2×2 Array{Float64,2}: + 1.0 0.0 + 0.0 1.0 +V factor: +2×2 Array{Float64,2}: + -0.0 -1.0 + 1.0 0.0 +Q factor: +2×2 Array{Float64,2}: + 1.0 0.0 + 0.0 1.0 +D1 factor: +2×2 SparseArrays.SparseMatrixCSC{Float64,Int64} with 2 stored entries: + [1, 1] = 0.707107 + [2, 2] = 0.707107 +D2 factor: +2×2 SparseArrays.SparseMatrixCSC{Float64,Int64} with 2 stored entries: + [1, 1] = 0.707107 + [2, 2] = 0.707107 +R0 factor: +2×2 Array{Float64,2}: + 1.41421 0.0 + 0.0 -1.41421 julia> F.U*F.D1*F.R0*F.Q' 2×2 Array{Float64,2}: @@ -501,13 +526,13 @@ svd(x::Number, y::Number) = svd(fill(x, 1, 1), fill(y, 1, 1)) FV = getfield(F, :V) FQ = getfield(F, :Q) FR = getfield(F, :R) - if d == :alpha + if d === :alpha return Fa - elseif d == :beta + elseif d === :beta return Fb - elseif d == :vals || d == :S + elseif d === :vals || d === :S return Fa[1:Fk + Fl] ./ Fb[1:Fk + Fl] - elseif d == :D1 + elseif d === :D1 m = size(FU, 1) if m - Fk - Fl >= 0 return [Matrix{T}(I, Fk, Fk) zeros(T, Fk, Fl) ; @@ -516,7 +541,7 @@ svd(x::Number, y::Number) = svd(fill(x, 1, 1), fill(y, 1, 1)) else return [Matrix{T}(I, m, Fk) [zeros(T, Fk, m - Fk); Diagonal(Fa[Fk + 1:m])] zeros(T, m, Fk + Fl - m)] end - elseif d == :D2 + elseif d === :D2 m = size(FU, 1) p = size(FV, 1) if m - Fk - Fl >= 0 @@ -524,7 +549,7 @@ svd(x::Number, y::Number) = svd(fill(x, 1, 1), fill(y, 1, 1)) else return [zeros(T, p, Fk) [Diagonal(Fb[Fk + 1:m]); zeros(T, Fk + p - m, m - Fk)] [zeros(T, m - Fk, Fk + Fl - m); Matrix{T}(I, Fk + p - m, Fk + Fl - m)]] end - elseif d == :R0 + elseif d === :R0 n = size(FQ, 1) return [zeros(T, Fk + Fl, n - Fk - Fl) FR] else @@ -535,6 +560,22 @@ end Base.propertynames(F::GeneralizedSVD) = (:alpha, :beta, :vals, :S, :D1, :D2, :R0, fieldnames(typeof(F))...) +function show(io::IO, mime::MIME{Symbol("text/plain")}, F::GeneralizedSVD{<:Any,<:AbstractArray}) + summary(io, F); println(io) + println(io, "U factor:") + show(io, mime, F.U) + println(io, "\nV factor:") + show(io, mime, F.V) + println(io, "\nQ factor:") + show(io, mime, F.Q) + println(io, "\nD1 factor:") + show(io, mime, F.D1) + println(io, "\nD2 factor:") + show(io, mime, F.D2) + println(io, "\nR0 factor:") + show(io, mime, F.R0) +end + """ svdvals!(A, B) diff --git a/stdlib/LinearAlgebra/src/symmetric.jl b/stdlib/LinearAlgebra/src/symmetric.jl index caa943e74367d..423331011fc03 100644 --- a/stdlib/LinearAlgebra/src/symmetric.jl +++ b/stdlib/LinearAlgebra/src/symmetric.jl @@ -469,34 +469,88 @@ for f in (:+, :-) end ## Matvec -@inline mul!(y::StridedVector{T}, A::Symmetric{T,<:StridedMatrix}, x::StridedVector{T}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasFloat} = - BLAS.symv!(A.uplo, alpha, A.data, x, beta, y) -@inline mul!(y::StridedVector{T}, A::Hermitian{T,<:StridedMatrix}, x::StridedVector{T}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasReal} = - BLAS.symv!(A.uplo, alpha, A.data, x, beta, y) -@inline mul!(y::StridedVector{T}, A::Hermitian{T,<:StridedMatrix}, x::StridedVector{T}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasComplex} = - BLAS.hemv!(A.uplo, alpha, A.data, x, beta, y) +@inline function mul!(y::StridedVector{T}, A::Symmetric{T,<:StridedMatrix}, x::StridedVector{T}, + α::Number, β::Number) where {T<:BlasFloat} + alpha, beta = promote(α, β, zero(T)) + if alpha isa Union{Bool,T} && beta isa Union{Bool,T} + return BLAS.symv!(A.uplo, alpha, A.data, x, beta, y) + else + return generic_matvecmul!(y, 'N', A, x, MulAddMul(α, β)) + end +end +@inline function mul!(y::StridedVector{T}, A::Hermitian{T,<:StridedMatrix}, x::StridedVector{T}, + α::Number, β::Number) where {T<:BlasReal} + alpha, beta = promote(α, β, zero(T)) + if alpha isa Union{Bool,T} && beta isa Union{Bool,T} + return BLAS.symv!(A.uplo, alpha, A.data, x, beta, y) + else + return generic_matvecmul!(y, 'N', A, x, MulAddMul(α, β)) + end +end +@inline function mul!(y::StridedVector{T}, A::Hermitian{T,<:StridedMatrix}, x::StridedVector{T}, + α::Number, β::Number) where {T<:BlasComplex} + alpha, beta = promote(α, β, zero(T)) + if alpha isa Union{Bool,T} && beta isa Union{Bool,T} + return BLAS.hemv!(A.uplo, alpha, A.data, x, beta, y) + else + return generic_matvecmul!(y, 'N', A, x, MulAddMul(α, β)) + end +end ## Matmat -@inline mul!(C::StridedMatrix{T}, A::Symmetric{T,<:StridedMatrix}, B::StridedMatrix{T}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasFloat} = - BLAS.symm!('L', A.uplo, alpha, A.data, B, beta, C) -@inline mul!(C::StridedMatrix{T}, A::StridedMatrix{T}, B::Symmetric{T,<:StridedMatrix}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasFloat} = - BLAS.symm!('R', B.uplo, alpha, B.data, A, beta, C) -@inline mul!(C::StridedMatrix{T}, A::Hermitian{T,<:StridedMatrix}, B::StridedMatrix{T}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasReal} = - BLAS.symm!('L', A.uplo, alpha, A.data, B, beta, C) -@inline mul!(C::StridedMatrix{T}, A::StridedMatrix{T}, B::Hermitian{T,<:StridedMatrix}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasReal} = - BLAS.symm!('R', B.uplo, alpha, B.data, A, beta, C) -@inline mul!(C::StridedMatrix{T}, A::Hermitian{T,<:StridedMatrix}, B::StridedMatrix{T}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasComplex} = - BLAS.hemm!('L', A.uplo, alpha, A.data, B, beta, C) -@inline mul!(C::StridedMatrix{T}, A::StridedMatrix{T}, B::Hermitian{T,<:StridedMatrix}, - alpha::Union{T, Bool}, beta::Union{T, Bool}) where {T<:BlasComplex} = - BLAS.hemm!('R', B.uplo, alpha, B.data, A, beta, C) +@inline function mul!(C::StridedMatrix{T}, A::Symmetric{T,<:StridedMatrix}, B::StridedMatrix{T}, + α::Number, β::Number) where {T<:BlasFloat} + alpha, beta = promote(α, β, zero(T)) + if alpha isa Union{Bool,T} && beta isa Union{Bool,T} + return BLAS.symm!('L', A.uplo, alpha, A.data, B, beta, C) + else + return generic_matmatmul!(C, 'N', 'N', A, B, MulAddMul(alpha, beta)) + end +end +@inline function mul!(C::StridedMatrix{T}, A::StridedMatrix{T}, B::Symmetric{T,<:StridedMatrix}, + α::Number, β::Number) where {T<:BlasFloat} + alpha, beta = promote(α, β, zero(T)) + if alpha isa Union{Bool,T} && beta isa Union{Bool,T} + return BLAS.symm!('R', B.uplo, alpha, B.data, A, beta, C) + else + return generic_matmatmul!(C, 'N', 'N', A, B, MulAddMul(alpha, beta)) + end +end +@inline function mul!(C::StridedMatrix{T}, A::Hermitian{T,<:StridedMatrix}, B::StridedMatrix{T}, + α::Number, β::Number) where {T<:BlasReal} + alpha, beta = promote(α, β, zero(T)) + if alpha isa Union{Bool,T} && beta isa Union{Bool,T} + return BLAS.symm!('L', A.uplo, alpha, A.data, B, beta, C) + else + return generic_matmatmul!(C, 'N', 'N', A, B, MulAddMul(alpha, beta)) + end +end +@inline function mul!(C::StridedMatrix{T}, A::StridedMatrix{T}, B::Hermitian{T,<:StridedMatrix}, + α::Number, β::Number) where {T<:BlasReal} + alpha, beta = promote(α, β, zero(T)) + if alpha isa Union{Bool,T} && beta isa Union{Bool,T} + return BLAS.symm!('R', B.uplo, alpha, B.data, A, beta, C) + else + return generic_matmatmul!(C, 'N', 'N', A, B, MulAddMul(alpha, beta)) + end +end +@inline function mul!(C::StridedMatrix{T}, A::Hermitian{T,<:StridedMatrix}, B::StridedMatrix{T}, + α::Number, β::Number) where {T<:BlasComplex} + alpha, beta = promote(α, β, zero(T)) + if alpha isa Union{Bool,T} && beta isa Union{Bool,T} + return BLAS.hemm!('L', A.uplo, alpha, A.data, B, beta, C) + else + return generic_matmatmul!(C, 'N', 'N', A, B, MulAddMul(alpha, beta)) + end +end +@inline function mul!(C::StridedMatrix{T}, A::StridedMatrix{T}, B::Hermitian{T,<:StridedMatrix}, + α::Number, β::Number) where {T<:BlasComplex} + alpha, beta = promote(α, β, zero(T)) + if alpha isa Union{Bool,T} && beta isa Union{Bool,T} + return BLAS.hemm!('R', B.uplo, alpha, B.data, A, beta, C) + else + return generic_matmatmul!(C, 'N', 'N', A, B, MulAddMul(alpha, beta)) + end +end *(A::HermOrSym, B::HermOrSym) = A * copyto!(similar(parent(B)), B) diff --git a/stdlib/LinearAlgebra/test/addmul.jl b/stdlib/LinearAlgebra/test/addmul.jl index 87704a432ef32..42529f3f4f334 100644 --- a/stdlib/LinearAlgebra/test/addmul.jl +++ b/stdlib/LinearAlgebra/test/addmul.jl @@ -1,3 +1,5 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + module TestAddmul using Base: rtoldefault diff --git a/stdlib/LinearAlgebra/test/adjtrans.jl b/stdlib/LinearAlgebra/test/adjtrans.jl index d0e82aafa78bf..16b4db98911ac 100644 --- a/stdlib/LinearAlgebra/test/adjtrans.jl +++ b/stdlib/LinearAlgebra/test/adjtrans.jl @@ -504,4 +504,25 @@ using .Main.OffsetArrays @test_throws BoundsError s[1, 4] end +@testset "specialized conj of Adjoint/Transpose" begin + realmat = [1 2; 3 4] + complexmat = ComplexF64[1+im 2; 3 4-im] + nested = [[complexmat] [-complexmat]; [0complexmat] [3complexmat]] + @testset "AdjOrTrans{...,$(typeof(i))}" for i in ( + realmat, vec(realmat), + complexmat, vec(complexmat), + nested, vec(nested), + ) + for (t,type) in ((transpose, Adjoint), (adjoint, Transpose)) + M = t(i) + @test conj(M) isa type + @test conj(M) == conj(collect(M)) + @test conj(conj(M)) === M + end + end + # test if `conj(transpose(::Hermitian))` is a no-op + hermitian = Hermitian([1 2+im; 2-im 3]) + @test conj(transpose(hermitian)) === hermitian +end + end # module TestAdjointTranspose diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index d55fbc32a7242..7883564c9c3f7 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -468,4 +468,34 @@ end end end +@testset "multiplication of bidiagonal and triangular matrix" begin + n = 5 + for eltyB in (Int, ComplexF64) + if eltyB == Int + BU = Bidiagonal(rand(1:7, n), rand(1:7, n - 1), :U) + BL = Bidiagonal(rand(1:7, n), rand(1:7, n - 1), :L) + else + BU = Bidiagonal(randn(eltyB, n), randn(eltyB, n - 1), :U) + BL = Bidiagonal(randn(eltyB, n), randn(eltyB, n - 1), :L) + end + for eltyT in (Int, ComplexF64) + for TriT in (LowerTriangular, UnitLowerTriangular, UpperTriangular, UnitUpperTriangular) + if eltyT == Int + T = TriT(rand(1:7, n, n)) + else + T = TriT(randn(eltyT, n, n)) + end + for B in (BU, BL) + MB = Matrix(B) + MT = Matrix(T) + for transB in (identity, adjoint, transpose), transT in (identity, adjoint, transpose) + @test transB(B) * transT(T) ≈ transB(MB) * transT(MT) + @test transT(T) * transB(B) ≈ transT(MT) * transB(MB) + end + end + end + end + end +end + end # module TestBidiagonal diff --git a/stdlib/LinearAlgebra/test/cholesky.jl b/stdlib/LinearAlgebra/test/cholesky.jl index 86bae3fa98401..5959db043cd73 100644 --- a/stdlib/LinearAlgebra/test/cholesky.jl +++ b/stdlib/LinearAlgebra/test/cholesky.jl @@ -336,9 +336,10 @@ end d = abs.(randn(3)) .+ 0.1 D = Diagonal(d) CD = cholesky(D) + CM = cholesky(Matrix(D)) @test CD isa Cholesky{Float64} - @test CD.U isa UpperTriangular{Float64} - @test CD.U == Diagonal(.√d) + @test CD.U == Diagonal(.√d) == CM.U + @test D ≈ CD.L * CD.U @test CD.info == 0 # real, failing @@ -347,13 +348,12 @@ end @test Dnpd.info == 2 # complex - d = cis.(rand(3) .* 2*π) - d .*= abs.(randn(3) .+ 0.1) - D = Diagonal(d) + D = complex(D) CD = cholesky(D) + CM = cholesky(Matrix(D)) @test CD isa Cholesky{Complex{Float64}} - @test CD.U isa UpperTriangular{Complex{Float64}} - @test CD.U == Diagonal(.√d) + @test CD.U == Diagonal(.√d) == CM.U + @test D ≈ CD.L * CD.U @test CD.info == 0 # complex, failing @@ -392,4 +392,11 @@ end end +@testset "issue #33704, casting low-rank CholeskyPivoted to Matrix" begin + A = randn(1,8) + B = A'A + C = cholesky(B, Val(true), check=false) + @test B ≈ Matrix(C) +end + end # module TestCholesky diff --git a/stdlib/LinearAlgebra/test/dense.jl b/stdlib/LinearAlgebra/test/dense.jl index ccba7234f1c53..3305a9284343b 100644 --- a/stdlib/LinearAlgebra/test/dense.jl +++ b/stdlib/LinearAlgebra/test/dense.jl @@ -29,6 +29,16 @@ Random.seed!(1234321) @test_throws ArgumentError cond(a,3) end end + @testset "Singular matrices" for p in (1, 2, Inf) + @test cond(zeros(Int, 2, 2), p) == Inf + @test cond(zeros(2, 2), p) == Inf + @test cond([0 0; 1 1], p) == Inf + @test cond([0. 0.; 1. 1.], p) == Inf + end + @testset "Issue #33547, condition number of 2x2 matrix" begin + M = [1.0 -2.0; -2.0 -1.5] + @test cond(M, 1) ≈ 2.227272727272727 + end end areal = randn(n,n)/2 diff --git a/stdlib/LinearAlgebra/test/lu.jl b/stdlib/LinearAlgebra/test/lu.jl index 28f26367e5141..7db0d7c7d320e 100644 --- a/stdlib/LinearAlgebra/test/lu.jl +++ b/stdlib/LinearAlgebra/test/lu.jl @@ -265,10 +265,6 @@ end @test_throws DomainError logdet([1 1; 1 -1]) end -@testset "Issue 21453" begin - @test_throws ArgumentError LinearAlgebra._cond1Inf(lu(randn(5,5)), 2, 2.0) -end - @testset "REPL printing" begin bf = IOBuffer() show(bf, "text/plain", lu(Matrix(I, 4, 4))) diff --git a/stdlib/LinearAlgebra/test/matmul.jl b/stdlib/LinearAlgebra/test/matmul.jl index 46dc2aea3a903..587dc66e4e6a9 100644 --- a/stdlib/LinearAlgebra/test/matmul.jl +++ b/stdlib/LinearAlgebra/test/matmul.jl @@ -178,6 +178,18 @@ end end end +@testset "mixed Blas-non-Blas matmul" begin + AA = rand(-10:10,6,6) + BB = rand(Float64,6,6) + CC = zeros(Float64,6,6) + for A in (copy(AA), view(AA, 1:6, 1:6)), B in (copy(BB), view(BB, 1:6, 1:6)), C in (copy(CC), view(CC, 1:6, 1:6)) + @test LinearAlgebra.mul!(C, A, B) == A*B + @test LinearAlgebra.mul!(C, transpose(A), transpose(B)) == transpose(A)*transpose(B) + @test LinearAlgebra.mul!(C, A, adjoint(B)) == A*transpose(B) + @test LinearAlgebra.mul!(C, adjoint(A), B) == transpose(A)*B + end +end + @testset "matrix algebra with subarrays of floats (stride != 1)" begin A = reshape(map(Float64,1:20),5,4) Aref = A[1:2:end,1:2:end] @@ -597,4 +609,19 @@ end @test D ≈ C end +@testset "multiplication of empty matrices without calling zero" begin + r, c = rand(0:9, 2) + A = collect(Number, rand(r, c)) + B = rand(c, 0) + C = A * B + @test size(C) == (r, 0) + @test_throws MethodError zero(eltype(C)) +end + +@testset "Issue #33873: genmatmul! with empty operands" begin + @test Matrix{Any}(undef, 0, 2) * Matrix{Any}(undef, 2, 3) == Matrix{Any}(undef, 0, 3) + @test_throws MethodError Matrix{Any}(undef, 2, 0) * Matrix{Any}(undef, 0, 3) + @test Matrix{Int}(undef, 2, 0) * Matrix{Int}(undef, 0, 3) == zeros(Int, 2, 3) +end + end # module TestMatmul diff --git a/stdlib/LinearAlgebra/test/structuredbroadcast.jl b/stdlib/LinearAlgebra/test/structuredbroadcast.jl index c3aa70e2c964e..deaa5caa7311c 100644 --- a/stdlib/LinearAlgebra/test/structuredbroadcast.jl +++ b/stdlib/LinearAlgebra/test/structuredbroadcast.jl @@ -14,7 +14,8 @@ using Test, LinearAlgebra T = Tridiagonal(rand(N - 1), rand(N), rand(N - 1)) U = UpperTriangular(rand(N,N)) L = LowerTriangular(rand(N,N)) - structuredarrays = (D, B, T, U, L) + M = Matrix(rand(N,N)) + structuredarrays = (D, B, T, U, L, M) fstructuredarrays = map(Array, structuredarrays) for (X, fX) in zip(structuredarrays, fstructuredarrays) @test (Q = broadcast(sin, X); typeof(Q) == typeof(X) && Q == broadcast(sin, fX)) @@ -56,6 +57,7 @@ end T = Tridiagonal(rand(N - 1), rand(N), rand(N - 1)) ◣ = LowerTriangular(rand(N,N)) ◥ = UpperTriangular(rand(N,N)) + M = Matrix(rand(N,N)) @test broadcast!(sin, copy(D), D) == Diagonal(sin.(D)) @test broadcast!(sin, copy(Bu), Bu) == Bidiagonal(sin.(Bu), :U) @@ -63,12 +65,14 @@ end @test broadcast!(sin, copy(T), T) == Tridiagonal(sin.(T)) @test broadcast!(sin, copy(◣), ◣) == LowerTriangular(sin.(◣)) @test broadcast!(sin, copy(◥), ◥) == UpperTriangular(sin.(◥)) + @test broadcast!(sin, copy(M), M) == Matrix(sin.(M)) @test broadcast!(*, copy(D), D, A) == Diagonal(broadcast(*, D, A)) @test broadcast!(*, copy(Bu), Bu, A) == Bidiagonal(broadcast(*, Bu, A), :U) @test broadcast!(*, copy(Bl), Bl, A) == Bidiagonal(broadcast(*, Bl, A), :L) @test broadcast!(*, copy(T), T, A) == Tridiagonal(broadcast(*, T, A)) @test broadcast!(*, copy(◣), ◣, A) == LowerTriangular(broadcast(*, ◣, A)) @test broadcast!(*, copy(◥), ◥, A) == UpperTriangular(broadcast(*, ◥, A)) + @test broadcast!(*, copy(M), M, A) == Matrix(broadcast(*, M, A)) @test_throws ArgumentError broadcast!(cos, copy(D), D) == Diagonal(sin.(D)) @test_throws ArgumentError broadcast!(cos, copy(Bu), Bu) == Bidiagonal(sin.(Bu), :U) @@ -93,7 +97,8 @@ end T = Tridiagonal(rand(N - 1), rand(N), rand(N - 1)) U = UpperTriangular(rand(N,N)) L = LowerTriangular(rand(N,N)) - structuredarrays = (D, B, T, U, L) + M = Matrix(rand(N,N)) + structuredarrays = (M, D, B, T, U, L) fstructuredarrays = map(Array, structuredarrays) for (X, fX) in zip(structuredarrays, fstructuredarrays) @test (Q = map(sin, X); typeof(Q) == typeof(X) && Q == map(sin, fX)) @@ -123,4 +128,22 @@ end end end +@testset "Issue #33397" begin + N = 5 + U = UpperTriangular(rand(N, N)) + L = LowerTriangular(rand(N, N)) + UnitU = UnitUpperTriangular(rand(N, N)) + UnitL = UnitLowerTriangular(rand(N, N)) + D = Diagonal(rand(N)) + @test U .+ L .+ D == U + L + D + @test L .+ U .+ D == L + U + D + @test UnitU .+ UnitL .+ D == UnitU + UnitL + D + @test UnitL .+ UnitU .+ D == UnitL + UnitU + D + @test U .+ UnitL .+ D == U + UnitL + D + @test L .+ UnitU .+ D == L + UnitU + D + @test L .+ U .+ L .+ U == L + U + L + U + @test U .+ L .+ U .+ L == U + L + U + L + @test L .+ UnitL .+ UnitU .+ U .+ D == L + UnitL + UnitU + U + D + @test L .+ U .+ D .+ D .+ D .+ D == L + U + D + D + D + D +end end diff --git a/stdlib/Logging/docs/src/index.md b/stdlib/Logging/docs/src/index.md index add2985c088e2..c481733fcf60a 100644 --- a/stdlib/Logging/docs/src/index.md +++ b/stdlib/Logging/docs/src/index.md @@ -58,13 +58,18 @@ automatically extracted. Let's examine the user-defined data first: * The *log level* is a broad category for the message that is used for early filtering. There are several standard levels of type [`LogLevel`](@ref); user-defined levels are also possible. - - Use `Debug` for verbose information that could be useful when debugging an - application or module. These events are disabled by default. - - Use `Info` to inform the user about the normal operation of the program. - - Use `Warn` when a potential problem is detected. - - Use `Error` to report errors where the code has enough context to recover - and continue. (When the code doesn't have enough context, an exception or - early return is more appropriate.) + Each is distinct in purpose: + - `Debug` is information intended for the developer of the program. + These events are disabled by default. + - `Info` is for general information to the user. + Think of it as an alternative to using `println` directly. + - `Warn` means something is wrong and action is likely required + but that for now the program is still working. + - `Error` means something is wrong and it is unlikely to be recovered, + at least by this part of the code. + Often this log-level is unneeded as throwing an exception can convey + all the required information. + * The *message* is an object describing the event. By convention `AbstractString`s passed as messages are assumed to be in markdown format. Other types will be displayed using `show(io,mime,obj)` according to the @@ -193,6 +198,24 @@ Similarly, the environment variable can be used to enable debug logging of modules, such as `Pkg`, or module roots (see [`Base.moduleroot`](@ref)). To enable all debug logging, use the special value `all`. +To turn debug logging on from the REPL, set `ENV["JULIA_DEBUG"]` to the +name of the module of interest. Functions defined in the REPL belong to +module `Main`; logging for them can be enabled like this: +```julia-repl +julia> foo() = @debug "foo" +foo (generic function with 1 method) + +julia> foo() + +julia> ENV["JULIA_DEBUG"] = Main +Main + +julia> foo() +┌ Debug: foo +└ @ Main REPL[1]:1 + +``` + ## Writing log events to a file Sometimes it can be useful to write log events to a file. Here is an example diff --git a/stdlib/Makefile b/stdlib/Makefile index a08a672b22422..d6bcfebfed0ce 100644 --- a/stdlib/Makefile +++ b/stdlib/Makefile @@ -17,10 +17,12 @@ $(build_datarootdir)/julia/stdlib/$(VERSDIR): STDLIBS = Base64 CRC32c Dates DelimitedFiles Distributed FileWatching \ Future InteractiveUtils Libdl LibGit2 LinearAlgebra Logging \ Markdown Mmap Printf Profile Random REPL Serialization SHA \ - SharedArrays Sockets SparseArrays Statistics SuiteSparse Test Unicode UUIDs -STDLIBS_EXT = Pkg + SharedArrays Sockets SparseArrays SuiteSparse Test Unicode UUIDs +STDLIBS_EXT = Pkg Statistics PKG_GIT_URL := git://github.com/JuliaLang/Pkg.jl.git PKG_TAR_URL = https://api.github.com/repos/JuliaLang/Pkg.jl/tarball/$1 +STATISTICS_GIT_URL := git://github.com/JuliaLang/Statistics.jl.git +STATISTICS_TAR_URL = https://api.github.com/repos/JuliaLang/Statistics.jl/tarball/$1 $(foreach module, $(STDLIBS_EXT), $(eval $(call stdlib-external,$(module),$(shell echo $(module) | tr a-z A-Z)))) @@ -33,3 +35,6 @@ getall get: $(addprefix get-, $(STDLIBS_EXT)) install: $(addprefix install-, $(STDLIBS_EXT)) $(STDLIBS_LINK_TARGETS) clean: $(addprefix clean-, $(STDLIBS_EXT)) $(CLEAN_TARGETS) distclean: $(addprefix distclean-, $(STDLIBS_EXT)) clean + +DEP_LIBS_STAGED := $(STDLIBS_EXT) +include $(JULIAHOME)/deps/tools/uninstallers.mk diff --git a/stdlib/Markdown/src/GitHub/table.jl b/stdlib/Markdown/src/GitHub/table.jl index b8652e00121e4..08a375cca2fb8 100644 --- a/stdlib/Markdown/src/GitHub/table.jl +++ b/stdlib/Markdown/src/GitHub/table.jl @@ -65,8 +65,10 @@ function html(io::IO, md::Table) withtag(io, :table) do for (i, row) in enumerate(md.rows) withtag(io, :tr) do - for c in md.rows[i] - withtag(io, i == 1 ? :th : :td) do + for (j, c) in enumerate(md.rows[i]) + alignment = md.align[j] + alignment = alignment === :l ? "left" : alignment === :r ? "right" : "center" + withtag(io, i == 1 ? :th : :td, ("align", alignment)) do htmlinline(io, c) end end @@ -81,9 +83,9 @@ colwidths(rows; len = length, min = 0) = reduce((x,y) -> max.(x,y), [min; convert(Vector{Vector{Int}}, mapmap(len, rows))]) padding(width, twidth, a) = - a == :l ? (0, twidth - width) : - a == :r ? (twidth - width, 0) : - a == :c ? (floor(Int, (twidth-width)/2), ceil(Int, (twidth-width)/2)) : + a === :l ? (0, twidth - width) : + a === :r ? (twidth - width, 0) : + a === :c ? (floor(Int, (twidth-width)/2), ceil(Int, (twidth-width)/2)) : error("Invalid alignment $a") function padcells!(rows, align; len = length, min = 0) @@ -97,9 +99,9 @@ function padcells!(rows, align; len = length, min = 0) end _dash(width, align) = - align == :l ? ":" * "-"^width * " " : - align == :r ? " " * "-"^width * ":" : - align == :c ? ":" * "-"^width * ":" : + align === :l ? ":" * "-"^width * " " : + align === :r ? " " * "-"^width * ":" : + align === :c ? ":" * "-"^width * ":" : throw(ArgumentError("Invalid alignment $align")) function plain(io::IO, md::Table) diff --git a/stdlib/Markdown/test/runtests.jl b/stdlib/Markdown/test/runtests.jl index 251ad4cc4bd45..47c977682ffc8 100644 --- a/stdlib/Markdown/test/runtests.jl +++ b/stdlib/Markdown/test/runtests.jl @@ -510,6 +510,7 @@ let text = """, table = Markdown.parse(text) @test text == Markdown.plain(table) + @test Markdown.html(table) == """
MarkdownTableTest
foobarbaz
barbazfoo
\n""" end let text = """ @@ -519,6 +520,7 @@ let text = """, table = Markdown.parse(text) @test text == Markdown.plain(table) + @test Markdown.html(table) == """
ab
x | y2
\n""" end # LaTeX extension diff --git a/stdlib/Pkg.version b/stdlib/Pkg.version index 75a9613e4348a..117a72d87a42e 100644 --- a/stdlib/Pkg.version +++ b/stdlib/Pkg.version @@ -1,2 +1,2 @@ PKG_BRANCH = master -PKG_SHA1 = 04ab197340696404272c691135a7fba38b64f0e6 +PKG_SHA1 = 0c2dddd40e4d7492d2a7337be54c345011e5f1e1 diff --git a/stdlib/Printf/src/Printf.jl b/stdlib/Printf/src/Printf.jl index 810610d653e72..46d70508d273b 100644 --- a/stdlib/Printf/src/Printf.jl +++ b/stdlib/Printf/src/Printf.jl @@ -1183,7 +1183,7 @@ end ### external printf interface ### is_str_expr(ex) = - isa(ex,Expr) && (ex.head == :string || (ex.head == :macrocall && isa(ex.args[1],Symbol) && + isa(ex,Expr) && (ex.head === :string || (ex.head === :macrocall && isa(ex.args[1],Symbol) && endswith(string(ex.args[1]),"str"))) function _printf(macroname, io, fmt, args) @@ -1192,7 +1192,7 @@ function _printf(macroname, io, fmt, args) has_splatting = false for arg in args - if isa(arg, Expr) && arg.head == :... + if isa(arg, Expr) && arg.head === :... has_splatting = true break end diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index 429bb80fe4f60..057bb4d287a40 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -444,9 +444,9 @@ function print_flat(io::IO, lilist::Vector{StackFrame}, n::Vector{Int}, m::Vector{Int}, cols::Int, filenamemap::Dict{Symbol,String}, fmt::ProfileFormat) - if fmt.sortedby == :count + if fmt.sortedby === :count p = sortperm(n) - elseif fmt.sortedby == :overhead + elseif fmt.sortedby === :overhead p = sortperm(m) else p = liperm(lilist) @@ -634,7 +634,7 @@ function tree!(root::StackFrameTree{T}, all::Vector{UInt64}, lidict::Union{LineI startframe = i else pushfirst!(build, parent) - if recur === :flat || recur == :flatc + if recur === :flat || recur === :flatc # Rewind the `parent` tree back, if this exact ip was already present *higher* in the current tree found = false for j in 1:(startframe - i) @@ -745,13 +745,13 @@ function print_tree(io::IO, bt::StackFrameTree{T}, cols::Int, fmt::ProfileFormat # Generate the string for each line strs = tree_format(nexts, level, cols, maxes, filenamemap, T === UInt64) # Recurse to the next level - if fmt.sortedby == :count + if fmt.sortedby === :count counts = collect(frame.count for frame in nexts) p = sortperm(counts) - elseif fmt.sortedby == :overhead + elseif fmt.sortedby === :overhead m = collect(frame.overhead for frame in nexts) p = sortperm(m) - elseif fmt.sortedby == :flat_count + elseif fmt.sortedby === :flat_count m = collect(frame.flat_count for frame in nexts) p = sortperm(m) else diff --git a/stdlib/REPL/docs/src/index.md b/stdlib/REPL/docs/src/index.md index 323d0f13fbbe7..a3532a42d5af5 100644 --- a/stdlib/REPL/docs/src/index.md +++ b/stdlib/REPL/docs/src/index.md @@ -52,6 +52,38 @@ If it is enabled, you can try it out by pasting the code block above this paragr the REPL. This feature does not work on the standard Windows command prompt due to its limitation at detecting when a paste occurs. +Objects are printed at the REPL using the [`show`](@ref) function with a specific [`IOContext`](@ref). +In particular, the `:limit` attribute is set to `true`. +Other attributes can receive in certain `show` methods a default value if it's not already set, +like `:compact`. +It's possible, as an experimental feature, to specify the attributes used by the REPL via the +`Base.active_repl.options.iocontext` dictionary (associating values to attributes). For example: + +```julia-repl +julia> rand(2, 2) +2×2 Array{Float64,2}: + 0.8833 0.329197 + 0.719708 0.59114 + +julia> show(IOContext(stdout, :compact => false), "text/plain", rand(2, 2)) + 0.43540323669187075 0.15759787870609387 + 0.2540832269192739 0.4597637838786053 +julia> Base.active_repl.options.iocontext[:compact] = false; + +julia> rand(2, 2) +2×2 Array{Float64,2}: + 0.2083967319174056 0.13330606013126012 + 0.6244375177790158 0.9777957560761545 +``` + +In order to define automatically the values of this dictionary at startup time, one can use the +[`atreplinit`](@ref) function in the `~/.julia/config/startup.jl` file, for example: +```julia +atreplinit() do repl + repl.options.iocontext[:compact] = false +end +``` + ### Help mode When the cursor is at the beginning of the line, the prompt can be changed to a help mode by typing @@ -174,15 +206,18 @@ to do so), or pressing Esc and then the key. | `^Q` | Write a number in REPL and press `^Q` to open editor at corresponding stackframe or method | | `meta-Left Arrow` | indent the current line on the left | | `meta-Right Arrow` | indent the current line on the right | - +| `meta-.` | insert last word from previous history entry | ### Customizing keybindings Julia's REPL keybindings may be fully customized to a user's preferences by passing a dictionary to `REPL.setup_interface`. The keys of this dictionary may be characters or strings. The key `'*'` refers to the default action. Control plus character `x` bindings are indicated with `"^x"`. -Meta plus `x` can be written `"\\Mx"`. The values of the custom keymap must be `nothing` (indicating -that the input should be ignored) or functions that accept the signature `(PromptState, AbstractREPL, Char)`. +Meta plus `x` can be written `"\\M-x"` or `"\ex"`, and Control plus `x` can be written +`"\\C-x"` or `"^x"`. +The values of the custom keymap must be `nothing` (indicating +that the input should be ignored) or functions that accept the signature +`(PromptState, AbstractREPL, Char)`. The `REPL.setup_interface` function must be called before the REPL is initialized, by registering the operation with [`atreplinit`](@ref) . For example, to bind the up and down arrow keys to move through history without prefix search, one could put the following code in `~/.julia/config/startup.jl`: diff --git a/stdlib/REPL/src/LineEdit.jl b/stdlib/REPL/src/LineEdit.jl index 0a71488859819..b536471cda75c 100644 --- a/stdlib/REPL/src/LineEdit.jl +++ b/stdlib/REPL/src/LineEdit.jl @@ -94,10 +94,10 @@ options(s::PromptState) = end function setmark(s::MIState, guess_region_active::Bool=true) - was_active = is_region_active(s) - guess_region_active && activate_region(s, s.key_repeats > 0 ? :mark : :off) + refresh = set_action!(s, :setmark) + s.current_action === :setmark && s.key_repeats > 0 && activate_region(s, :mark) mark(buffer(s)) - was_active && refresh_line(s) + refresh && refresh_line(s) nothing end @@ -239,30 +239,35 @@ function preserve_active(command::Symbol) command ∈ [:edit_indent, :edit_transpose_lines_down!, :edit_transpose_lines_up!] end +# returns whether the "active region" status changed visibly, +# i.e. whether there should be a visual refresh function set_action!(s::MIState, command::Symbol) # if a command is already running, don't update the current_action field, # as the caller is used as a helper function - s.current_action == :unknown || return + s.current_action === :unknown || return false + + active = region_active(s) + + ## record current action + s.current_action = command ## handle activeness of the region - is_shift_move(cmd) = startswith(String(cmd), "shift_") - if is_shift_move(command) - if region_active(s) != :shift - setmark(s, false) + if startswith(String(command), "shift_") # shift-move command + if active !== :shift + setmark(s) # s.current_action must already have been set activate_region(s, :shift) # NOTE: if the region was already active from a non-shift # move (e.g. ^Space^Space), the region is visibly changed + return active !== :off # active status is reset end elseif !(preserve_active(command) || - command_group(command) == :movement && region_active(s) == :mark) + command_group(command) === :movement && region_active(s) === :mark) # if we move after a shift-move, the region is de-activated # (e.g. like emacs behavior) deactivate_region(s) + return active !== :off end - - ## record current action - s.current_action = command - nothing + false end set_action!(s, command::Symbol) = nothing @@ -375,11 +380,13 @@ refresh_multi_line(s::ModeState; kw...) = refresh_multi_line(terminal(s), s; kw. refresh_multi_line(termbuf::TerminalBuffer, s::ModeState; kw...) = refresh_multi_line(termbuf, terminal(s), s; kw...) refresh_multi_line(termbuf::TerminalBuffer, term, s::ModeState; kw...) = (@assert term == terminal(s); refresh_multi_line(termbuf,s; kw...)) -function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf::IOBuffer, state::InputAreaState, prompt = ""; +function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf::IOBuffer, + state::InputAreaState, prompt = ""; indent = 0, region_active = false) _clear_input_area(termbuf, state) cols = width(terminal) + rows = height(terminal) curs_row = -1 # relative to prompt (1-based) curs_pos = -1 # 1-based column position of the cursor cur_row = 0 # count of the number of rows @@ -395,16 +402,31 @@ function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf # Now go through the buffer line by line seek(buf, 0) moreinput = true # add a blank line if there is a trailing newline on the last line + lastline = false # indicates when to stop printing lines, even when there are potentially + # more (for the case where rows is too small to print everything) + # Note: when there are too many lines for rows, we still print the first lines + # even if they are going to not be visible in the end: for simplicity, but + # also because it does the 'right thing' when the window is resized while moreinput - l = readline(buf, keep=true) - moreinput = endswith(l, "\n") + line = readline(buf, keep=true) + moreinput = endswith(line, "\n") + if rows == 1 && line_pos <= sizeof(line) - moreinput + # we special case rows == 1, as otherwise by the time the cursor is seen to + # be in the current line, it's too late to chop the '\n' away + lastline = true + curs_row = 1 + curs_pos = lindent + line_pos + end + if moreinput && lastline # we want to print only one "visual" line, so + line = chomp(line) # don't include the trailing "\n" + end # We need to deal with on-screen characters, so use textwidth to compute occupied columns - llength = textwidth(l) - slength = sizeof(l) + llength = textwidth(line) + slength = sizeof(line) cur_row += 1 # lwrite: what will be written to termbuf - lwrite = region_active ? highlight_region(l, regstart, regstop, written, slength) : - l + lwrite = region_active ? highlight_region(line, regstart, regstop, written, slength) : + line written += slength cmove_col(termbuf, lindent + 1) write(termbuf, lwrite) @@ -414,7 +436,9 @@ function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf line_pos -= slength # '\n' gets an extra pos # in this case, we haven't yet written the cursor position if line_pos < 0 || !moreinput - num_chars = (line_pos >= 0 ? llength : textwidth(l[1:prevind(l, line_pos + slength + 1)])) + num_chars = line_pos >= 0 ? + llength : + textwidth(line[1:prevind(line, line_pos + slength + 1)]) curs_row, curs_pos = divrem(lindent + num_chars - 1, cols) curs_row += cur_row curs_pos += 1 @@ -434,6 +458,12 @@ function refresh_multi_line(termbuf::TerminalBuffer, terminal::UnixTerminal, buf end cur_row += div(max(lindent + llength + miscountnl - 1, 0), cols) lindent = indent < 0 ? lindent : indent + + lastline && break + if curs_row >= 0 && cur_row + 1 >= rows && # when too many lines, + cur_row - curs_row + 1 >= rows ÷ 2 # center the cursor + lastline = true + end end seek(buf, buf_pos) @@ -647,7 +677,7 @@ function edit_move_down(s) end function edit_shift_move(s::MIState, move_function::Function) - @assert command_group(move_function) == :movement + @assert command_group(move_function) === :movement set_action!(s, Symbol(:shift_, move_function)) return move_function(s) end @@ -999,7 +1029,7 @@ function edit_transpose_words(buf::IOBuffer, mode=:emacs) mode in [:readline, :emacs] || throw(ArgumentError("`mode` must be `:readline` or `:emacs`")) pos = position(buf) - if mode == :emacs + if mode === :emacs char_move_word_left(buf) char_move_word_right(buf) end @@ -1078,7 +1108,7 @@ function edit_lower_case(s) end function edit_title_case(s) set_action!(s, :edit_title_case) - return edit_replace_word_right(s, uppercasefirst) + return edit_replace_word_right(s, titlecase) end function edit_replace_word_right(s, replace::Function) @@ -1270,12 +1300,12 @@ function normalize_key(key::AbstractString) c, i = iterate(key, i) if c == 'C' c, i = iterate(key, i) - @assert c == '-' + c == '-' || error("the Control key specifier must start with \"\\\\C-\"") c, i = iterate(key, i) write(buf, uppercase(c)-64) elseif c == 'M' c, i = iterate(key, i) - @assert c == '-' + c == '-' || error("the Meta key specifier must start with \"\\\\M-\"") c, i = iterate(key, i) write(buf, '\e') write(buf, c) @@ -1327,9 +1357,17 @@ struct KeyAlias KeyAlias(seq) = new(normalize_key(seq)) end -function match_input(k::Function, s, term, cs, keymap) +function match_input(f::Function, s, term, cs, keymap) update_key_repeats(s, cs) - return keymap_fcn(k, String(cs)) + c = String(cs) + return function (s, p) + r = Base.invokelatest(f, s, p, c) + if isa(r, Symbol) + return r + else + return :ok + end + end end match_input(k::Nothing, s, term, cs, keymap) = (s,p) -> return :ok @@ -1339,29 +1377,17 @@ match_input(k::KeyAlias, s, term, cs, keymap) = function match_input(k::Dict, s, term=terminal(s), cs=Char[], keymap = k) # if we run out of characters to match before resolving an action, # return an empty keymap function - eof(term) && return keymap_fcn(nothing, "") + eof(term) && return (s, p) -> :abort c = read(term, Char) # Ignore any `wildcard` as this is used as a # placeholder for the wildcard (see normalize_key("*")) - c == wildcard && return keymap_fcn(nothing, "") + c == wildcard && return (s, p) -> :ok push!(cs, c) key = haskey(k, c) ? c : wildcard # if we don't match on the key, look for a default action then fallback on 'nothing' to ignore return match_input(get(k, key, nothing), s, term, cs, keymap) end -keymap_fcn(f::Nothing, c) = (s, p) -> return :ok -function keymap_fcn(f::Function, c) - return function (s, p) - r = Base.invokelatest(f, s, p, c) - if isa(r, Symbol) - return r - else - return :ok - end - end -end - update_key_repeats(s, keystroke) = nothing function update_key_repeats(s::MIState, keystroke) s.key_repeats = s.previous_key == keystroke ? s.key_repeats + 1 : 0 @@ -1942,6 +1968,25 @@ function move_line_end(buf::IOBuffer) nothing end +edit_insert_last_word(s::MIState) = + edit_insert(s, get_last_word(IOBuffer(mode(s).hist.history[end]))) + +function get_last_word(buf::IOBuffer) + move_line_end(buf) + char_move_word_left(buf) + posbeg = position(buf) + char_move_word_right(buf) + posend = position(buf) + buf = take!(buf) + word = String(buf[posbeg+1:posend]) + rest = String(buf[posend+1:end]) + lp, rp, lb, rb = count.(.==(('(', ')', '[', ']')), rest) + special = any(in.(('\'', '"', '`'), rest)) + !special && lp == rp && lb == rb ? + word *= rest : + word +end + function commit_line(s) cancel_beep(s) move_input_end(s) @@ -2013,7 +2058,7 @@ end function edit_abort(s, confirm::Bool=options(s).confirm_exit; key="^D") set_action!(s, :edit_abort) - if !confirm || s.last_action == :edit_abort + if !confirm || s.last_action === :edit_abort println(terminal(s)) return :abort else @@ -2080,6 +2125,7 @@ AnyDict( "\eOc" => "\ef", # Meta Enter "\e\r" => (s,o...)->edit_insert_newline(s), + "\e." => (s,o...)->edit_insert_last_word(s), "\e\n" => "\e\r", "^_" => (s,o...)->edit_undo!(s), "\e_" => (s,o...)->edit_redo!(s), @@ -2176,6 +2222,8 @@ const prefix_history_keymap = merge!( "\e[*" => "*", "\eO*" => "*", "\e[1;5*" => "*", # Ctrl-Arrow + "\e[1;2*" => "*", # Shift-Arrow + "\e[1;3*" => "*", # Meta-Arrow "\e[200~" => "*" ), # VT220 editing commands @@ -2403,8 +2451,9 @@ function prompt!(term::TextTerminal, prompt::ModalInterface, s::MIState = init_s transition(s, old_state) status = :done end - status != :ignore && (s.last_action = s.current_action) + 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 diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index a20b4d027c4b4..514ce37c14400 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -129,7 +129,12 @@ end function display(d::REPLDisplay, mime::MIME"text/plain", x) io = outstream(d.repl) get(io, :color, false) && write(io, answer_color(d.repl)) - show(IOContext(io, :limit => true, :module => Main), mime, x) + if isdefined(d.repl, :options) && isdefined(d.repl.options, :iocontext) + # this can override the :limit property set initially + io = foldl(IOContext, d.repl.options.iocontext, + init=IOContext(io, :limit => true, :module => Main)) + end + show(io, mime, x) println(io) nothing end @@ -243,7 +248,7 @@ function run_frontend(repl::BasicREPL, backend::REPLBackendRef) end end ast = Base.parse_input_line(line) - (isa(ast,Expr) && ast.head == :incomplete) || break + (isa(ast,Expr) && ast.head === :incomplete) || break end if !isempty(line) response = eval_with_backend(ast, backend) @@ -283,6 +288,8 @@ mutable struct Options auto_indent_bracketed_paste::Bool # set to true if terminal knows paste mode # cancel auto-indent when next character is entered within this time frame : auto_indent_time_threshold::Float64 + # default IOContext settings at the REPL + iocontext::Dict{Symbol,Any} end Options(; @@ -299,13 +306,16 @@ Options(; auto_indent = true, auto_indent_tmp_off = false, auto_indent_bracketed_paste = false, - auto_indent_time_threshold = 0.005) = + auto_indent_time_threshold = 0.005, + iocontext = Dict{Symbol,Any}()) = Options(hascolor, extra_keymap, tabwidth, kill_ring_max, region_animation_duration, beep_duration, beep_blink, beep_maxduration, beep_colors, beep_use_current, backspace_align, backspace_adjust, confirm_exit, - auto_indent, auto_indent_tmp_off, auto_indent_bracketed_paste, auto_indent_time_threshold) + auto_indent, auto_indent_tmp_off, auto_indent_bracketed_paste, + auto_indent_time_threshold, + iocontext) # for use by REPLs not having an options field const GlobalOptions = Options() @@ -955,7 +965,7 @@ function setup_interface( end end ast, pos = Meta.parse(input, oldpos, raise=false, depwarn=false) - if (isa(ast, Expr) && (ast.head == :error || ast.head == :incomplete)) || + if (isa(ast, Expr) && (ast.head === :error || ast.head === :incomplete)) || (pos > ncodeunits(input) && !endswith(input, '\n')) # remaining text is incomplete (an error, or parser ran to the end but didn't stop with a newline): # Insert all the remaining text as one line (might be empty) diff --git a/stdlib/REPL/src/REPLCompletions.jl b/stdlib/REPL/src/REPLCompletions.jl index 442ffda3edc65..6b15996b51d97 100644 --- a/stdlib/REPL/src/REPLCompletions.jl +++ b/stdlib/REPL/src/REPLCompletions.jl @@ -337,7 +337,7 @@ end # will show it consist of Expr, QuoteNode's and Symbol's which all needs to # be handled differently to iterate down to get the value of whitespace_chars. function get_value(sym::Expr, fn) - sym.head != :. && return (nothing, false) + sym.head !== :. && return (nothing, false) for ex in sym.args fn, found = get_value(ex, fn) !found && return (nothing, false) @@ -552,12 +552,12 @@ function project_deps_get_completion_candidates(pkgstarts::String, project_file: for line in eachline(io) if occursin(Base.re_section, line) state = occursin(Base.re_section_deps, line) ? :deps : :other - elseif state == :top + elseif state === :top if (m = match(Base.re_name_to_string, line)) !== nothing root_name = String(m.captures[1]) startswith(root_name, pkgstarts) && push!(loading_candidates, root_name) end - elseif state == :deps + elseif state === :deps if (m = match(Base.re_key_to_string, line)) !== nothing dep_name = m.captures[1] startswith(dep_name, pkgstarts) && push!(loading_candidates, dep_name) @@ -592,7 +592,7 @@ function completions(string, pos, context_module=Main)::Completions paths, r, success = complete_path(replace(string[r], r"\\ " => " "), pos) - if inc_tag == :string && + if inc_tag === :string && length(paths) == 1 && # Only close if there's a single choice, !isdir(expanduser(replace(string[startpos:prevind(string, first(r))] * paths[1].path, r"\\ " => " "))) && # except if it's a directory @@ -610,7 +610,7 @@ function completions(string, pos, context_module=Main)::Completions # Make sure that only bslash_completions is working on strings inc_tag==:string && return String[], 0:-1, false - if inc_tag == :other && should_method_complete(partial) + if inc_tag === :other && should_method_complete(partial) frange, method_name_end = find_start_brace(partial) ex = Meta.parse(partial[frange] * ")", raise=false, depwarn=false) @@ -621,7 +621,7 @@ function completions(string, pos, context_module=Main)::Completions return complete_methods(ex, context_module), first(frange):(method_name_end - 1), false end end - elseif inc_tag == :comment + elseif inc_tag === :comment return Completion[], 0:-1, false end diff --git a/stdlib/REPL/src/TerminalMenus/config.jl b/stdlib/REPL/src/TerminalMenus/config.jl index ce2cd3b88bf60..d84a14aac7595 100644 --- a/stdlib/REPL/src/TerminalMenus/config.jl +++ b/stdlib/REPL/src/TerminalMenus/config.jl @@ -29,26 +29,26 @@ function config(;charset::Symbol = :na, supress_output::Union{Nothing, Bool}=nothing, ctrl_c_interrupt::Union{Nothing, Bool}=nothing) - if charset == :ascii + if charset === :ascii cursor = '>' up_arrow = '^' down_arrow = 'v' checked = "[X]" unchecked = "[ ]" - elseif charset == :unicode + elseif charset === :unicode cursor = '→' up_arrow = '↑' down_arrow = '↓' checked = "✓" unchecked = "⬚" - elseif charset == :na + elseif charset === :na else throw(ArgumentError("charset should be :ascii or :unicode, received $charset")) end scroll ∉ [:na, :wrap, :nowrap] && throw(ArgumentError("scroll must be :wrap or :nowrap, received $scroll")) - scroll == :wrap && (CONFIG[:scroll_wrap] = true) - scroll == :nowrap && (CONFIG[:scroll_wrap] = false) + scroll === :wrap && (CONFIG[:scroll_wrap] = true) + scroll === :nowrap && (CONFIG[:scroll_wrap] = false) cursor != '\0' && (CONFIG[:cursor] = cursor) up_arrow != '\0' && (CONFIG[:up_arrow] = up_arrow) diff --git a/stdlib/REPL/src/Terminals.jl b/stdlib/REPL/src/Terminals.jl index f0e03c0a23e40..c8135bb6f2879 100644 --- a/stdlib/REPL/src/Terminals.jl +++ b/stdlib/REPL/src/Terminals.jl @@ -158,7 +158,7 @@ else function hascolor(t::TTYTerminal) startswith(t.term_type, "xterm") && return true try - @static if Sys.KERNEL == :FreeBSD + @static if Sys.KERNEL === :FreeBSD return success(`tput AF 0`) else return success(`tput setaf 0`) diff --git a/stdlib/REPL/test/lineedit.jl b/stdlib/REPL/test/lineedit.jl index 5125b11d97a8e..b0fb4cd08fe57 100644 --- a/stdlib/REPL/test/lineedit.jl +++ b/stdlib/REPL/test/lineedit.jl @@ -606,7 +606,7 @@ end @testset "change case on the right" begin local buf = IOBuffer() - edit_insert(buf, "aa bb CC") + edit_insert(buf, "aa bB CC") seekstart(buf) LineEdit.edit_upper_case(buf) LineEdit.edit_title_case(buf) @@ -878,5 +878,20 @@ end buf.mark = 0 seek(buf, 1) @test transform!(transpose_lines_down_reg!, buf) == ("l2\nl3\nl1", 4, 3) +end +@testset "edit_insert_last_word" begin + get_last_word(str::String) = LineEdit.get_last_word(IOBuffer(str)) + @test get_last_word("1+2") == "2" + @test get_last_word("1+23") == "23" + @test get_last_word("1+2") == "2" + @test get_last_word(""" "a" * "b" """) == "b" + @test get_last_word(""" "a" * 'b' """) == "b" + @test get_last_word(""" "a" * `b` """) == "b" + @test get_last_word("g()") == "g()" + @test get_last_word("g(1, 2)") == "2" + @test get_last_word("g(1, f())") == "f" + @test get_last_word("a[1]") == "1" + @test get_last_word("a[b[]]") == "b" + @test get_last_word("a[]") == "a[]" end diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index 5da586b4586aa..37b775779b3ba 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -66,6 +66,24 @@ end # Writing ^C to the repl will cause sigint, so let's not die on that ccall(:jl_exit_on_sigint, Cvoid, (Cint,), 0) + +# make sure `run_interface` can normally handle `eof` +# without any special handling by the user +fake_repl() do stdin_write, stdout_read, repl + panel = LineEdit.Prompt("test"; + prompt_prefix = "", + prompt_suffix = Base.text_colors[:white], + on_enter = s -> true) + panel.on_done = (s, buf, ok) -> begin + @test !ok + @test bytesavailable(buf) == position(buf) == 0 + nothing + end + repltask = @async REPL.run_interface(repl.t, LineEdit.ModalInterface(Any[panel])) + close(stdin_write) + Base.wait(repltask) +end + # These are integration tests. If you want to unit test test e.g. completion, or # exact LineEdit behavior, put them in the appropriate test files. # Furthermore since we are emulating an entire terminal, there may be control characters @@ -107,45 +125,52 @@ fake_repl() do stdin_write, stdout_read, repl # Test cd feature in shell mode. origpwd = pwd() mktempdir() do tmpdir - # Test `cd`'ing to an absolute path - write(stdin_write, ";") - readuntil(stdout_read, "shell> ") - write(stdin_write, "cd $(escape_string(tmpdir))\n") - readuntil(stdout_read, "cd $(escape_string(tmpdir))") - readuntil(stdout_read, realpath(tmpdir)) - readuntil(stdout_read, "\n") - readuntil(stdout_read, "\n") - @test pwd() == realpath(tmpdir) - - # Test using `cd` to move to the home directory - write(stdin_write, ";") - readuntil(stdout_read, "shell> ") - write(stdin_write, "cd\n") - readuntil(stdout_read, realpath(homedir())) - readuntil(stdout_read, "\n") - readuntil(stdout_read, "\n") - @test pwd() == realpath(homedir()) + try + samefile = Base.Filesystem.samefile + tmpdir_pwd = cd(pwd, tmpdir) + homedir_pwd = cd(pwd, homedir()) - # Test using `-` to jump backward to tmpdir - write(stdin_write, ";") - readuntil(stdout_read, "shell> ") - write(stdin_write, "cd -\n") - readuntil(stdout_read, tmpdir) - readuntil(stdout_read, "\n") - readuntil(stdout_read, "\n") - @test pwd() == realpath(tmpdir) - - # Test using `~` in `cd` commands - if !Sys.iswindows() + # Test `cd`'ing to an absolute path + write(stdin_write, ";") + readuntil(stdout_read, "shell> ") + write(stdin_write, "cd $(escape_string(tmpdir))\n") + readuntil(stdout_read, "cd $(escape_string(tmpdir))") + readuntil(stdout_read, tmpdir_pwd) + readuntil(stdout_read, "\n") + readuntil(stdout_read, "\n") + @test samefile(".", tmpdir) + + # Test using `cd` to move to the home directory + write(stdin_write, ";") + readuntil(stdout_read, "shell> ") + write(stdin_write, "cd\n") + readuntil(stdout_read, homedir_pwd) + readuntil(stdout_read, "\n") + readuntil(stdout_read, "\n") + @test samefile(".", homedir_pwd) + + # Test using `-` to jump backward to tmpdir write(stdin_write, ";") readuntil(stdout_read, "shell> ") - write(stdin_write, "cd ~\n") - readuntil(stdout_read, realpath(homedir())) + write(stdin_write, "cd -\n") + readuntil(stdout_read, tmpdir_pwd) readuntil(stdout_read, "\n") readuntil(stdout_read, "\n") - @test pwd() == realpath(homedir()) + @test samefile(".", tmpdir) + + # Test using `~` (Base.expanduser) in `cd` commands + if !Sys.iswindows() + write(stdin_write, ";") + readuntil(stdout_read, "shell> ") + write(stdin_write, "cd ~\n") + readuntil(stdout_read, homedir_pwd) + readuntil(stdout_read, "\n") + readuntil(stdout_read, "\n") + @test samefile(".", homedir_pwd) + end + finally + cd(origpwd) end - cd(origpwd) end # issue #20482 @@ -221,7 +246,7 @@ fake_repl() do stdin_write, stdout_read, repl write(stdin_write, ";") readuntil(stdout_read, "shell> ") Base.print_shell_escaped(stdin_write, Base.julia_cmd().exec..., special=Base.shell_special) - write(stdin_write, """ -e "println(\\"HI\\")\"""") + write(stdin_write, """ -e "println(\\"HI\\")\" """) readuntil(stdout_read, ")\"") proc_stdout_read, proc_stdout = redirect_stdout() get_stdout = @async read(proc_stdout_read, String) @@ -299,8 +324,16 @@ fake_repl() do stdin_write, stdout_read, repl write(stdin_write, "\e[B\n") readuntil(stdout_read, s2) - # Close REPL ^D - write(stdin_write, '\x04') + # test that prefix history search "passes through" key bindings to parent mode + write(stdin_write, "0x321\n") + readuntil(stdout_read, "0x321") + write(stdin_write, "\e[A\e[1;3C|||") # uparrow (go up history) and then Meta-rightarrow (indent right) + s2 = readuntil(stdout_read, "|||", keep=true) + @test endswith(s2, " 0x321\r\e[13C|||") # should have a space (from Meta-rightarrow) and not + # have a spurious C before ||| (the one here is not spurious!) + + # Delete line (^U) and close REPL (^D) + write(stdin_write, "\x15\x04") Base.wait(repltask) nothing @@ -415,6 +448,11 @@ for prompt = ["TestΠ", () -> randstring(rand(1:10))] s = LineEdit.init_state(repl.t, repl.interface) LineEdit.edit_insert(s, "wip") + # LineEdit functions related to history + LineEdit.edit_insert_last_word(s) + @test buffercontents(LineEdit.buffer(s)) == "wip2" + LineEdit.edit_backspace(s) # remove the "2" + # Test that navigating history skips invalid modes # (in both directions) LineEdit.history_prev(s, hp) @@ -701,16 +739,17 @@ fake_repl() do stdin_write, stdout_read, repl LineEdit.default_keymap, LineEdit.escape_defaults]) c = Condition() - panel.on_done = (s,buf,ok)->begin + panel.on_done = (s, buf, ok) -> begin if !ok - LineEdit.transition(s,:abort) + LineEdit.transition(s, :abort) end line = strip(String(take!(buf))) LineEdit.reset_state(s) - return notify(c,line) + notify(c, line) + nothing end - repltask = @async REPL.run_interface(repl.t, LineEdit.ModalInterface([panel,search_prompt])) + repltask = @async REPL.run_interface(repl.t, LineEdit.ModalInterface(Any[panel, search_prompt])) write(stdin_write,"a\n") @test wait(c) == "a" @@ -1033,3 +1072,21 @@ fake_repl() do stdin_write, stdout_read, repl wait(repltask) @test istaskdone(repltask) end + +fake_repl() do stdin_write, stdout_read, repl + repltask = @async begin + REPL.run_repl(repl) + end + write(stdin_write, "anything\x15\x19\x19") # ^u^y^y : kill line backwards + 2 yanks + s1 = readuntil(stdout_read, "anything") # typed + s2 = readuntil(stdout_read, "anything") # yanked (first ^y) + s3 = readuntil(stdout_read, "anything") # previous yanked refreshed (from second ^y) + s4 = readuntil(stdout_read, "anything", keep=true) # last yanked + # necessary to read at least some part of the buffer, + # for the "region_active" to have time to be updated + + @test LineEdit.state(repl.mistate).region_active == :off + @test s4 == "anything" # no control characters between the last two occurences of "anything" + write(stdin_write, "\x15\x04") + Base.wait(repltask) +end diff --git a/stdlib/Random/src/Random.jl b/stdlib/Random/src/Random.jl index a3ac839677535..fc9469cb1652e 100644 --- a/stdlib/Random/src/Random.jl +++ b/stdlib/Random/src/Random.jl @@ -315,6 +315,9 @@ Pick a random element or array of random elements from the set of values specifi point numbers and to ``[0, 1)+i[0, 1)]`` for complex floating point numbers; `S` defaults to [`Float64`](@ref). +When only one argument is passed besides the optional `rng` and is a `Tuple`, it is interpreted +as a collection of values (`S`) and not as `dims`. + !!! compat "Julia 1.1" Support for `S` as a tuple requires at least Julia 1.1. @@ -330,6 +333,14 @@ julia> using Random julia> rand(MersenneTwister(0), Dict(1=>2, 3=>4)) 1=>2 + +julia> rand((2, 3)) +3 + +julia> rand(Float64, (2, 3)) +2×3 Array{Float64,2}: + 0.999717 0.0143835 0.540787 + 0.696556 0.783855 0.938235 ``` !!! note diff --git a/stdlib/Serialization/src/Serialization.jl b/stdlib/Serialization/src/Serialization.jl index e54f16886dfb1..9f860a08a045f 100644 --- a/stdlib/Serialization/src/Serialization.jl +++ b/stdlib/Serialization/src/Serialization.jl @@ -1372,6 +1372,4 @@ function deserialize(s::AbstractSerializer, ::Type{Base.StackTraces.StackFrame}) return Base.StackTraces.StackFrame(func, file, line, nothing, from_c, inlined, pointer) end -include("precompile.jl") - end diff --git a/stdlib/Serialization/src/precompile.jl b/stdlib/Serialization/src/precompile.jl deleted file mode 100644 index 7bbc18a44c29f..0000000000000 --- a/stdlib/Serialization/src/precompile.jl +++ /dev/null @@ -1,40 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -precompile(Tuple{typeof(Serialization.object_number), Core.TypeName}) -precompile(Tuple{typeof(Serialization.deserialize_array), Serialization.Serializer{Base.PipeEndpoint}}) -precompile(Tuple{typeof(Serialization.deserialize), Base.PipeEndpoint}) -precompile(Tuple{typeof(Serialization.deserialize), Serialization.Serializer{Base.PipeEndpoint}}) -precompile(Tuple{typeof(Serialization.deserialize), Serialization.Serializer{Base.PipeEndpoint}, Type{Int64}}) -precompile(Tuple{typeof(Serialization.deserialize), Serialization.Serializer{Base.PipeEndpoint}, Type{QuoteNode}}) -precompile(Tuple{typeof(Serialization.deserialize), Serialization.Serializer{Base.PipeEndpoint}, Type{String}}) -precompile(Tuple{typeof(Serialization.deserialize), Serialization.Serializer{Base.PipeEndpoint}, Type{UInt64}}) -precompile(Tuple{typeof(Serialization.deserialize_cycle), Serialization.Serializer{Base.PipeEndpoint}, Expr}) -precompile(Tuple{typeof(Serialization.deserialize_datatype), Serialization.Serializer{Base.PipeEndpoint}}) -precompile(Tuple{typeof(Serialization.deserialize_expr), Serialization.Serializer{Base.PipeEndpoint}, Int64}) -precompile(Tuple{typeof(Serialization.handle_deserialize), Serialization.Serializer{Base.PipeEndpoint}, Int32}) -precompile(Tuple{typeof(Serialization.serialize_any), Serialization.Serializer{Base.Pipe}, QuoteNode}) -precompile(Tuple{typeof(Serialization.serialize), Serialization.Serializer{Base.Pipe}, Array{Any, 1}}) -precompile(Tuple{typeof(Serialization.serialize), Serialization.Serializer{Base.Pipe}, Array{String, 1}}) -precompile(Tuple{typeof(Serialization.serialize), Serialization.Serializer{Base.Pipe}, Bool}) -precompile(Tuple{typeof(Serialization.serialize), Serialization.Serializer{Base.Pipe}, Expr}) -precompile(Tuple{typeof(Serialization.serialize), Serialization.Serializer{Base.Pipe}, Int64}) -precompile(Tuple{typeof(Serialization.serialize), Serialization.Serializer{Base.Pipe}, Module}) -precompile(Tuple{typeof(Serialization.serialize), Serialization.Serializer{Base.Pipe}, QuoteNode}) -precompile(Tuple{typeof(Serialization.serialize), Serialization.Serializer{Base.Pipe}, Core.SimpleVector}) -precompile(Tuple{typeof(Serialization.serialize), Serialization.Serializer{Base.Pipe}, String}) -precompile(Tuple{typeof(Serialization.serialize), Serialization.Serializer{Base.Pipe}, Symbol}) -precompile(Tuple{typeof(Serialization.serialize), Serialization.Serializer{Base.Pipe}, Tuple{Int64}}) -precompile(Tuple{typeof(Serialization.serialize), Serialization.Serializer{Base.Pipe}, Tuple{Symbol, UInt64}}) -precompile(Tuple{typeof(Serialization.serialize), Serialization.Serializer{Base.Pipe}, Type{Any}}) -precompile(Tuple{typeof(Serialization.serialize), Serialization.Serializer{Base.Pipe}, Core.TypeName}) -precompile(Tuple{typeof(Serialization.serialize), Serialization.Serializer{Base.Pipe}, UInt64}) -precompile(Tuple{typeof(Serialization.serialize_cycle), Serialization.Serializer{Base.Pipe}, Expr}) -precompile(Tuple{typeof(Serialization.serialize_cycle), Serialization.Serializer{Base.Pipe}, Core.TypeName}) -precompile(Tuple{typeof(Serialization.serialize_cycle), Serialization.Serializer{Base.Pipe}, UInt64}) -precompile(Tuple{typeof(Serialization.serialize_cycle_header), Serialization.Serializer{Base.Pipe}, QuoteNode}) -precompile(Tuple{typeof(Serialization.serialize_mod_names), Serialization.Serializer{Base.Pipe}, Module}) -precompile(Tuple{typeof(Serialization.serialize_type), Serialization.Serializer{Base.Pipe}, DataType}) -precompile(Tuple{typeof(Serialization.serialize_type_data), Serialization.Serializer{Base.Pipe}, Type{Any}, Bool}) -precompile(Tuple{typeof(Serialization.serialize_typename), Serialization.Serializer{Base.Pipe}, Core.TypeName}) -precompile(Tuple{typeof(Serialization.should_send_whole_type), Serialization.Serializer{Base.Pipe}, Type{Any}}) -precompile(Tuple{typeof(Serialization.write_as_tag), Base.Pipe, Int32}) diff --git a/stdlib/SharedArrays/src/SharedArrays.jl b/stdlib/SharedArrays/src/SharedArrays.jl index 91c555dc6757f..c80a74e617227 100644 --- a/stdlib/SharedArrays/src/SharedArrays.jl +++ b/stdlib/SharedArrays/src/SharedArrays.jl @@ -454,7 +454,7 @@ function serialize(s::AbstractSerializer, S::SharedArray) for n in fieldnames(SharedArray) if n in [:s, :pidx, :loc_subarr_1d] writetag(s.io, UNDEFREF_TAG) - elseif n == :refs + elseif n === :refs v = getfield(S, n) if isa(v[1], Future) # convert to ids to avoid distributed GC overhead @@ -612,9 +612,9 @@ function print_shmem_limits(slen) pfx = "kernel" elseif Sys.isapple() pfx = "kern.sysv" - elseif Sys.KERNEL == :FreeBSD || Sys.KERNEL == :DragonFly + elseif Sys.KERNEL === :FreeBSD || Sys.KERNEL === :DragonFly pfx = "kern.ipc" - elseif Sys.KERNEL == :OpenBSD + elseif Sys.KERNEL === :OpenBSD pfx = "kern.shminfo" else # seems NetBSD does not have *.shmall diff --git a/stdlib/SharedArrays/test/runtests.jl b/stdlib/SharedArrays/test/runtests.jl index 2d9030321a63e..ee741aa4a647b 100644 --- a/stdlib/SharedArrays/test/runtests.jl +++ b/stdlib/SharedArrays/test/runtests.jl @@ -213,7 +213,7 @@ d[5,1:2:4,8] .= 19 AA = rand(4,2) A = @inferred(convert(SharedArray, AA)) B = @inferred(convert(SharedArray, copy(AA'))) -@test B*A == AA'*AA +@test B*A ≈ AA'*AA d=SharedArray{Int64,2}((10,10); init = D->fill!(D.loc_subarr_1d, myid()), pids=[id_me, id_other]) d2 = map(x->1, d) diff --git a/stdlib/Sockets/test/runtests.jl b/stdlib/Sockets/test/runtests.jl index 0ed7f5db99cd4..15590fd91eba3 100644 --- a/stdlib/Sockets/test/runtests.jl +++ b/stdlib/Sockets/test/runtests.jl @@ -2,6 +2,26 @@ using Sockets, Random, Test +# set up a watchdog alarm for 10 minutes +# so that we can attempt to get a "friendly" backtrace if something gets stuck +# (although this'll also terminate any attempted debugging session) +# expected test duration is about 5-10 seconds +function killjob(d) + Core.print(Core.stderr, d) + if Sys.islinux() + SIGINFO = 10 + elseif Sys.isbsd() + SIGINFO = 29 + end + if @isdefined(SIGINFO) + ccall(:uv_kill, Cint, (Cint, Cint), getpid(), SIGINFO) + sleep(1) + end + ccall(:uv_kill, Cint, (Cint, Cint), getpid(), Base.SIGTERM) + nothing +end +Timer(t -> killjob("KILLING BY SOCKETS TEST WATCHDOG\n"), 600) + @testset "parsing" begin @test ip"127.0.0.1" == IPv4(127,0,0,1) @test ip"192.0" == IPv4(192,0,0,0) diff --git a/stdlib/SparseArrays/src/abstractsparse.jl b/stdlib/SparseArrays/src/abstractsparse.jl index adb3e84174eb2..ccc9bbec78814 100644 --- a/stdlib/SparseArrays/src/abstractsparse.jl +++ b/stdlib/SparseArrays/src/abstractsparse.jl @@ -13,7 +13,7 @@ abstract type AbstractSparseArray{Tv,Ti,N} <: AbstractArray{Tv,N} end AbstractSparseVector{Tv,Ti} Supertype for one-dimensional sparse arrays (or array-like types) with elements -of type `Tv` and index type `Ti`. Alias for `AbstractSparseArray{Tv,Ti,1}``. +of type `Tv` and index type `Ti`. Alias for `AbstractSparseArray{Tv,Ti,1}`. """ const AbstractSparseVector{Tv,Ti} = AbstractSparseArray{Tv,Ti,1} """ diff --git a/stdlib/SparseArrays/src/linalg.jl b/stdlib/SparseArrays/src/linalg.jl index 3c1a2c1d1a11c..516cabebac507 100644 --- a/stdlib/SparseArrays/src/linalg.jl +++ b/stdlib/SparseArrays/src/linalg.jl @@ -4,9 +4,10 @@ import LinearAlgebra: checksquare using Random: rand! # In matrix-vector multiplication, the correct orientation of the vector is assumed. -const AdjOrTransStridedMatrix{T} = Union{StridedMatrix{T},Adjoint{<:Any,<:StridedMatrix{T}},Transpose{<:Any,<:StridedMatrix{T}}} +const StridedOrTriangularMatrix{T} = Union{StridedMatrix{T}, LowerTriangular{T}, UnitLowerTriangular{T}, UpperTriangular{T}, UnitUpperTriangular{T}} +const AdjOrTransStridedOrTriangularMatrix{T} = Union{StridedOrTriangularMatrix{T},Adjoint{<:Any,<:StridedOrTriangularMatrix{T}},Transpose{<:Any,<:StridedOrTriangularMatrix{T}}} -function mul!(C::StridedVecOrMat, A::AbstractSparseMatrixCSC, B::Union{StridedVector,AdjOrTransStridedMatrix}, α::Number, β::Number) +function mul!(C::StridedVecOrMat, A::AbstractSparseMatrixCSC, B::Union{StridedVector,AdjOrTransStridedOrTriangularMatrix}, α::Number, β::Number) size(A, 2) == size(B, 1) || throw(DimensionMismatch()) size(A, 1) == size(C, 1) || throw(DimensionMismatch()) size(B, 2) == size(C, 2) || throw(DimensionMismatch()) @@ -27,10 +28,10 @@ function mul!(C::StridedVecOrMat, A::AbstractSparseMatrixCSC, B::Union{StridedVe end *(A::AbstractSparseMatrixCSC{TA}, x::StridedVector{Tx}) where {TA,Tx} = (T = promote_op(matprod, TA, Tx); mul!(similar(x, T, size(A, 1)), A, x, true, false)) -*(A::AbstractSparseMatrixCSC{TA}, B::StridedMatrix{Tx}) where {TA,Tx} = +*(A::AbstractSparseMatrixCSC{TA}, B::AdjOrTransStridedOrTriangularMatrix{Tx}) where {TA,Tx} = (T = promote_op(matprod, TA, Tx); mul!(similar(B, T, (size(A, 1), size(B, 2))), A, B, true, false)) -function mul!(C::StridedVecOrMat, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, B::Union{StridedVector,AdjOrTransStridedMatrix}, α::Number, β::Number) +function mul!(C::StridedVecOrMat, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, B::Union{StridedVector,AdjOrTransStridedOrTriangularMatrix}, α::Number, β::Number) A = adjA.parent size(A, 2) == size(C, 1) || throw(DimensionMismatch()) size(A, 1) == size(B, 1) || throw(DimensionMismatch()) @@ -53,10 +54,10 @@ function mul!(C::StridedVecOrMat, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC} end *(adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, x::StridedVector{Tx}) where {Tx} = (T = promote_op(matprod, eltype(adjA), Tx); mul!(similar(x, T, size(adjA, 1)), adjA, x, true, false)) -*(adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, B::AdjOrTransStridedMatrix) = +*(adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, B::AdjOrTransStridedOrTriangularMatrix) = (T = promote_op(matprod, eltype(adjA), eltype(B)); mul!(similar(B, T, (size(adjA, 1), size(B, 2))), adjA, B, true, false)) -function mul!(C::StridedVecOrMat, transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}, B::Union{StridedVector,AdjOrTransStridedMatrix}, α::Number, β::Number) +function mul!(C::StridedVecOrMat, transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}, B::Union{StridedVector,AdjOrTransStridedOrTriangularMatrix}, α::Number, β::Number) A = transA.parent size(A, 2) == size(C, 1) || throw(DimensionMismatch()) size(A, 1) == size(B, 1) || throw(DimensionMismatch()) @@ -79,19 +80,19 @@ function mul!(C::StridedVecOrMat, transA::Transpose{<:Any,<:AbstractSparseMatrix end *(transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}, x::StridedVector{Tx}) where {Tx} = (T = promote_op(matprod, eltype(transA), Tx); mul!(similar(x, T, size(transA, 1)), transA, x, true, false)) -*(transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}, B::AdjOrTransStridedMatrix) = +*(transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}, B::AdjOrTransStridedOrTriangularMatrix) = (T = promote_op(matprod, eltype(transA), eltype(B)); mul!(similar(B, T, (size(transA, 1), size(B, 2))), transA, B, true, false)) # For compatibility with dense multiplication API. Should be deleted when dense multiplication # API is updated to follow BLAS API. -mul!(C::StridedVecOrMat, A::AbstractSparseMatrixCSC, B::Union{StridedVector,AdjOrTransStridedMatrix}) = +mul!(C::StridedVecOrMat, A::AbstractSparseMatrixCSC, B::Union{StridedVector,AdjOrTransStridedOrTriangularMatrix}) = mul!(C, A, B, true, false) -mul!(C::StridedVecOrMat, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, B::Union{StridedVector,AdjOrTransStridedMatrix}) = +mul!(C::StridedVecOrMat, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, B::Union{StridedVector,AdjOrTransStridedOrTriangularMatrix}) = mul!(C, adjA, B, true, false) -mul!(C::StridedVecOrMat, transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}, B::Union{StridedVector,AdjOrTransStridedMatrix}) = +mul!(C::StridedVecOrMat, transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}, B::Union{StridedVector,AdjOrTransStridedOrTriangularMatrix}) = mul!(C, transA, B, true, false) -function mul!(C::StridedVecOrMat, X::AdjOrTransStridedMatrix, A::AbstractSparseMatrixCSC, α::Number, β::Number) +function mul!(C::StridedVecOrMat, X::AdjOrTransStridedOrTriangularMatrix, A::AbstractSparseMatrixCSC, α::Number, β::Number) mX, nX = size(X) nX == size(A, 1) || throw(DimensionMismatch()) mX == size(C, 1) || throw(DimensionMismatch()) @@ -106,10 +107,10 @@ function mul!(C::StridedVecOrMat, X::AdjOrTransStridedMatrix, A::AbstractSparseM end C end -*(X::AdjOrTransStridedMatrix, A::AbstractSparseMatrixCSC{TvA}) where {TvA} = +*(X::AdjOrTransStridedOrTriangularMatrix, A::AbstractSparseMatrixCSC{TvA}) where {TvA} = (T = promote_op(matprod, eltype(X), TvA); mul!(similar(X, T, (size(X, 1), size(A, 2))), X, A, true, false)) -function mul!(C::StridedVecOrMat, X::AdjOrTransStridedMatrix, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, α::Number, β::Number) +function mul!(C::StridedVecOrMat, X::AdjOrTransStridedOrTriangularMatrix, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}, α::Number, β::Number) A = adjA.parent mX, nX = size(X) nX == size(A, 2) || throw(DimensionMismatch()) @@ -125,10 +126,10 @@ function mul!(C::StridedVecOrMat, X::AdjOrTransStridedMatrix, adjA::Adjoint{<:An end C end -*(X::AdjOrTransStridedMatrix, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}) = +*(X::AdjOrTransStridedOrTriangularMatrix, adjA::Adjoint{<:Any,<:AbstractSparseMatrixCSC}) = (T = promote_op(matprod, eltype(X), eltype(adjA)); mul!(similar(X, T, (size(X, 1), size(adjA, 2))), X, adjA, true, false)) -function mul!(C::StridedVecOrMat, X::AdjOrTransStridedMatrix, transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}, α::Number, β::Number) +function mul!(C::StridedVecOrMat, X::AdjOrTransStridedOrTriangularMatrix, transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}, α::Number, β::Number) A = transA.parent mX, nX = size(X) nX == size(A, 2) || throw(DimensionMismatch()) @@ -144,7 +145,7 @@ function mul!(C::StridedVecOrMat, X::AdjOrTransStridedMatrix, transA::Transpose{ end C end -*(X::AdjOrTransStridedMatrix, transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}) = +*(X::AdjOrTransStridedOrTriangularMatrix, transA::Transpose{<:Any,<:AbstractSparseMatrixCSC}) = (T = promote_op(matprod, eltype(X), eltype(transA)); mul!(similar(X, T, (size(X, 1), size(transA, 2))), X, transA, true, false)) function (*)(D::Diagonal, A::AbstractSparseMatrixCSC) diff --git a/stdlib/SparseArrays/src/sparsematrix.jl b/stdlib/SparseArrays/src/sparsematrix.jl index 8334b9e8d70df..8620159846e63 100644 --- a/stdlib/SparseArrays/src/sparsematrix.jl +++ b/stdlib/SparseArrays/src/sparsematrix.jl @@ -3510,7 +3510,7 @@ end # sortSparseMatrixCSC!(A, sortindices = :sortcols) # Sort each column with sort() # sortSparseMatrixCSC!(A, sortindices = :doubletranspose) # Sort with a double transpose function sortSparseMatrixCSC!(A::AbstractSparseMatrixCSC{Tv,Ti}; sortindices::Symbol = :sortcols) where {Tv,Ti} - if sortindices == :doubletranspose + if sortindices === :doubletranspose nB, mB = size(A) B = SparseMatrixCSC(mB, nB, Vector{Ti}(undef, nB+1), similar(rowvals(A)), similar(nonzeros(A))) transpose!(B, A) diff --git a/stdlib/SparseArrays/src/sparsevector.jl b/stdlib/SparseArrays/src/sparsevector.jl index c9f718147bca1..c072943bd9141 100644 --- a/stdlib/SparseArrays/src/sparsevector.jl +++ b/stdlib/SparseArrays/src/sparsevector.jl @@ -1334,7 +1334,7 @@ end for f in [:sum, :maximum, :minimum], op in [:abs, :abs2] SV = :AbstractSparseVector - if f == :minimum + if f === :minimum @eval ($f)(::typeof($op), x::$SV{T}) where {T<:Number} = nnz(x) < length(x) ? ($op)(zero(T)) : ($f)($op, nonzeros(x)) else @eval ($f)(::typeof($op), x::$SV) = ($f)($op, nonzeros(x)) diff --git a/stdlib/SparseArrays/test/forbidproperties.jl b/stdlib/SparseArrays/test/forbidproperties.jl index e44e7bd97e11e..fd182cf065f1a 100644 --- a/stdlib/SparseArrays/test/forbidproperties.jl +++ b/stdlib/SparseArrays/test/forbidproperties.jl @@ -1,3 +1,5 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + using SparseArrays Base.getproperty(S::SparseMatrixCSC, ::Symbol) = error("use accessor function") Base.getproperty(S::SparseVector, ::Symbol) = error("use accessor function") diff --git a/stdlib/SparseArrays/test/sparse.jl b/stdlib/SparseArrays/test/sparse.jl index 1f2b450d5c3ac..071825e00b90f 100644 --- a/stdlib/SparseArrays/test/sparse.jl +++ b/stdlib/SparseArrays/test/sparse.jl @@ -384,6 +384,29 @@ end end end +@testset "multiplication of sparse matrix and triangular matrix" begin + _sparse_test_matrix(n, T) = T == Int ? sparse(rand(0:4, n, n)) : sprandn(T, n, n, 0.6) + _triangular_test_matrix(n, TA, T) = T == Int ? TA(rand(0:9, n, n)) : TA(randn(T, n, n)) + + n = 5 + for T1 in (Int, Float64, ComplexF32) + S = _sparse_test_matrix(n, T1) + MS = Matrix(S) + for T2 in (Int, Float64, ComplexF32) + for TM in (LowerTriangular, UnitLowerTriangular, UpperTriangular, UnitLowerTriangular) + T = _triangular_test_matrix(n, TM, T2) + MT = Matrix(T) + @test isa(T * S, DenseMatrix) + @test isa(S * T, DenseMatrix) + for transT in (identity, adjoint, transpose), transS in (identity, adjoint, transpose) + @test transT(T) * transS(S) ≈ transT(MT) * transS(MS) + @test transS(S) * transT(T) ≈ transS(MS) * transT(MT) + end + end + end + end +end + @testset "Issue #30502" begin @test nnz(sprand(UInt8(16), UInt8(16), 1.0)) == 256 @test nnz(sprand(UInt8(16), UInt8(16), 1.0, ones)) == 256 diff --git a/stdlib/Statistics.version b/stdlib/Statistics.version new file mode 100644 index 0000000000000..4387cc8478470 --- /dev/null +++ b/stdlib/Statistics.version @@ -0,0 +1,2 @@ +STATISTICS_BRANCH = master +STATISTICS_SHA1 = a2203d3b67f7413701be5de251622cb85c9cc69d diff --git a/stdlib/Statistics/Project.toml b/stdlib/Statistics/Project.toml deleted file mode 100644 index 12c967736bbfb..0000000000000 --- a/stdlib/Statistics/Project.toml +++ /dev/null @@ -1,13 +0,0 @@ -name = "Statistics" -uuid = "10745b16-79ce-11e8-11f9-7d13ad32a3b2" - -[deps] -LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e" -SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf" - -[extras] -Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" -Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40" - -[targets] -test = ["Random", "Test"] diff --git a/stdlib/Statistics/docs/src/index.md b/stdlib/Statistics/docs/src/index.md deleted file mode 100644 index 25cdf29216a88..0000000000000 --- a/stdlib/Statistics/docs/src/index.md +++ /dev/null @@ -1,22 +0,0 @@ -# Statistics - -The Statistics module contains basic statistics functionality. - -!!! note - To use any of the examples described below, run `using Statistics` and then the code from the example. - -```@docs -Statistics.std -Statistics.stdm -Statistics.var -Statistics.varm -Statistics.cor -Statistics.cov -Statistics.mean! -Statistics.mean -Statistics.median! -Statistics.median -Statistics.middle -Statistics.quantile! -Statistics.quantile -``` diff --git a/stdlib/Statistics/src/Statistics.jl b/stdlib/Statistics/src/Statistics.jl deleted file mode 100644 index 07151e4784561..0000000000000 --- a/stdlib/Statistics/src/Statistics.jl +++ /dev/null @@ -1,1057 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -""" - Statistics - -Standard library module for basic statistics functionality. -""" -module Statistics - -using LinearAlgebra, SparseArrays - -using Base: has_offset_axes, require_one_based_indexing - -export cor, cov, std, stdm, var, varm, mean!, mean, - median!, median, middle, quantile!, quantile - -##### mean ##### - -""" - mean(itr) - -Compute the mean of all elements in a collection. - -!!! note - If `itr` contains `NaN` or [`missing`](@ref) values, the result is also - `NaN` or `missing` (`missing` takes precedence if array contains both). - Use the [`skipmissing`](@ref) function to omit `missing` entries and compute the - mean of non-missing values. - -# Examples -```jldoctest -julia> mean(1:20) -10.5 - -julia> mean([1, missing, 3]) -missing - -julia> mean(skipmissing([1, missing, 3])) -2.0 -``` -""" -mean(itr) = mean(identity, itr) - -""" - mean(f::Function, itr) - -Apply the function `f` to each element of collection `itr` and take the mean. - -```jldoctest -julia> mean(√, [1, 2, 3]) -1.3820881233139908 - -julia> mean([√1, √2, √3]) -1.3820881233139908 -``` -""" -function mean(f, itr) - y = iterate(itr) - if y === nothing - return Base.mapreduce_empty_iter(f, Base.add_sum, itr, - Base.IteratorEltype(itr)) / 0 - end - count = 1 - value, state = y - f_value = f(value) - total = Base.reduce_first(Base.add_sum, f_value) - y = iterate(itr, state) - while y !== nothing - value, state = y - total += f(value) - count += 1 - y = iterate(itr, state) - end - return total/count -end - -""" - mean(f::Function, A::AbstractArray; dims) - -Apply the function `f` to each element of array `A` and take the mean over dimensions `dims`. - -!!! compat "Julia 1.3" - This method requires at least Julia 1.3. - -```jldoctest -julia> mean(√, [1, 2, 3]) -1.3820881233139908 - -julia> mean([√1, √2, √3]) -1.3820881233139908 - -julia> mean(√, [1 2 3; 4 5 6], dims=2) -2×1 Array{Float64,2}: - 1.3820881233139908 - 2.2285192400943226 -``` -""" -mean(f, A::AbstractArray; dims=:) = _mean(f, A, dims) - -_mean(f, A::AbstractArray, ::Colon) = sum(f, A) / length(A) -_mean(f, A::AbstractArray, dims) = sum(f, A, dims=dims) / mapreduce(i -> size(A, i), *, unique(dims); init=1) - -""" - mean!(r, v) - -Compute the mean of `v` over the singleton dimensions of `r`, and write results to `r`. - -# Examples -```jldoctest -julia> v = [1 2; 3 4] -2×2 Array{Int64,2}: - 1 2 - 3 4 - -julia> mean!([1., 1.], v) -2-element Array{Float64,1}: - 1.5 - 3.5 - -julia> mean!([1. 1.], v) -1×2 Array{Float64,2}: - 2.0 3.0 -``` -""" -function mean!(R::AbstractArray, A::AbstractArray) - sum!(R, A; init=true) - x = max(1, length(R)) // length(A) - R .= R .* x - return R -end - -""" - mean(A::AbstractArray; dims) - -Compute the mean of an array over the given dimensions. - -!!! compat "Julia 1.1" - `mean` for empty arrays requires at least Julia 1.1. - -# Examples -```jldoctest -julia> A = [1 2; 3 4] -2×2 Array{Int64,2}: - 1 2 - 3 4 - -julia> mean(A, dims=1) -1×2 Array{Float64,2}: - 2.0 3.0 - -julia> mean(A, dims=2) -2×1 Array{Float64,2}: - 1.5 - 3.5 -``` -""" -mean(A::AbstractArray; dims=:) = _mean(A, dims) - -_mean(A::AbstractArray{T}, region) where {T} = mean!(Base.reducedim_init(t -> t/2, +, A, region), A) -_mean(A::AbstractArray, ::Colon) = sum(A) / length(A) - -function mean(r::AbstractRange{<:Real}) - isempty(r) && return oftype((first(r) + last(r)) / 2, NaN) - (first(r) + last(r)) / 2 -end - -median(r::AbstractRange{<:Real}) = mean(r) - -##### variances ##### - -# faster computation of real(conj(x)*y) -realXcY(x::Real, y::Real) = x*y -realXcY(x::Complex, y::Complex) = real(x)*real(y) + imag(x)*imag(y) - -var(iterable; corrected::Bool=true, mean=nothing) = _var(iterable, corrected, mean) - -function _var(iterable, corrected::Bool, mean) - y = iterate(iterable) - if y === nothing - T = eltype(iterable) - return oftype((abs2(zero(T)) + abs2(zero(T)))/2, NaN) - end - count = 1 - value, state = y - y = iterate(iterable, state) - if mean === nothing - # Use Welford algorithm as seen in (among other places) - # Knuth's TAOCP, Vol 2, page 232, 3rd edition. - M = value / 1 - S = real(zero(M)) - while y !== nothing - value, state = y - y = iterate(iterable, state) - count += 1 - new_M = M + (value - M) / count - S = S + realXcY(value - M, value - new_M) - M = new_M - end - return S / (count - Int(corrected)) - elseif isa(mean, Number) # mean provided - # Cannot use a compensated version, e.g. the one from - # "Updating Formulae and a Pairwise Algorithm for Computing Sample Variances." - # by Chan, Golub, and LeVeque, Technical Report STAN-CS-79-773, - # Department of Computer Science, Stanford University, - # because user can provide mean value that is different to mean(iterable) - sum2 = abs2(value - mean::Number) - while y !== nothing - value, state = y - y = iterate(iterable, state) - count += 1 - sum2 += abs2(value - mean) - end - return sum2 / (count - Int(corrected)) - else - throw(ArgumentError("invalid value of mean, $(mean)::$(typeof(mean))")) - end -end - -centralizedabs2fun(m) = x -> abs2.(x - m) -centralize_sumabs2(A::AbstractArray, m) = - mapreduce(centralizedabs2fun(m), +, A) -centralize_sumabs2(A::AbstractArray, m, ifirst::Int, ilast::Int) = - Base.mapreduce_impl(centralizedabs2fun(m), +, A, ifirst, ilast) - -function centralize_sumabs2!(R::AbstractArray{S}, A::AbstractArray, means::AbstractArray) where S - # following the implementation of _mapreducedim! at base/reducedim.jl - lsiz = Base.check_reducedims(R,A) - isempty(R) || fill!(R, zero(S)) - isempty(A) && return R - - if Base.has_fast_linear_indexing(A) && lsiz > 16 && !has_offset_axes(R, means) - nslices = div(length(A), lsiz) - ibase = first(LinearIndices(A))-1 - for i = 1:nslices - @inbounds R[i] = centralize_sumabs2(A, means[i], ibase+1, ibase+lsiz) - ibase += lsiz - end - return R - end - indsAt, indsRt = Base.safe_tail(axes(A)), Base.safe_tail(axes(R)) # handle d=1 manually - keep, Idefault = Broadcast.shapeindexer(indsRt) - if Base.reducedim1(R, A) - i1 = first(Base.axes1(R)) - @inbounds for IA in CartesianIndices(indsAt) - IR = Broadcast.newindex(IA, keep, Idefault) - r = R[i1,IR] - m = means[i1,IR] - @simd for i in axes(A, 1) - r += abs2(A[i,IA] - m) - end - R[i1,IR] = r - end - else - @inbounds for IA in CartesianIndices(indsAt) - IR = Broadcast.newindex(IA, keep, Idefault) - @simd for i in axes(A, 1) - R[i,IR] += abs2(A[i,IA] - means[i,IR]) - end - end - end - return R -end - -function varm!(R::AbstractArray{S}, A::AbstractArray, m::AbstractArray; corrected::Bool=true) where S - if isempty(A) - fill!(R, convert(S, NaN)) - else - rn = div(length(A), length(R)) - Int(corrected) - centralize_sumabs2!(R, A, m) - R .= R .* (1 // rn) - end - return R -end - -""" - varm(itr, m; dims, corrected::Bool=true) - -Compute the sample variance of collection `itr`, with known mean(s) `m`. - -The algorithm returns an estimator of the generative distribution's variance -under the assumption that each entry of `itr` is an IID drawn from that generative -distribution. For arrays, this computation is equivalent to calculating -`sum((itr .- mean(itr)).^2) / (length(itr) - 1)`. -If `corrected` is `true`, then the sum is scaled with `n-1`, -whereas the sum is scaled with `n` if `corrected` is -`false` with `n` the number of elements in `itr`. - -If `itr` is an `AbstractArray`, `dims` can be provided to compute the variance -over dimensions, and `m` may contain means for each dimension of `itr`. - -!!! note - If array contains `NaN` or [`missing`](@ref) values, the result is also - `NaN` or `missing` (`missing` takes precedence if array contains both). - Use the [`skipmissing`](@ref) function to omit `missing` entries and compute the - variance of non-missing values. -""" -varm(A::AbstractArray, m::AbstractArray; corrected::Bool=true, dims=:) = _varm(A, m, corrected, dims) - -_varm(A::AbstractArray{T}, m, corrected::Bool, region) where {T} = - varm!(Base.reducedim_init(t -> abs2(t)/2, +, A, region), A, m; corrected=corrected) - -varm(A::AbstractArray, m; corrected::Bool=true) = _varm(A, m, corrected, :) - -function _varm(A::AbstractArray{T}, m, corrected::Bool, ::Colon) where T - n = length(A) - n == 0 && return oftype((abs2(zero(T)) + abs2(zero(T)))/2, NaN) - return centralize_sumabs2(A, m) / (n - Int(corrected)) -end - - -""" - var(itr; dims, corrected::Bool=true, mean=nothing) - -Compute the sample variance of collection `itr`. - -The algorithm returns an estimator of the generative distribution's variance -under the assumption that each entry of `itr` is an IID drawn from that generative -distribution. For arrays, this computation is equivalent to calculating -`sum((itr .- mean(itr)).^2) / (length(itr) - 1)). -If `corrected` is `true`, then the sum is scaled with `n-1`, -whereas the sum is scaled with `n` if `corrected` is -`false` with `n` the number of elements in `itr`. - -A pre-computed `mean` may be provided. - -If `itr` is an `AbstractArray`, `dims` can be provided to compute the variance -over dimensions, and `mean` may contain means for each dimension of `itr`. - -!!! note - If array contains `NaN` or [`missing`](@ref) values, the result is also - `NaN` or `missing` (`missing` takes precedence if array contains both). - Use the [`skipmissing`](@ref) function to omit `missing` entries and compute the - variance of non-missing values. -""" -var(A::AbstractArray; corrected::Bool=true, mean=nothing, dims=:) = _var(A, corrected, mean, dims) - -_var(A::AbstractArray, corrected::Bool, mean, dims) = - varm(A, something(mean, Statistics.mean(A, dims=dims)); corrected=corrected, dims=dims) - -_var(A::AbstractArray, corrected::Bool, mean, ::Colon) = - real(varm(A, something(mean, Statistics.mean(A)); corrected=corrected)) - -varm(iterable, m; corrected::Bool=true) = _var(iterable, corrected, m) - -## variances over ranges - -varm(v::AbstractRange, m::AbstractArray) = range_varm(v, m) -varm(v::AbstractRange, m) = range_varm(v, m) - -function range_varm(v::AbstractRange, m) - f = first(v) - m - s = step(v) - l = length(v) - vv = f^2 * l / (l - 1) + f * s * l + s^2 * l * (2 * l - 1) / 6 - if l == 0 || l == 1 - return typeof(vv)(NaN) - end - return vv -end - -function var(v::AbstractRange) - s = step(v) - l = length(v) - vv = abs2(s) * (l + 1) * l / 12 - if l == 0 || l == 1 - return typeof(vv)(NaN) - end - return vv -end - - -##### standard deviation ##### - -function sqrt!(A::AbstractArray) - for i in eachindex(A) - @inbounds A[i] = sqrt(A[i]) - end - A -end - -stdm(A::AbstractArray, m; corrected::Bool=true) = - sqrt.(varm(A, m; corrected=corrected)) - -""" - std(itr; corrected::Bool=true, mean=nothing[, dims]) - -Compute the sample standard deviation of collection `itr`. - -The algorithm returns an estimator of the generative distribution's standard -deviation under the assumption that each entry of `itr` is an IID drawn from that generative -distribution. For arrays, this computation is equivalent to calculating -`sqrt(sum((itr .- mean(itr)).^2) / (length(itr) - 1))`. -If `corrected` is `true`, then the sum is scaled with `n-1`, -whereas the sum is scaled with `n` if `corrected` is -`false` with `n` the number of elements in `itr`. - -A pre-computed `mean` may be provided. - -If `itr` is an `AbstractArray`, `dims` can be provided to compute the standard deviation -over dimensions, and `means` may contain means for each dimension of `itr`. - -!!! note - If array contains `NaN` or [`missing`](@ref) values, the result is also - `NaN` or `missing` (`missing` takes precedence if array contains both). - Use the [`skipmissing`](@ref) function to omit `missing` entries and compute the - standard deviation of non-missing values. -""" -std(A::AbstractArray; corrected::Bool=true, mean=nothing, dims=:) = _std(A, corrected, mean, dims) - -_std(A::AbstractArray, corrected::Bool, mean, dims) = - sqrt.(var(A; corrected=corrected, mean=mean, dims=dims)) - -_std(A::AbstractArray, corrected::Bool, mean, ::Colon) = - sqrt.(var(A; corrected=corrected, mean=mean)) - -_std(A::AbstractArray{<:AbstractFloat}, corrected::Bool, mean, dims) = - sqrt!(var(A; corrected=corrected, mean=mean, dims=dims)) - -_std(A::AbstractArray{<:AbstractFloat}, corrected::Bool, mean, ::Colon) = - sqrt.(var(A; corrected=corrected, mean=mean)) - -std(iterable; corrected::Bool=true, mean=nothing) = - sqrt(var(iterable, corrected=corrected, mean=mean)) - -""" - stdm(itr, m; corrected::Bool=true) - -Compute the sample standard deviation of collection `itr`, with known mean(s) `m`. - -The algorithm returns an estimator of the generative distribution's standard -deviation under the assumption that each entry of `itr` is an IID drawn from that generative -distribution. For arrays, this computation is equivalent to calculating -`sqrt(sum((itr .- mean(itr)).^2) / (length(itr) - 1))`. -If `corrected` is `true`, then the sum is scaled with `n-1`, -whereas the sum is scaled with `n` if `corrected` is -`false` with `n` the number of elements in `itr`. - -A pre-computed `mean` may be provided. - -If `itr` is an `AbstractArray`, `dims` can be provided to compute the standard deviation -over dimensions, and `m` may contain means for each dimension of `itr`. - -!!! note - If array contains `NaN` or [`missing`](@ref) values, the result is also - `NaN` or `missing` (`missing` takes precedence if array contains both). - Use the [`skipmissing`](@ref) function to omit `missing` entries and compute the - standard deviation of non-missing values. -""" -stdm(iterable, m; corrected::Bool=true) = - std(iterable, corrected=corrected, mean=m) - - -###### covariance ###### - -# auxiliary functions - -_conj(x::AbstractArray{<:Real}) = x -_conj(x::AbstractArray) = conj(x) - -_getnobs(x::AbstractVector, vardim::Int) = length(x) -_getnobs(x::AbstractMatrix, vardim::Int) = size(x, vardim) - -function _getnobs(x::AbstractVecOrMat, y::AbstractVecOrMat, vardim::Int) - n = _getnobs(x, vardim) - _getnobs(y, vardim) == n || throw(DimensionMismatch("dimensions of x and y mismatch")) - return n -end - -_vmean(x::AbstractVector, vardim::Int) = mean(x) -_vmean(x::AbstractMatrix, vardim::Int) = mean(x, dims=vardim) - -# core functions - -unscaled_covzm(x::AbstractVector{<:Number}) = sum(abs2, x) -unscaled_covzm(x::AbstractVector) = sum(t -> t*t', x) -unscaled_covzm(x::AbstractMatrix, vardim::Int) = (vardim == 1 ? _conj(x'x) : x * x') - -unscaled_covzm(x::AbstractVector, y::AbstractVector) = sum(conj(y[i])*x[i] for i in eachindex(y, x)) -unscaled_covzm(x::AbstractVector, y::AbstractMatrix, vardim::Int) = - (vardim == 1 ? *(transpose(x), _conj(y)) : *(transpose(x), transpose(_conj(y)))) -unscaled_covzm(x::AbstractMatrix, y::AbstractVector, vardim::Int) = - (c = vardim == 1 ? *(transpose(x), _conj(y)) : x * _conj(y); reshape(c, length(c), 1)) -unscaled_covzm(x::AbstractMatrix, y::AbstractMatrix, vardim::Int) = - (vardim == 1 ? *(transpose(x), _conj(y)) : *(x, adjoint(y))) - -# covzm (with centered data) - -covzm(x::AbstractVector; corrected::Bool=true) = unscaled_covzm(x) / (length(x) - Int(corrected)) -function covzm(x::AbstractMatrix, vardim::Int=1; corrected::Bool=true) - C = unscaled_covzm(x, vardim) - T = promote_type(typeof(first(C) / 1), eltype(C)) - A = convert(AbstractMatrix{T}, C) - b = 1//(size(x, vardim) - corrected) - A .= A .* b - return A -end -covzm(x::AbstractVector, y::AbstractVector; corrected::Bool=true) = - unscaled_covzm(x, y) / (length(x) - Int(corrected)) -function covzm(x::AbstractVecOrMat, y::AbstractVecOrMat, vardim::Int=1; corrected::Bool=true) - C = unscaled_covzm(x, y, vardim) - T = promote_type(typeof(first(C) / 1), eltype(C)) - A = convert(AbstractArray{T}, C) - b = 1//(_getnobs(x, y, vardim) - corrected) - A .= A .* b - return A -end - -# covm (with provided mean) -## Use map(t -> t - xmean, x) instead of x .- xmean to allow for Vector{Vector} -## which can't be handled by broadcast -covm(x::AbstractVector, xmean; corrected::Bool=true) = - covzm(map(t -> t - xmean, x); corrected=corrected) -covm(x::AbstractMatrix, xmean, vardim::Int=1; corrected::Bool=true) = - covzm(x .- xmean, vardim; corrected=corrected) -covm(x::AbstractVector, xmean, y::AbstractVector, ymean; corrected::Bool=true) = - covzm(map(t -> t - xmean, x), map(t -> t - ymean, y); corrected=corrected) -covm(x::AbstractVecOrMat, xmean, y::AbstractVecOrMat, ymean, vardim::Int=1; corrected::Bool=true) = - covzm(x .- xmean, y .- ymean, vardim; corrected=corrected) - -# cov (API) -""" - cov(x::AbstractVector; corrected::Bool=true) - -Compute the variance of the vector `x`. If `corrected` is `true` (the default) then the sum -is scaled with `n-1`, whereas the sum is scaled with `n` if `corrected` is `false` where `n = length(x)`. -""" -cov(x::AbstractVector; corrected::Bool=true) = covm(x, mean(x); corrected=corrected) - -""" - cov(X::AbstractMatrix; dims::Int=1, corrected::Bool=true) - -Compute the covariance matrix of the matrix `X` along the dimension `dims`. If `corrected` -is `true` (the default) then the sum is scaled with `n-1`, whereas the sum is scaled with `n` -if `corrected` is `false` where `n = size(X, dims)`. -""" -cov(X::AbstractMatrix; dims::Int=1, corrected::Bool=true) = - covm(X, _vmean(X, dims), dims; corrected=corrected) - -""" - cov(x::AbstractVector, y::AbstractVector; corrected::Bool=true) - -Compute the covariance between the vectors `x` and `y`. If `corrected` is `true` (the -default), computes ``\\frac{1}{n-1}\\sum_{i=1}^n (x_i-\\bar x) (y_i-\\bar y)^*`` where -``*`` denotes the complex conjugate and `n = length(x) = length(y)`. If `corrected` is -`false`, computes ``\\frac{1}{n}\\sum_{i=1}^n (x_i-\\bar x) (y_i-\\bar y)^*``. -""" -cov(x::AbstractVector, y::AbstractVector; corrected::Bool=true) = - covm(x, mean(x), y, mean(y); corrected=corrected) - -""" - cov(X::AbstractVecOrMat, Y::AbstractVecOrMat; dims::Int=1, corrected::Bool=true) - -Compute the covariance between the vectors or matrices `X` and `Y` along the dimension -`dims`. If `corrected` is `true` (the default) then the sum is scaled with `n-1`, whereas -the sum is scaled with `n` if `corrected` is `false` where `n = size(X, dims) = size(Y, dims)`. -""" -cov(X::AbstractVecOrMat, Y::AbstractVecOrMat; dims::Int=1, corrected::Bool=true) = - covm(X, _vmean(X, dims), Y, _vmean(Y, dims), dims; corrected=corrected) - -##### correlation ##### - -""" - clampcor(x) - -Clamp a real correlation to between -1 and 1, leaving complex correlations unchanged -""" -clampcor(x::Real) = clamp(x, -1, 1) -clampcor(x) = x - -# cov2cor! - -function cov2cor!(C::AbstractMatrix{T}, xsd::AbstractArray) where T - require_one_based_indexing(C, xsd) - nx = length(xsd) - size(C) == (nx, nx) || throw(DimensionMismatch("inconsistent dimensions")) - for j = 1:nx - for i = 1:j-1 - C[i,j] = adjoint(C[j,i]) - end - C[j,j] = oneunit(T) - for i = j+1:nx - C[i,j] = clampcor(C[i,j] / (xsd[i] * xsd[j])) - end - end - return C -end -function cov2cor!(C::AbstractMatrix, xsd, ysd::AbstractArray) - require_one_based_indexing(C, ysd) - nx, ny = size(C) - length(ysd) == ny || throw(DimensionMismatch("inconsistent dimensions")) - for (j, y) in enumerate(ysd) # fixme (iter): here and in all `cov2cor!` we assume that `C` is efficiently indexed by integers - for i in 1:nx - C[i,j] = clampcor(C[i, j] / (xsd * y)) - end - end - return C -end -function cov2cor!(C::AbstractMatrix, xsd::AbstractArray, ysd) - require_one_based_indexing(C, xsd) - nx, ny = size(C) - length(xsd) == nx || throw(DimensionMismatch("inconsistent dimensions")) - for j in 1:ny - for (i, x) in enumerate(xsd) - C[i,j] = clampcor(C[i,j] / (x * ysd)) - end - end - return C -end -function cov2cor!(C::AbstractMatrix, xsd::AbstractArray, ysd::AbstractArray) - require_one_based_indexing(C, xsd, ysd) - nx, ny = size(C) - (length(xsd) == nx && length(ysd) == ny) || - throw(DimensionMismatch("inconsistent dimensions")) - for (i, x) in enumerate(xsd) - for (j, y) in enumerate(ysd) - C[i,j] = clampcor(C[i,j] / (x * y)) - end - end - return C -end - -# corzm (non-exported, with centered data) - -corzm(x::AbstractVector{T}) where {T} = one(real(T)) -function corzm(x::AbstractMatrix, vardim::Int=1) - c = unscaled_covzm(x, vardim) - return cov2cor!(c, collect(sqrt(c[i,i]) for i in 1:min(size(c)...))) -end -corzm(x::AbstractVector, y::AbstractMatrix, vardim::Int=1) = - cov2cor!(unscaled_covzm(x, y, vardim), sqrt(sum(abs2, x)), sqrt!(sum(abs2, y, dims=vardim))) -corzm(x::AbstractMatrix, y::AbstractVector, vardim::Int=1) = - cov2cor!(unscaled_covzm(x, y, vardim), sqrt!(sum(abs2, x, dims=vardim)), sqrt(sum(abs2, y))) -corzm(x::AbstractMatrix, y::AbstractMatrix, vardim::Int=1) = - cov2cor!(unscaled_covzm(x, y, vardim), sqrt!(sum(abs2, x, dims=vardim)), sqrt!(sum(abs2, y, dims=vardim))) - -# corm - -corm(x::AbstractVector{T}, xmean) where {T} = one(real(T)) -corm(x::AbstractMatrix, xmean, vardim::Int=1) = corzm(x .- xmean, vardim) -function corm(x::AbstractVector, mx, y::AbstractVector, my) - require_one_based_indexing(x, y) - n = length(x) - length(y) == n || throw(DimensionMismatch("inconsistent lengths")) - n > 0 || throw(ArgumentError("correlation only defined for non-empty vectors")) - - @inbounds begin - # Initialize the accumulators - xx = zero(sqrt(abs2(one(x[1])))) - yy = zero(sqrt(abs2(one(y[1])))) - xy = zero(x[1] * y[1]') - - @simd for i in eachindex(x, y) - xi = x[i] - mx - yi = y[i] - my - xx += abs2(xi) - yy += abs2(yi) - xy += xi * yi' - end - end - return clampcor(xy / max(xx, yy) / sqrt(min(xx, yy) / max(xx, yy))) -end - -corm(x::AbstractVecOrMat, xmean, y::AbstractVecOrMat, ymean, vardim::Int=1) = - corzm(x .- xmean, y .- ymean, vardim) - -# cor -""" - cor(x::AbstractVector) - -Return the number one. -""" -cor(x::AbstractVector) = one(real(eltype(x))) - -""" - cor(X::AbstractMatrix; dims::Int=1) - -Compute the Pearson correlation matrix of the matrix `X` along the dimension `dims`. -""" -cor(X::AbstractMatrix; dims::Int=1) = corm(X, _vmean(X, dims), dims) - -""" - cor(x::AbstractVector, y::AbstractVector) - -Compute the Pearson correlation between the vectors `x` and `y`. -""" -cor(x::AbstractVector, y::AbstractVector) = corm(x, mean(x), y, mean(y)) - -""" - cor(X::AbstractVecOrMat, Y::AbstractVecOrMat; dims=1) - -Compute the Pearson correlation between the vectors or matrices `X` and `Y` along the dimension `dims`. -""" -cor(x::AbstractVecOrMat, y::AbstractVecOrMat; dims::Int=1) = - corm(x, _vmean(x, dims), y, _vmean(y, dims), dims) - -##### median & quantiles ##### - -""" - middle(x) - -Compute the middle of a scalar value, which is equivalent to `x` itself, but of the type of `middle(x, x)` for consistency. -""" -middle(x::Union{Bool,Int8,Int16,Int32,Int64,Int128,UInt8,UInt16,UInt32,UInt64,UInt128}) = Float64(x) -# Specialized functions for real types allow for improved performance -middle(x::AbstractFloat) = x -middle(x::Real) = (x + zero(x)) / 1 - -""" - middle(x, y) - -Compute the middle of two reals `x` and `y`, which is -equivalent in both value and type to computing their mean (`(x + y) / 2`). -""" -middle(x::Real, y::Real) = x/2 + y/2 - -""" - middle(range) - -Compute the middle of a range, which consists of computing the mean of its extrema. -Since a range is sorted, the mean is performed with the first and last element. - -```jldoctest -julia> middle(1:10) -5.5 -``` -""" -middle(a::AbstractRange) = middle(a[1], a[end]) - -""" - middle(a) - -Compute the middle of an array `a`, which consists of finding its -extrema and then computing their mean. - -```jldoctest -julia> a = [1,2,3.6,10.9] -4-element Array{Float64,1}: - 1.0 - 2.0 - 3.6 - 10.9 - -julia> middle(a) -5.95 -``` -""" -middle(a::AbstractArray) = ((v1, v2) = extrema(a); middle(v1, v2)) - -""" - median!(v) - -Like [`median`](@ref), but may overwrite the input vector. -""" -function median!(v::AbstractVector) - isempty(v) && throw(ArgumentError("median of an empty array is undefined, $(repr(v))")) - eltype(v)>:Missing && any(ismissing, v) && return missing - (eltype(v)<:AbstractFloat || eltype(v)>:AbstractFloat) && any(isnan, v) && return convert(eltype(v), NaN) - inds = axes(v, 1) - n = length(inds) - mid = div(first(inds)+last(inds),2) - if isodd(n) - return middle(partialsort!(v,mid)) - else - m = partialsort!(v, mid:mid+1) - return middle(m[1], m[2]) - end -end -median!(v::AbstractArray) = median!(vec(v)) - -""" - median(itr) - -Compute the median of all elements in a collection. -For an even number of elements no exact median element exists, so the result is -equivalent to calculating mean of two median elements. - -!!! note - If `itr` contains `NaN` or [`missing`](@ref) values, the result is also - `NaN` or `missing` (`missing` takes precedence if `itr` contains both). - Use the [`skipmissing`](@ref) function to omit `missing` entries and compute the - median of non-missing values. - -# Examples -```jldoctest -julia> median([1, 2, 3]) -2.0 - -julia> median([1, 2, 3, 4]) -2.5 - -julia> median([1, 2, missing, 4]) -missing - -julia> median(skipmissing([1, 2, missing, 4])) -2.0 -``` -""" -median(itr) = median!(collect(itr)) - -""" - median(A::AbstractArray; dims) - -Compute the median of an array along the given dimensions. - -# Examples -```jldoctest -julia> median([1 2; 3 4], dims=1) -1×2 Array{Float64,2}: - 2.0 3.0 -``` -""" -median(v::AbstractArray; dims=:) = _median(v, dims) - -_median(v::AbstractArray, dims) = mapslices(median!, v, dims = dims) - -_median(v::AbstractArray{T}, ::Colon) where {T} = median!(copyto!(Array{T,1}(undef, length(v)), v)) - -# for now, use the R/S definition of quantile; may want variants later -# see ?quantile in R -- this is type 7 -""" - quantile!([q::AbstractArray, ] v::AbstractVector, p; sorted=false) - -Compute the quantile(s) of a vector `v` at a specified probability or vector or tuple of -probabilities `p` on the interval [0,1]. If `p` is a vector, an optional -output array `q` may also be specified. (If not provided, a new output array is created.) -The keyword argument `sorted` indicates whether `v` can be assumed to be sorted; if -`false` (the default), then the elements of `v` will be partially sorted in-place. - -Quantiles are computed via linear interpolation between the points `((k-1)/(n-1), v[k])`, -for `k = 1:n` where `n = length(v)`. This corresponds to Definition 7 of Hyndman and Fan -(1996), and is the same as the R default. - -!!! note - An `ArgumentError` is thrown if `v` contains `NaN` or [`missing`](@ref) values. - -* Hyndman, R.J and Fan, Y. (1996) "Sample Quantiles in Statistical Packages", - *The American Statistician*, Vol. 50, No. 4, pp. 361-365 - -# Examples -```jldoctest -julia> x = [3, 2, 1]; - -julia> quantile!(x, 0.5) -2.0 - -julia> x -3-element Array{Int64,1}: - 1 - 2 - 3 - -julia> y = zeros(3); - -julia> quantile!(y, x, [0.1, 0.5, 0.9]) === y -true - -julia> y -3-element Array{Float64,1}: - 1.2 - 2.0 - 2.8 -``` -""" -function quantile!(q::AbstractArray, v::AbstractVector, p::AbstractArray; - sorted::Bool=false) - require_one_based_indexing(q, v, p) - if size(p) != size(q) - throw(DimensionMismatch("size of p, $(size(p)), must equal size of q, $(size(q))")) - end - isempty(q) && return q - - minp, maxp = extrema(p) - _quantilesort!(v, sorted, minp, maxp) - - for (i, j) in zip(eachindex(p), eachindex(q)) - @inbounds q[j] = _quantile(v,p[i]) - end - return q -end - -function quantile!(v::AbstractVector, p::Union{AbstractArray, Tuple{Vararg{Real}}}; - sorted::Bool=false) - if !isempty(p) - minp, maxp = extrema(p) - _quantilesort!(v, sorted, minp, maxp) - end - return map(x->_quantile(v, x), p) -end - -quantile!(v::AbstractVector, p::Real; sorted::Bool=false) = - _quantile(_quantilesort!(v, sorted, p, p), p) - -# Function to perform partial sort of v for quantiles in given range -function _quantilesort!(v::AbstractArray, sorted::Bool, minp::Real, maxp::Real) - isempty(v) && throw(ArgumentError("empty data vector")) - require_one_based_indexing(v) - - if !sorted - lv = length(v) - lo = floor(Int,1+minp*(lv-1)) - hi = ceil(Int,1+maxp*(lv-1)) - - # only need to perform partial sort - sort!(v, 1, lv, Base.Sort.PartialQuickSort(lo:hi), Base.Sort.Forward) - end - ismissing(v[end]) && throw(ArgumentError("quantiles are undefined in presence of missing values")) - isnan(v[end]) && throw(ArgumentError("quantiles are undefined in presence of NaNs")) - return v -end - -# Core quantile lookup function: assumes `v` sorted -@inline function _quantile(v::AbstractVector, p::Real) - 0 <= p <= 1 || throw(ArgumentError("input probability out of [0,1] range")) - require_one_based_indexing(v) - - lv = length(v) - f0 = (lv - 1)*p # 0-based interpolated index - t0 = trunc(f0) - h = f0 - t0 - i = trunc(Int,t0) + 1 - - a = v[i] - b = v[i + (h > 0)] - if isfinite(a) && isfinite(b) - return a + h*(b-a) - else - return (1-h)*a + h*b - end -end - - -""" - quantile(itr, p; sorted=false) - -Compute the quantile(s) of a collection `itr` at a specified probability or vector or tuple of -probabilities `p` on the interval [0,1]. The keyword argument `sorted` indicates whether -`itr` can be assumed to be sorted. - -Quantiles are computed via linear interpolation between the points `((k-1)/(n-1), v[k])`, -for `k = 1:n` where `n = length(itr)`. This corresponds to Definition 7 of Hyndman and Fan -(1996), and is the same as the R default. - -!!! note - An `ArgumentError` is thrown if `itr` contains `NaN` or [`missing`](@ref) values. - Use the [`skipmissing`](@ref) function to omit `missing` entries and compute the - quantiles of non-missing values. - -- Hyndman, R.J and Fan, Y. (1996) "Sample Quantiles in Statistical Packages", - *The American Statistician*, Vol. 50, No. 4, pp. 361-365 - -# Examples -```jldoctest -julia> quantile(0:20, 0.5) -10.0 - -julia> quantile(0:20, [0.1, 0.5, 0.9]) -3-element Array{Float64,1}: - 2.0 - 10.0 - 18.0 - -julia> quantile(skipmissing([1, 10, missing]), 0.5) -5.5 -``` -""" -quantile(itr, p; sorted::Bool=false) = quantile!(collect(itr), p, sorted=sorted) - -quantile(v::AbstractVector, p; sorted::Bool=false) = - quantile!(sorted ? v : Base.copymutable(v), p; sorted=sorted) - - -##### SparseArrays optimizations ##### - -function cov(X::SparseMatrixCSC; dims::Int=1, corrected::Bool=true) - vardim = dims - a, b = size(X) - n, p = vardim == 1 ? (a, b) : (b, a) - - # The covariance can be decomposed into two terms - # 1/(n - 1) ∑ (x_i - x̄)*(x_i - x̄)' = 1/(n - 1) (∑ x_i*x_i' - n*x̄*x̄') - # which can be evaluated via a sparse matrix-matrix product - - # Compute ∑ x_i*x_i' = X'X using sparse matrix-matrix product - out = Matrix(unscaled_covzm(X, vardim)) - - # Compute x̄ - x̄ᵀ = mean(X, dims=vardim) - - # Subtract n*x̄*x̄' from X'X - @inbounds for j in 1:p, i in 1:p - out[i,j] -= x̄ᵀ[i] * x̄ᵀ[j]' * n - end - - # scale with the sample size n or the corrected sample size n - 1 - return rmul!(out, inv(n - corrected)) -end - -# This is the function that does the reduction underlying var/std -function centralize_sumabs2!(R::AbstractArray{S}, A::SparseMatrixCSC{Tv,Ti}, means::AbstractArray) where {S,Tv,Ti} - require_one_based_indexing(R, A, means) - lsiz = Base.check_reducedims(R,A) - size(means) == size(R) || error("size of means must match size of R") - isempty(R) || fill!(R, zero(S)) - isempty(A) && return R - - rowval = rowvals(A) - nzval = nonzeros(A) - m = size(A, 1) - n = size(A, 2) - - if size(R, 1) == size(R, 2) == 1 - # Reduction along both columns and rows - R[1, 1] = centralize_sumabs2(A, means[1]) - elseif size(R, 1) == 1 - # Reduction along rows - @inbounds for col = 1:n - mu = means[col] - r = convert(S, (m - length(nzrange(A, col)))*abs2(mu)) - @simd for j = nzrange(A, col) - r += abs2(nzval[j] - mu) - end - R[1, col] = r - end - elseif size(R, 2) == 1 - # Reduction along columns - rownz = fill(convert(Ti, n), m) - @inbounds for col = 1:n - @simd for j = nzrange(A, col) - row = rowval[j] - R[row, 1] += abs2(nzval[j] - means[row]) - rownz[row] -= 1 - end - end - for i = 1:m - R[i, 1] += rownz[i]*abs2(means[i]) - end - else - # Reduction along a dimension > 2 - @inbounds for col = 1:n - lastrow = 0 - @simd for j = nzrange(A, col) - row = rowval[j] - for i = lastrow+1:row-1 - R[i, col] = abs2(means[i, col]) - end - R[row, col] = abs2(nzval[j] - means[row, col]) - lastrow = row - end - for i = lastrow+1:m - R[i, col] = abs2(means[i, col]) - end - end - end - return R -end - -end # module diff --git a/stdlib/Statistics/test/runtests.jl b/stdlib/Statistics/test/runtests.jl deleted file mode 100644 index 73ff01fa37642..0000000000000 --- a/stdlib/Statistics/test/runtests.jl +++ /dev/null @@ -1,738 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -using Statistics, Test, Random, LinearAlgebra, SparseArrays -using Test: guardseed - -Random.seed!(123) - -@testset "middle" begin - @test middle(3) === 3.0 - @test middle(2, 3) === 2.5 - let x = ((floatmax(1.0)/4)*3) - @test middle(x, x) === x - end - @test middle(1:8) === 4.5 - @test middle([1:8;]) === 4.5 - - # ensure type-correctness - for T in [Bool,Int8,Int16,Int32,Int64,Int128,UInt8,UInt16,UInt32,UInt64,UInt128,Float16,Float32,Float64] - @test middle(one(T)) === middle(one(T), one(T)) - end -end - -@testset "median" begin - @test median([1.]) === 1. - @test median([1.,3]) === 2. - @test median([1.,3,2]) === 2. - - @test median([1,3,2]) === 2.0 - @test median([1,3,2,4]) === 2.5 - - @test median([0.0,Inf]) == Inf - @test median([0.0,-Inf]) == -Inf - @test median([0.,Inf,-Inf]) == 0.0 - @test median([1.,-1.,Inf,-Inf]) == 0.0 - @test isnan(median([-Inf,Inf])) - - X = [2 3 1 -1; 7 4 5 -4] - @test all(median(X, dims=2) .== [1.5, 4.5]) - @test all(median(X, dims=1) .== [4.5 3.5 3.0 -2.5]) - @test X == [2 3 1 -1; 7 4 5 -4] # issue #17153 - - @test_throws ArgumentError median([]) - @test isnan(median([NaN])) - @test isnan(median([0.0,NaN])) - @test isnan(median([NaN,0.0])) - @test isnan(median([NaN,0.0,1.0])) - @test isnan(median(Any[NaN,0.0,1.0])) - @test isequal(median([NaN 0.0; 1.2 4.5], dims=2), reshape([NaN; 2.85], 2, 1)) - - @test ismissing(median([1, missing])) - @test ismissing(median([1, 2, missing])) - @test ismissing(median([NaN, 2.0, missing])) - @test ismissing(median([NaN, missing])) - @test ismissing(median([missing, NaN])) - @test ismissing(median(Any[missing, 2.0, 3.0, 4.0, NaN])) - @test median(skipmissing([1, missing, 2])) === 1.5 - - @test median!([1 2 3 4]) == 2.5 - @test median!([1 2; 3 4]) == 2.5 - - @test invoke(median, Tuple{AbstractVector}, 1:10) == median(1:10) == 5.5 - - @test @inferred(median(Float16[1, 2, NaN])) === Float16(NaN) - @test @inferred(median(Float16[1, 2, 3])) === Float16(2) - @test @inferred(median(Float32[1, 2, NaN])) === NaN32 - @test @inferred(median(Float32[1, 2, 3])) === 2.0f0 -end - -@testset "mean" begin - @test_throws MethodError mean(()) - @test mean((1,2,3)) === 2. - @test mean([0]) === 0. - @test mean([1.]) === 1. - @test mean([1.,3]) == 2. - @test mean([1,2,3]) == 2. - @test mean([0 1 2; 4 5 6], dims=1) == [2. 3. 4.] - @test mean([1 2 3; 4 5 6], dims=1) == [2.5 3.5 4.5] - @test mean(-, [1 2 3 ; 4 5 6], dims=1) == [-2.5 -3.5 -4.5] - @test mean(-, [1 2 3 ; 4 5 6], dims=2) == transpose([-2.0 -5.0]) - @test mean(-, [1 2 3 ; 4 5 6], dims=(1, 2)) == -3.5 .* ones(1, 1) - @test mean(-, [1 2 3 ; 4 5 6], dims=(1, 1)) == [-2.5 -3.5 -4.5] - @test mean(-, [1 2 3 ; 4 5 6], dims=()) == Float64[-1 -2 -3 ; -4 -5 -6] - @test mean(i->i+1, 0:2) === 2. - @test mean(isodd, [3]) === 1. - @test mean(x->3x, (1,1)) === 3. - - # mean of iterables: - n = 10; a = randn(n); b = randn(n) - @test mean(Tuple(a)) ≈ mean(a) - @test mean(Tuple(a + b*im)) ≈ mean(a + b*im) - @test mean(cos, Tuple(a)) ≈ mean(cos, a) - @test mean(x->x/2, a + b*im) ≈ mean(a + b*im) / 2. - @test ismissing(mean(Tuple((1, 2, missing, 4, 5)))) - - @test isnan(mean([NaN])) - @test isnan(mean([0.0,NaN])) - @test isnan(mean([NaN,0.0])) - - @test isnan(mean([0.,Inf,-Inf])) - @test isnan(mean([1.,-1.,Inf,-Inf])) - @test isnan(mean([-Inf,Inf])) - @test isequal(mean([NaN 0.0; 1.2 4.5], dims=2), reshape([NaN; 2.85], 2, 1)) - - @test ismissing(mean([1, missing])) - @test ismissing(mean([NaN, missing])) - @test ismissing(mean([missing, NaN])) - @test isequal(mean([missing 1.0; 2.0 3.0], dims=1), [missing 2.0]) - @test mean(skipmissing([1, missing, 2])) === 1.5 - @test isequal(mean(Complex{Float64}[]), NaN+NaN*im) - @test mean(Complex{Float64}[]) isa Complex{Float64} - @test isequal(mean(skipmissing(Complex{Float64}[])), NaN+NaN*im) - @test mean(skipmissing(Complex{Float64}[])) isa Complex{Float64} - @test isequal(mean(abs, Complex{Float64}[]), NaN) - @test mean(abs, Complex{Float64}[]) isa Float64 - @test isequal(mean(abs, skipmissing(Complex{Float64}[])), NaN) - @test mean(abs, skipmissing(Complex{Float64}[])) isa Float64 - @test isequal(mean(Int[]), NaN) - @test mean(Int[]) isa Float64 - @test isequal(mean(skipmissing(Int[])), NaN) - @test mean(skipmissing(Int[])) isa Float64 - @test_throws MethodError mean([]) - @test_throws MethodError mean(skipmissing([])) - @test_throws ArgumentError mean((1 for i in 2:1)) - - # Check that small types are accumulated using wider type - for T in (Int8, UInt8) - x = [typemax(T) typemax(T)] - g = (v for v in x) - @test mean(x) == mean(g) == typemax(T) - @test mean(identity, x) == mean(identity, g) == typemax(T) - @test mean(x, dims=2) == [typemax(T)]' - end -end - -@testset "mean/median for ranges" begin - for f in (mean, median) - for n = 2:5 - @test f(2:n) == f([2:n;]) - @test f(2:0.1:n) ≈ f([2:0.1:n;]) - end - end - @test mean(2:1) === NaN - @test mean(big(2):1) isa BigFloat -end - -@testset "var & std" begin - # edge case: empty vector - # iterable; this has to throw for type stability - @test_throws MethodError var(()) - @test_throws MethodError var((); corrected=false) - @test_throws MethodError var((); mean=2) - @test_throws MethodError var((); mean=2, corrected=false) - # reduction - @test isnan(var(Int[])) - @test isnan(var(Int[]; corrected=false)) - @test isnan(var(Int[]; mean=2)) - @test isnan(var(Int[]; mean=2, corrected=false)) - # reduction across dimensions - @test isequal(var(Int[], dims=1), [NaN]) - @test isequal(var(Int[], dims=1; corrected=false), [NaN]) - @test isequal(var(Int[], dims=1; mean=[2]), [NaN]) - @test isequal(var(Int[], dims=1; mean=[2], corrected=false), [NaN]) - - # edge case: one-element vector - # iterable - @test isnan(@inferred(var((1,)))) - @test var((1,); corrected=false) === 0.0 - @test var((1,); mean=2) === Inf - @test var((1,); mean=2, corrected=false) === 1.0 - # reduction - @test isnan(@inferred(var([1]))) - @test var([1]; corrected=false) === 0.0 - @test var([1]; mean=2) === Inf - @test var([1]; mean=2, corrected=false) === 1.0 - # reduction across dimensions - @test isequal(@inferred(var([1], dims=1)), [NaN]) - @test var([1], dims=1; corrected=false) ≈ [0.0] - @test var([1], dims=1; mean=[2]) ≈ [Inf] - @test var([1], dims=1; mean=[2], corrected=false) ≈ [1.0] - - @test var(1:8) == 6. - @test varm(1:8,1) == varm(Vector(1:8),1) - @test isnan(varm(1:1,1)) - @test isnan(var(1:1)) - @test isnan(var(1:-1)) - - @test @inferred(var(1.0:8.0)) == 6. - @test varm(1.0:8.0,1.0) == varm(Vector(1.0:8.0),1) - @test isnan(varm(1.0:1.0,1.0)) - @test isnan(var(1.0:1.0)) - @test isnan(var(1.0:-1.0)) - - @test @inferred(var(1.0f0:8.0f0)) === 6.f0 - @test varm(1.0f0:8.0f0,1.0f0) == varm(Vector(1.0f0:8.0f0),1) - @test isnan(varm(1.0f0:1.0f0,1.0f0)) - @test isnan(var(1.0f0:1.0f0)) - @test isnan(var(1.0f0:-1.0f0)) - - @test varm([1,2,3], 2) ≈ 1. - @test var([1,2,3]) ≈ 1. - @test var([1,2,3]; corrected=false) ≈ 2.0/3 - @test var([1,2,3]; mean=0) ≈ 7. - @test var([1,2,3]; mean=0, corrected=false) ≈ 14.0/3 - - @test varm((1,2,3), 2) ≈ 1. - @test var((1,2,3)) ≈ 1. - @test var((1,2,3); corrected=false) ≈ 2.0/3 - @test var((1,2,3); mean=0) ≈ 7. - @test var((1,2,3); mean=0, corrected=false) ≈ 14.0/3 - @test_throws ArgumentError var((1,2,3); mean=()) - - @test var([1 2 3 4 5; 6 7 8 9 10], dims=2) ≈ [2.5 2.5]' - @test var([1 2 3 4 5; 6 7 8 9 10], dims=2; corrected=false) ≈ [2.0 2.0]' - - @test var(collect(1:99), dims=1) ≈ [825] - @test var(Matrix(transpose(collect(1:99))), dims=2) ≈ [825] - - @test stdm([1,2,3], 2) ≈ 1. - @test std([1,2,3]) ≈ 1. - @test std([1,2,3]; corrected=false) ≈ sqrt(2.0/3) - @test std([1,2,3]; mean=0) ≈ sqrt(7.0) - @test std([1,2,3]; mean=0, corrected=false) ≈ sqrt(14.0/3) - - @test stdm([1.0,2,3], 2) ≈ 1. - @test std([1.0,2,3]) ≈ 1. - @test std([1.0,2,3]; corrected=false) ≈ sqrt(2.0/3) - @test std([1.0,2,3]; mean=0) ≈ sqrt(7.0) - @test std([1.0,2,3]; mean=0, corrected=false) ≈ sqrt(14.0/3) - - @test std([1.0,2,3]; dims=1)[] ≈ 1. - @test std([1.0,2,3]; dims=1, corrected=false)[] ≈ sqrt(2.0/3) - @test std([1.0,2,3]; dims=1, mean=[0])[] ≈ sqrt(7.0) - @test std([1.0,2,3]; dims=1, mean=[0], corrected=false)[] ≈ sqrt(14.0/3) - - @test stdm((1,2,3), 2) ≈ 1. - @test std((1,2,3)) ≈ 1. - @test std((1,2,3); corrected=false) ≈ sqrt(2.0/3) - @test std((1,2,3); mean=0) ≈ sqrt(7.0) - @test std((1,2,3); mean=0, corrected=false) ≈ sqrt(14.0/3) - - @test std([1 2 3 4 5; 6 7 8 9 10], dims=2) ≈ sqrt.([2.5 2.5]') - @test std([1 2 3 4 5; 6 7 8 9 10], dims=2; corrected=false) ≈ sqrt.([2.0 2.0]') - - let A = ComplexF64[exp(i*im) for i in 1:10^4] - @test varm(A, 0.) ≈ sum(map(abs2, A)) / (length(A) - 1) - @test varm(A, mean(A)) ≈ var(A) - end - - @test var([1//1, 2//1]) isa Rational{Int} - @test var([1//1, 2//1], dims=1) isa Vector{Rational{Int}} - - @test std([1//1, 2//1]) isa Float64 - @test std([1//1, 2//1], dims=1) isa Vector{Float64} - - @testset "var: empty cases" begin - A = Matrix{Int}(undef, 0,1) - @test var(A) === NaN - - @test isequal(var(A, dims=1), fill(NaN, 1, 1)) - @test isequal(var(A, dims=2), fill(NaN, 0, 1)) - @test isequal(var(A, dims=(1, 2)), fill(NaN, 1, 1)) - @test isequal(var(A, dims=3), fill(NaN, 0, 1)) - end - - # issue #6672 - @test std(AbstractFloat[1,2,3], dims=1) == [1.0] - - for f in (var, std) - @test ismissing(f([1, missing])) - @test ismissing(f([NaN, missing])) - @test ismissing(f([missing, NaN])) - @test isequal(f([missing 1.0; 2.0 3.0], dims=1), [missing f([1.0, 3.0])]) - @test f(skipmissing([1, missing, 2])) === f([1, 2]) - end - for f in (varm, stdm) - @test ismissing(f([1, missing], 0)) - @test ismissing(f([1, 2], missing)) - @test ismissing(f([1, NaN], missing)) - @test ismissing(f([NaN, missing], 0)) - @test ismissing(f([missing, NaN], 0)) - @test ismissing(f([NaN, missing], missing)) - @test ismissing(f([missing, NaN], missing)) - @test f(skipmissing([1, missing, 2]), 0) === f([1, 2], 0) - end - - @test isequal(var(Complex{Float64}[]), NaN) - @test var(Complex{Float64}[]) isa Float64 - @test isequal(var(skipmissing(Complex{Float64}[])), NaN) - @test var(skipmissing(Complex{Float64}[])) isa Float64 - @test_throws MethodError var([]) - @test_throws MethodError var(skipmissing([])) - @test_throws MethodError var((1 for i in 2:1)) - @test isequal(var(Int[]), NaN) - @test var(Int[]) isa Float64 - @test isequal(var(skipmissing(Int[])), NaN) - @test var(skipmissing(Int[])) isa Float64 -end - -function safe_cov(x, y, zm::Bool, cr::Bool) - n = length(x) - if !zm - x = x .- mean(x) - y = y .- mean(y) - end - dot(vec(x), vec(y)) / (n - Int(cr)) -end -X = [1.0 5.0; - 2.0 4.0; - 3.0 6.0; - 4.0 2.0; - 5.0 1.0] -Y = [6.0 2.0; - 1.0 7.0; - 5.0 8.0; - 3.0 4.0; - 2.0 3.0] - -@testset "covariance" begin - for vd in [1, 2], zm in [true, false], cr in [true, false] - # println("vd = $vd: zm = $zm, cr = $cr") - if vd == 1 - k = size(X, 2) - Cxx = zeros(k, k) - Cxy = zeros(k, k) - for i = 1:k, j = 1:k - Cxx[i,j] = safe_cov(X[:,i], X[:,j], zm, cr) - Cxy[i,j] = safe_cov(X[:,i], Y[:,j], zm, cr) - end - x1 = vec(X[:,1]) - y1 = vec(Y[:,1]) - else - k = size(X, 1) - Cxx = zeros(k, k) - Cxy = zeros(k, k) - for i = 1:k, j = 1:k - Cxx[i,j] = safe_cov(X[i,:], X[j,:], zm, cr) - Cxy[i,j] = safe_cov(X[i,:], Y[j,:], zm, cr) - end - x1 = vec(X[1,:]) - y1 = vec(Y[1,:]) - end - - c = zm ? Statistics.covm(x1, 0, corrected=cr) : - cov(x1, corrected=cr) - @test isa(c, Float64) - @test c ≈ Cxx[1,1] - @inferred cov(x1, corrected=cr) - - @test cov(X) == Statistics.covm(X, mean(X, dims=1)) - C = zm ? Statistics.covm(X, 0, vd, corrected=cr) : - cov(X, dims=vd, corrected=cr) - @test size(C) == (k, k) - @test C ≈ Cxx - @inferred cov(X, dims=vd, corrected=cr) - - @test cov(x1, y1) == Statistics.covm(x1, mean(x1), y1, mean(y1)) - c = zm ? Statistics.covm(x1, 0, y1, 0, corrected=cr) : - cov(x1, y1, corrected=cr) - @test isa(c, Float64) - @test c ≈ Cxy[1,1] - @inferred cov(x1, y1, corrected=cr) - - if vd == 1 - @test cov(x1, Y) == Statistics.covm(x1, mean(x1), Y, mean(Y, dims=1)) - end - C = zm ? Statistics.covm(x1, 0, Y, 0, vd, corrected=cr) : - cov(x1, Y, dims=vd, corrected=cr) - @test size(C) == (1, k) - @test vec(C) ≈ Cxy[1,:] - @inferred cov(x1, Y, dims=vd, corrected=cr) - - if vd == 1 - @test cov(X, y1) == Statistics.covm(X, mean(X, dims=1), y1, mean(y1)) - end - C = zm ? Statistics.covm(X, 0, y1, 0, vd, corrected=cr) : - cov(X, y1, dims=vd, corrected=cr) - @test size(C) == (k, 1) - @test vec(C) ≈ Cxy[:,1] - @inferred cov(X, y1, dims=vd, corrected=cr) - - @test cov(X, Y) == Statistics.covm(X, mean(X, dims=1), Y, mean(Y, dims=1)) - C = zm ? Statistics.covm(X, 0, Y, 0, vd, corrected=cr) : - cov(X, Y, dims=vd, corrected=cr) - @test size(C) == (k, k) - @test C ≈ Cxy - @inferred cov(X, Y, dims=vd, corrected=cr) - end - - @testset "floating point accuracy for `cov` of large numbers" begin - A = [4.0, 7.0, 13.0, 16.0] - C = A .+ 1.0e10 - @test cov(A, A) ≈ cov(C, C) - end -end - -function safe_cor(x, y, zm::Bool) - if !zm - x = x .- mean(x) - y = y .- mean(y) - end - x = vec(x) - y = vec(y) - dot(x, y) / (sqrt(dot(x, x)) * sqrt(dot(y, y))) -end -@testset "correlation" begin - for vd in [1, 2], zm in [true, false] - # println("vd = $vd: zm = $zm") - if vd == 1 - k = size(X, 2) - Cxx = zeros(k, k) - Cxy = zeros(k, k) - for i = 1:k, j = 1:k - Cxx[i,j] = safe_cor(X[:,i], X[:,j], zm) - Cxy[i,j] = safe_cor(X[:,i], Y[:,j], zm) - end - x1 = vec(X[:,1]) - y1 = vec(Y[:,1]) - else - k = size(X, 1) - Cxx = zeros(k, k) - Cxy = zeros(k, k) - for i = 1:k, j = 1:k - Cxx[i,j] = safe_cor(X[i,:], X[j,:], zm) - Cxy[i,j] = safe_cor(X[i,:], Y[j,:], zm) - end - x1 = vec(X[1,:]) - y1 = vec(Y[1,:]) - end - - c = zm ? Statistics.corm(x1, 0) : cor(x1) - @test isa(c, Float64) - @test c ≈ Cxx[1,1] - @inferred cor(x1) - - @test cor(X) == Statistics.corm(X, mean(X, dims=1)) - C = zm ? Statistics.corm(X, 0, vd) : cor(X, dims=vd) - @test size(C) == (k, k) - @test C ≈ Cxx - @inferred cor(X, dims=vd) - - @test cor(x1, y1) == Statistics.corm(x1, mean(x1), y1, mean(y1)) - c = zm ? Statistics.corm(x1, 0, y1, 0) : cor(x1, y1) - @test isa(c, Float64) - @test c ≈ Cxy[1,1] - @inferred cor(x1, y1) - - if vd == 1 - @test cor(x1, Y) == Statistics.corm(x1, mean(x1), Y, mean(Y, dims=1)) - end - C = zm ? Statistics.corm(x1, 0, Y, 0, vd) : cor(x1, Y, dims=vd) - @test size(C) == (1, k) - @test vec(C) ≈ Cxy[1,:] - @inferred cor(x1, Y, dims=vd) - - if vd == 1 - @test cor(X, y1) == Statistics.corm(X, mean(X, dims=1), y1, mean(y1)) - end - C = zm ? Statistics.corm(X, 0, y1, 0, vd) : cor(X, y1, dims=vd) - @test size(C) == (k, 1) - @test vec(C) ≈ Cxy[:,1] - @inferred cor(X, y1, dims=vd) - - @test cor(X, Y) == Statistics.corm(X, mean(X, dims=1), Y, mean(Y, dims=1)) - C = zm ? Statistics.corm(X, 0, Y, 0, vd) : cor(X, Y, dims=vd) - @test size(C) == (k, k) - @test C ≈ Cxy - @inferred cor(X, Y, dims=vd) - end - - @test cor(repeat(1:17, 1, 17))[2] <= 1.0 - @test cor(1:17, 1:17) <= 1.0 - @test cor(1:17, 18:34) <= 1.0 - @test cor(Any[1, 2], Any[1, 2]) == 1.0 - @test isnan(cor([0], Int8[81])) - let tmp = range(1, stop=85, length=100) - tmp2 = Vector(tmp) - @test cor(tmp, tmp) <= 1.0 - @test cor(tmp, tmp2) <= 1.0 - end -end - -@testset "quantile" begin - @test quantile([1,2,3,4],0.5) == 2.5 - @test quantile([1,2,3,4],[0.5]) == [2.5] - @test quantile([1., 3],[.25,.5,.75])[2] == median([1., 3]) - @test quantile(100.0:-1.0:0.0, 0.0:0.1:1.0) == 0.0:10.0:100.0 - @test quantile(0.0:100.0, 0.0:0.1:1.0, sorted=true) == 0.0:10.0:100.0 - @test quantile(100f0:-1f0:0.0, 0.0:0.1:1.0) == 0f0:10f0:100f0 - @test quantile([Inf,Inf],0.5) == Inf - @test quantile([-Inf,1],0.5) == -Inf - @test quantile([0,1],1e-18) == 1e-18 - @test quantile([1, 2, 3, 4],[]) == [] - @test quantile([1, 2, 3, 4], (0.5,)) == (2.5,) - @test quantile([4, 9, 1, 5, 7, 8, 2, 3, 5, 17, 11], - (0.1, 0.2, 0.4, 0.9)) == (2.0, 3.0, 5.0, 11.0) - @test quantile(Union{Int, Missing}[4, 9, 1, 5, 7, 8, 2, 3, 5, 17, 11], - [0.1, 0.2, 0.4, 0.9]) == [2.0, 3.0, 5.0, 11.0] - @test quantile(Any[4, 9, 1, 5, 7, 8, 2, 3, 5, 17, 11], - [0.1, 0.2, 0.4, 0.9]) == [2.0, 3.0, 5.0, 11.0] - @test quantile([4, 9, 1, 5, 7, 8, 2, 3, 5, 17, 11], - Any[0.1, 0.2, 0.4, 0.9]) == [2.0, 3.0, 5.0, 11.0] - @test quantile([4, 9, 1, 5, 7, 8, 2, 3, 5, 17, 11], - Any[0.1, 0.2, 0.4, 0.9]) isa Vector{Float64} - @test quantile(Any[4, 9, 1, 5, 7, 8, 2, 3, 5, 17, 11], - Any[0.1, 0.2, 0.4, 0.9]) == [2, 3, 5, 11] - @test quantile(Any[4, 9, 1, 5, 7, 8, 2, 3, 5, 17, 11], - Any[0.1, 0.2, 0.4, 0.9]) isa Vector{Float64} - @test quantile([1, 2, 3, 4], ()) == () - @test isempty(quantile([1, 2, 3, 4], Float64[])) - @test quantile([1, 2, 3, 4], Float64[]) isa Vector{Float64} - @test quantile([1, 2, 3, 4], []) isa Vector{Any} - @test quantile([1, 2, 3, 4], [0, 1]) isa Vector{Int} - - @test quantile(Any[1, 2, 3], 0.5) isa Float64 - @test quantile(Any[1, big(2), 3], 0.5) isa BigFloat - @test quantile(Any[1, 2, 3], Float16(0.5)) isa Float16 - @test quantile(Any[1, Float16(2), 3], Float16(0.5)) isa Float16 - @test quantile(Any[1, big(2), 3], Float16(0.5)) isa BigFloat - - @test_throws ArgumentError quantile([1, missing], 0.5) - @test_throws ArgumentError quantile([1, NaN], 0.5) - @test quantile(skipmissing([1, missing, 2]), 0.5) === 1.5 - - # make sure that type inference works correctly in normal cases - for T in [Int, BigInt, Float64, Float16, BigFloat, Rational{Int}, Rational{BigInt}] - for S in [Float64, Float16, BigFloat, Rational{Int}, Rational{BigInt}] - @inferred quantile(T[1, 2, 3], S(0.5)) - @inferred quantile(T[1, 2, 3], S(0.6)) - @inferred quantile(T[1, 2, 3], S[0.5, 0.6]) - @inferred quantile(T[1, 2, 3], (S(0.5), S(0.6))) - end - end - x = [3; 2; 1] - y = zeros(3) - @test quantile!(y, x, [0.1, 0.5, 0.9]) === y - @test y == [1.2, 2.0, 2.8] -end - -# StatsBase issue 164 -let y = [0.40003674665581906, 0.4085630862624367, 0.41662034698690303, 0.41662034698690303, 0.42189053966652057, 0.42189053966652057, 0.42553514344518345, 0.43985732442991354] - @test issorted(quantile(y, range(0.01, stop=0.99, length=17))) -end - -@testset "variance of complex arrays (#13309)" begin - z = rand(ComplexF64, 10) - @test var(z) ≈ invoke(var, Tuple{Any}, z) ≈ cov(z) ≈ var(z,dims=1)[1] ≈ sum(abs2, z .- mean(z))/9 - @test isa(var(z), Float64) - @test isa(invoke(var, Tuple{Any}, z), Float64) - @test isa(cov(z), Float64) - @test isa(var(z,dims=1), Vector{Float64}) - @test varm(z, 0.0) ≈ invoke(varm, Tuple{Any,Float64}, z, 0.0) ≈ sum(abs2, z)/9 - @test isa(varm(z, 0.0), Float64) - @test isa(invoke(varm, Tuple{Any,Float64}, z, 0.0), Float64) - @test cor(z) === 1.0 - v = varm([1.0+2.0im], 0; corrected = false) - @test v ≈ 5 - @test isa(v, Float64) -end - -@testset "cov and cor of complex arrays (issue #21093)" begin - x = [2.7 - 3.3im, 0.9 + 5.4im, 0.1 + 0.2im, -1.7 - 5.8im, 1.1 + 1.9im] - y = [-1.7 - 1.6im, -0.2 + 6.5im, 0.8 - 10.0im, 9.1 - 3.4im, 2.7 - 5.5im] - @test cov(x, y) ≈ 4.8365 - 12.119im - @test cov(y, x) ≈ 4.8365 + 12.119im - @test cov(x, reshape(y, :, 1)) ≈ reshape([4.8365 - 12.119im], 1, 1) - @test cov(reshape(x, :, 1), y) ≈ reshape([4.8365 - 12.119im], 1, 1) - @test cov(reshape(x, :, 1), reshape(y, :, 1)) ≈ reshape([4.8365 - 12.119im], 1, 1) - @test cov([x y]) ≈ [21.779 4.8365-12.119im; - 4.8365+12.119im 54.548] - @test cor(x, y) ≈ 0.14032104449218274 - 0.35160772008699703im - @test cor(y, x) ≈ 0.14032104449218274 + 0.35160772008699703im - @test cor(x, reshape(y, :, 1)) ≈ reshape([0.14032104449218274 - 0.35160772008699703im], 1, 1) - @test cor(reshape(x, :, 1), y) ≈ reshape([0.14032104449218274 - 0.35160772008699703im], 1, 1) - @test cor(reshape(x, :, 1), reshape(y, :, 1)) ≈ reshape([0.14032104449218274 - 0.35160772008699703im], 1, 1) - @test cor([x y]) ≈ [1.0 0.14032104449218274-0.35160772008699703im - 0.14032104449218274+0.35160772008699703im 1.0] -end - -@testset "Issue #17153 and PR #17154" begin - a = rand(10,10) - b = copy(a) - x = median(a, dims=1) - @test b == a - x = median(a, dims=2) - @test b == a - x = mean(a, dims=1) - @test b == a - x = mean(a, dims=2) - @test b == a - x = var(a, dims=1) - @test b == a - x = var(a, dims=2) - @test b == a - x = std(a, dims=1) - @test b == a - x = std(a, dims=2) - @test b == a -end - -# dimensional correctness -const BASE_TEST_PATH = joinpath(Sys.BINDIR, "..", "share", "julia", "test") -isdefined(Main, :Furlongs) || @eval Main include(joinpath($(BASE_TEST_PATH), "testhelpers", "Furlongs.jl")) -using .Main.Furlongs - -Statistics.middle(x::Furlong{p}) where {p} = Furlong{p}(middle(x.val)) -Statistics.middle(x::Furlong{p}, y::Furlong{p}) where {p} = Furlong{p}(middle(x.val, y.val)) - -@testset "Unitful elements" begin - r = Furlong(1):Furlong(1):Furlong(2) - a = Vector(r) - @test sum(r) == sum(a) == Furlong(3) - @test cumsum(r) == Furlong.([1,3]) - @test mean(r) == mean(a) == median(a) == median(r) == Furlong(1.5) - @test var(r) == var(a) == Furlong{2}(0.5) - @test std(r) == std(a) == Furlong{1}(sqrt(0.5)) - - # Issue #21786 - A = [Furlong{1}(rand(-5:5)) for i in 1:2, j in 1:2] - @test mean(mean(A, dims=1), dims=2)[1] === mean(A) - @test var(A, dims=1)[1] === var(A[:, 1]) - @test std(A, dims=1)[1] === std(A[:, 1]) -end - -# Issue #22901 -@testset "var and quantile of Any arrays" begin - x = Any[1, 2, 4, 10] - y = Any[1, 2, 4, 10//1] - @test var(x) === 16.25 - @test var(y) === 65//4 - @test std(x) === sqrt(16.25) - @test quantile(x, 0.5) === 3.0 - @test quantile(x, 1//2) === 3//1 -end - -@testset "Promotion in covzm. Issue #8080" begin - A = [1 -1 -1; -1 1 1; -1 1 -1; 1 -1 -1; 1 -1 1] - @test Statistics.covzm(A) - mean(A, dims=1)'*mean(A, dims=1)*size(A, 1)/(size(A, 1) - 1) ≈ cov(A) - A = [1//1 -1 -1; -1 1 1; -1 1 -1; 1 -1 -1; 1 -1 1] - @test (A'A - size(A, 1)*mean(A, dims=1)'*mean(A, dims=1))/4 == cov(A) -end - -@testset "Mean along dimension of empty array" begin - a0 = zeros(0) - a00 = zeros(0, 0) - a01 = zeros(0, 1) - a10 = zeros(1, 0) - @test isequal(mean(a0, dims=1) , fill(NaN, 1)) - @test isequal(mean(a00, dims=(1, 2)), fill(NaN, 1, 1)) - @test isequal(mean(a01, dims=1) , fill(NaN, 1, 1)) - @test isequal(mean(a10, dims=2) , fill(NaN, 1, 1)) -end - -@testset "cov/var/std of Vector{Vector}" begin - x = [[2,4,6],[4,6,8]] - @test var(x) ≈ vec(var([x[1] x[2]], dims=2)) - @test std(x) ≈ vec(std([x[1] x[2]], dims=2)) - @test cov(x) ≈ cov([x[1] x[2]], dims=2) -end - -@testset "var of sparse array" begin - se33 = SparseMatrixCSC{Float64}(I, 3, 3) - sA = sprandn(3, 7, 0.5) - pA = sparse(rand(3, 7)) - - for arr in (se33, sA, pA) - farr = Array(arr) - @test var(arr) ≈ var(farr) - @test var(arr, dims=1) ≈ var(farr, dims=1) - @test var(arr, dims=2) ≈ var(farr, dims=2) - @test var(arr, dims=(1, 2)) ≈ [var(farr)] - @test isequal(var(arr, dims=3), var(farr, dims=3)) - end - - @testset "empty cases" begin - @test var(sparse(Int[])) === NaN - @test isequal(var(spzeros(0, 1), dims=1), var(Matrix{Int}(I, 0, 1), dims=1)) - @test isequal(var(spzeros(0, 1), dims=2), var(Matrix{Int}(I, 0, 1), dims=2)) - @test isequal(var(spzeros(0, 1), dims=(1, 2)), var(Matrix{Int}(I, 0, 1), dims=(1, 2))) - @test isequal(var(spzeros(0, 1), dims=3), var(Matrix{Int}(I, 0, 1), dims=3)) - end -end - -# Faster covariance function for sparse matrices -# Prevents densifying the input matrix when subtracting the mean -# Test against dense implementation -# PR https://github.com/JuliaLang/julia/pull/22735 -# Part of this test needed to be hacked due to the treatment -# of Inf in sparse matrix algebra -# https://github.com/JuliaLang/julia/issues/22921 -# The issue will be resolved in -# https://github.com/JuliaLang/julia/issues/22733 -@testset "optimizing sparse $elty covariance" for elty in (Float64, Complex{Float64}) - n = 10 - p = 5 - np2 = div(n*p, 2) - nzvals, x_sparse = guardseed(1) do - if elty <: Real - nzvals = randn(np2) - else - nzvals = complex.(randn(np2), randn(np2)) - end - nzvals, sparse(rand(1:n, np2), rand(1:p, np2), nzvals, n, p) - end - x_dense = convert(Matrix{elty}, x_sparse) - @testset "Test with no Infs and NaNs, vardim=$vardim, corrected=$corrected" for vardim in (1, 2), - corrected in (true, false) - @test cov(x_sparse, dims=vardim, corrected=corrected) ≈ - cov(x_dense , dims=vardim, corrected=corrected) - end - - @testset "Test with $x11, vardim=$vardim, corrected=$corrected" for x11 in (NaN, Inf), - vardim in (1, 2), - corrected in (true, false) - x_sparse[1,1] = x11 - x_dense[1 ,1] = x11 - - cov_sparse = cov(x_sparse, dims=vardim, corrected=corrected) - cov_dense = cov(x_dense , dims=vardim, corrected=corrected) - @test cov_sparse[2:end, 2:end] ≈ cov_dense[2:end, 2:end] - @test isfinite.(cov_sparse) == isfinite.(cov_dense) - @test isfinite.(cov_sparse) == isfinite.(cov_dense) - end - - @testset "Test with NaN and Inf, vardim=$vardim, corrected=$corrected" for vardim in (1, 2), - corrected in (true, false) - x_sparse[1,1] = Inf - x_dense[1 ,1] = Inf - x_sparse[2,1] = NaN - x_dense[2 ,1] = NaN - - cov_sparse = cov(x_sparse, dims=vardim, corrected=corrected) - cov_dense = cov(x_dense , dims=vardim, corrected=corrected) - @test cov_sparse[(1 + vardim):end, (1 + vardim):end] ≈ - cov_dense[ (1 + vardim):end, (1 + vardim):end] - @test isfinite.(cov_sparse) == isfinite.(cov_dense) - @test isfinite.(cov_sparse) == isfinite.(cov_dense) - end -end diff --git a/stdlib/SuiteSparse/src/cholmod.jl b/stdlib/SuiteSparse/src/cholmod.jl index 86381eeb3d179..7b66ffcdfc32e 100644 --- a/stdlib/SuiteSparse/src/cholmod.jl +++ b/stdlib/SuiteSparse/src/cholmod.jl @@ -375,12 +375,12 @@ mutable struct FactorComponent{Tv,S} <: AbstractMatrix{Tv} function FactorComponent{Tv,S}(F::Factor{Tv}) where {Tv,S} s = unsafe_load(pointer(F)) if s.is_ll != 0 - if !(S == :L || S == :U || S == :PtL || S == :UP) + if !(S === :L || S === :U || S === :PtL || S === :UP) throw(CHOLMODException(string(S, " not supported for sparse ", "LLt matrices; try :L, :U, :PtL, or :UP"))) end - elseif !(S == :L || S == :U || S == :PtL || S == :UP || - S == :D || S == :LD || S == :DU || S == :PtLD || S == :DUP) + elseif !(S === :L || S === :U || S === :PtL || S === :UP || + S === :D || S === :LD || S === :DU || S === :PtLD || S === :DUP) throw(CHOLMODException(string(S, " not supported for sparse LDLt ", "matrices; try :L, :U, :PtL, :UP, :D, :LD, :DU, :PtLD, or :DUP"))) end @@ -798,13 +798,25 @@ get_perm(FC::FactorComponent) = get_perm(Factor(FC)) # Conversion/construction function Dense{T}(A::StridedVecOrMat) where T<:VTypes d = allocate_dense(size(A, 1), size(A, 2), stride(A, 2), T) - s = unsafe_load(pointer(d)) - for i in eachindex(A) - unsafe_store!(s.x, A[i], i) + GC.@preserve d begin + s = unsafe_load(pointer(d)) + for (i, c) in enumerate(eachindex(A)) + unsafe_store!(s.x, A[c], i) + end end d end -function Dense(A::StridedVecOrMat) +function Dense{T}(A::Union{Adjoint{<:Any, <:StridedVecOrMat}, Transpose{<:Any, <:StridedVecOrMat}}) where T<:VTypes + d = allocate_dense(size(A, 1), size(A, 2), size(A, 1), T) + GC.@preserve d begin + s = unsafe_load(pointer(d)) + for (i, c) in enumerate(eachindex(A)) + unsafe_store!(s.x, A[c], i) + end + end + d +end +function Dense(A::Union{StridedVecOrMat, Adjoint{<:Any, <:StridedVecOrMat}, Transpose{<:Any, <:StridedVecOrMat}}) T = promote_type(eltype(A), Float64) return Dense{T}(A) end @@ -1182,9 +1194,9 @@ function getindex(A::Sparse{T}, i0::Integer, i1::Integer) where T end @inline function getproperty(F::Factor, sym::Symbol) - if sym == :p + if sym === :p return get_perm(F) - elseif sym == :ptr + elseif sym === :ptr return getfield(F, :ptr) else return FactorComponent(F, sym) @@ -1224,11 +1236,12 @@ function *(A::Sparse{Tv}, adjB::Adjoint{Tv,Sparse{Tv}}) where Tv<:VRealTypes ## A->stype == 0 (storage of upper and lower parts). If necessary ## the matrix A is first converted to stype == 0 s = unsafe_load(pointer(A)) + fset = s.ncol == 0 ? SuiteSparse_long[] : SuiteSparse_long[0:s.ncol-1;] if s.stype != 0 aa1 = copy(A, 0, 1) - return aat(aa1, SuiteSparse_long[0:s.ncol-1;], 1) + return aat(aa1, fset, 1) else - return aat(A, SuiteSparse_long[0:s.ncol-1;], 1) + return aat(A, fset, 1) end end @@ -1680,8 +1693,14 @@ end # Likewise the two following explicit Vector and Matrix defs (rather than a single VecOrMat) (\)(L::Factor{T}, B::Vector{Complex{T}}) where {T<:Float64} = complex.(L\real(B), L\imag(B)) (\)(L::Factor{T}, B::Matrix{Complex{T}}) where {T<:Float64} = complex.(L\real(B), L\imag(B)) +(\)(L::Factor{T}, B::Adjoint{<:Any, <:Matrix{Complex{T}}}) where {T<:Float64} = complex.(L\real(B), L\imag(B)) +(\)(L::Factor{T}, B::Transpose{<:Any, <:Matrix{Complex{T}}}) where {T<:Float64} = complex.(L\real(B), L\imag(B)) + (\)(L::Factor{T}, b::StridedVector) where {T<:VTypes} = Vector(L\Dense{T}(b)) (\)(L::Factor{T}, B::StridedMatrix) where {T<:VTypes} = Matrix(L\Dense{T}(B)) +(\)(L::Factor{T}, B::Adjoint{<:Any, <:StridedMatrix}) where {T<:VTypes} = Matrix(L\Dense{T}(B)) +(\)(L::Factor{T}, B::Transpose{<:Any, <:StridedMatrix}) where {T<:VTypes} = Matrix(L\Dense{T}(B)) + (\)(L::Factor, B::Sparse) = spsolve(CHOLMOD_A, L, B) # When right hand side is sparse, we have to ensure that the rhs is not marked as symmetric. (\)(L::Factor, B::SparseVecOrMat) = sparse(spsolve(CHOLMOD_A, L, Sparse(B, 0))) @@ -1703,7 +1722,8 @@ const RealHermSymComplexHermF64SSL = Union{ Symmetric{Float64,SparseMatrixCSC{Float64,SuiteSparse_long}}, Hermitian{Float64,SparseMatrixCSC{Float64,SuiteSparse_long}}, Hermitian{Complex{Float64},SparseMatrixCSC{Complex{Float64},SuiteSparse_long}}} -function \(A::RealHermSymComplexHermF64SSL, B::StridedVecOrMat) +const StridedVecOrMatInclAdjAndTrans = Union{StridedVecOrMat, Adjoint{<:Any, <:StridedVecOrMat}, Transpose{<:Any, <:StridedVecOrMat}} +function \(A::RealHermSymComplexHermF64SSL, B::StridedVecOrMatInclAdjAndTrans) F = cholesky(A; check = false) if issuccess(F) return \(F, B) @@ -1716,7 +1736,7 @@ function \(A::RealHermSymComplexHermF64SSL, B::StridedVecOrMat) end end end -function \(adjA::Adjoint{<:Any,<:RealHermSymComplexHermF64SSL}, B::StridedVecOrMat) +function \(adjA::Adjoint{<:Any,<:RealHermSymComplexHermF64SSL}, B::StridedVecOrMatInclAdjAndTrans) A = adjA.parent F = cholesky(A; check = false) if issuccess(F) diff --git a/stdlib/SuiteSparse/src/spqr.jl b/stdlib/SuiteSparse/src/spqr.jl index 88a4522520fd8..d5bc42562a8c7 100644 --- a/stdlib/SuiteSparse/src/spqr.jl +++ b/stdlib/SuiteSparse/src/spqr.jl @@ -143,29 +143,6 @@ Matrix{T}(Q::QRSparseQ) where {T} = lmul!(Q, Matrix{T}(I, size(Q, 1), min(size(Q _default_tol(A::SparseMatrixCSC) = 20*sum(size(A))*eps(real(eltype(A)))*maximum(norm(view(A, :, i)) for i in 1:size(A, 2)) -function LinearAlgebra.qr(A::SparseMatrixCSC{Tv}; tol = _default_tol(A)) where {Tv <: CHOLMOD.VTypes} - R = Ref{Ptr{CHOLMOD.C_Sparse{Tv}}}() - E = Ref{Ptr{CHOLMOD.SuiteSparse_long}}() - H = Ref{Ptr{CHOLMOD.C_Sparse{Tv}}}() - HPinv = Ref{Ptr{CHOLMOD.SuiteSparse_long}}() - HTau = Ref{Ptr{CHOLMOD.C_Dense{Tv}}}(C_NULL) - - # SPQR doesn't accept symmetric matrices so we explicitly set the stype - r, p, hpinv = _qr!(ORDERING_DEFAULT, tol, 0, 0, Sparse(A, 0), - C_NULL, C_NULL, C_NULL, C_NULL, - R, E, H, HPinv, HTau) - - R_ = SparseMatrixCSC(Sparse(R[])) - return QRSparse(SparseMatrixCSC(Sparse(H[])), - vec(Array(CHOLMOD.Dense(HTau[]))), - SparseMatrixCSC(min(size(A)...), - size(R_, 2), - getcolptr(R_), - rowvals(R_), - nonzeros(R_)), - p, hpinv) -end - """ qr(A) -> QRSparse @@ -173,6 +150,12 @@ Compute the `QR` factorization of a sparse matrix `A`. Fill-reducing row and col are used such that `F.R = F.Q'*A[F.prow,F.pcol]`. The main application of this type is to solve least squares or underdetermined problems with [`\\`](@ref). The function calls the C library SPQR. +!!! note + `qr(A::SparseMatrixCSC)` uses the SPQR library that is part of SuiteSparse. + As this library only supports sparse matrices with [`Float64`](@ref) or + `ComplexF64` elements, as of Julia v1.4 `qr` converts `A` into a copy that is + of type `SparseMatrixCSC{Float64}` or `SparseMatrixCSC{ComplexF64}` as appropriate. + # Examples ```jldoctest julia> A = sparse([1,2,3,4], [1,1,2,2], [1.0,1.0,1.0,1.0]) @@ -206,7 +189,39 @@ Column permutation: 2 ``` """ -LinearAlgebra.qr(A::SparseMatrixCSC; tol = _default_tol(A)) = qr(A, Val{true}, tol = tol) +function LinearAlgebra.qr(A::SparseMatrixCSC{Tv}; tol=_default_tol(A)) where {Tv <: CHOLMOD.VTypes} + R = Ref{Ptr{CHOLMOD.C_Sparse{Tv}}}() + E = Ref{Ptr{CHOLMOD.SuiteSparse_long}}() + H = Ref{Ptr{CHOLMOD.C_Sparse{Tv}}}() + HPinv = Ref{Ptr{CHOLMOD.SuiteSparse_long}}() + HTau = Ref{Ptr{CHOLMOD.C_Dense{Tv}}}(C_NULL) + + # SPQR doesn't accept symmetric matrices so we explicitly set the stype + r, p, hpinv = _qr!(ORDERING_DEFAULT, tol, 0, 0, Sparse(A, 0), + C_NULL, C_NULL, C_NULL, C_NULL, + R, E, H, HPinv, HTau) + + R_ = SparseMatrixCSC(Sparse(R[])) + return QRSparse(SparseMatrixCSC(Sparse(H[])), + vec(Array(CHOLMOD.Dense(HTau[]))), + SparseMatrixCSC(min(size(A)...), + size(R_, 2), + getcolptr(R_), + rowvals(R_), + nonzeros(R_)), + p, hpinv) +end +LinearAlgebra.qr(A::SparseMatrixCSC{<:Union{Float16,Float32}}; tol=_default_tol(A)) = + qr(convert(SparseMatrixCSC{Float64}, A); tol=tol) +LinearAlgebra.qr(A::SparseMatrixCSC{<:Union{ComplexF16,ComplexF32}}; tol=_default_tol(A)) = + qr(convert(SparseMatrixCSC{ComplexF64}, A); tol=tol) +LinearAlgebra.qr(A::Union{SparseMatrixCSC{T},SparseMatrixCSC{Complex{T}}}; + tol=_default_tol(A)) where {T<:AbstractFloat} = + throw(ArgumentError(string("matrix type ", typeof(A), "not supported. ", + "Try qr(convert(SparseMatrixCSC{Float64/ComplexF64,Int}, A)) for ", + "sparse floating point QR using SPQR or qr(Array(A)) for generic ", + "dense QR."))) +LinearAlgebra.qr(A::SparseMatrixCSC; tol=_default_tol(A)) = qr(float(A); tol=tol) function LinearAlgebra.lmul!(Q::QRSparseQ, A::StridedVecOrMat) if size(A, 1) != size(Q, 1) @@ -312,11 +327,11 @@ julia> F.pcol ``` """ @inline function Base.getproperty(F::QRSparse, d::Symbol) - if d == :Q + if d === :Q return QRSparseQ(F.factors, F.τ, size(F, 2)) - elseif d == :prow + elseif d === :prow return invperm(F.rpivinv) - elseif d == :pcol + elseif d === :pcol return F.cpiv else getfield(F, d) diff --git a/stdlib/SuiteSparse/src/umfpack.jl b/stdlib/SuiteSparse/src/umfpack.jl index 155cbe2ab53ef..13dafc03a6f0f 100644 --- a/stdlib/SuiteSparse/src/umfpack.jl +++ b/stdlib/SuiteSparse/src/umfpack.jl @@ -190,9 +190,18 @@ function size(F::UmfpackLU, dim::Integer) end end -function show(io::IO, F::UmfpackLU) - print(io, "UMFPACK LU Factorization of a $(size(F)) sparse matrix") - F.numeric != C_NULL && print(io, '\n', F.numeric) +function show(io::IO, mime::MIME{Symbol("text/plain")}, F::UmfpackLU) + if F.numeric != C_NULL + if issuccess(F) + summary(io, F); println(io) + println(io, "L factor:") + show(io, mime, F.L) + println(io, "\nU factor:") + show(io, mime, F.U) + else + print(io, "Failed factorization of type $(typeof(F))") + end + end end function deserialize(s::AbstractSerializer, t::Type{UmfpackLU{Tv,Ti}}) where {Tv,Ti} @@ -214,7 +223,7 @@ end ## Wrappers for UMFPACK functions # generate the name of the C function according to the value and integer types -umf_nm(nm,Tv,Ti) = "umfpack_" * (Tv == :Float64 ? "d" : "z") * (Ti == :Int64 ? "l_" : "i_") * nm +umf_nm(nm,Tv,Ti) = "umfpack_" * (Tv === :Float64 ? "d" : "z") * (Ti === :Int64 ? "l_" : "i_") * nm for itype in UmfpackIndexTypes sym_r = umf_nm("symbolic", :Float64, itype) @@ -487,20 +496,20 @@ end @inline function getproperty(lu::UmfpackLU, d::Symbol) - if d == :L || d == :U || d == :p || d == :q || d == :Rs || d == :(:) + if d === :L || d === :U || d === :p || d === :q || d === :Rs || d === :(:) # Guard the call to umf_extract behaind a branch to avoid infinite recursion L, U, p, q, Rs = umf_extract(lu) - if d == :L + if d === :L return L - elseif d == :U + elseif d === :U return U - elseif d == :p + elseif d === :p return p - elseif d == :q + elseif d === :q return q - elseif d == :Rs + elseif d === :Rs return Rs - elseif d == :(:) + elseif d === :(:) return (L, U, p, q, Rs) end else diff --git a/stdlib/SuiteSparse/test/cholmod.jl b/stdlib/SuiteSparse/test/cholmod.jl index 4f6927b14f05a..5c3be022daa1a 100644 --- a/stdlib/SuiteSparse/test/cholmod.jl +++ b/stdlib/SuiteSparse/test/cholmod.jl @@ -855,3 +855,23 @@ end @test !issuccess(ldlt!(F, M; check = false)) end end + +@testset "Issue #27860" begin + for typeA in (Float64, ComplexF64), typeB in (Float64, ComplexF64), transform in (adjoint, transpose) + A = sparse(typeA[2.0 0.1; 0.1 2.0]) + B = randn(typeB, 2, 2) + @test A \ transform(B) ≈ cholesky(A) \ transform(B) ≈ Matrix(A) \ transform(B) + end +end + +@testset "Issue #33365" begin + A = Sparse(spzeros(0, 0)) + @test A * A' == A + @test A' * A == A + B = Sparse(spzeros(0, 4)) + @test B * B' == Sparse(spzeros(0, 0)) + @test B' * B == Sparse(spzeros(4, 4)) + C = Sparse(spzeros(3, 0)) + @test C * C' == Sparse(spzeros(3, 3)) + @test C' * C == Sparse(spzeros(0, 0)) +end diff --git a/stdlib/SuiteSparse/test/umfpack.jl b/stdlib/SuiteSparse/test/umfpack.jl index 356c0cf3fa4e7..e7ad0644b5d8c 100644 --- a/stdlib/SuiteSparse/test/umfpack.jl +++ b/stdlib/SuiteSparse/test/umfpack.jl @@ -190,3 +190,19 @@ using LinearAlgebra: Adjoint, Transpose, SingularException end end + +@testset "REPL printing of UmfpackLU" begin + # regular matrix + A = sparse([1, 2], [1, 2], Float64[1.0, 1.0]) + F = lu(A) + facstring = sprint((t, s) -> show(t, "text/plain", s), F) + lstring = sprint((t, s) -> show(t, "text/plain", s), F.L) + ustring = sprint((t, s) -> show(t, "text/plain", s), F.U) + @test facstring == "$(summary(F))\nL factor:\n$lstring\nU factor:\n$ustring" + + # singular matrix + B = sparse(zeros(Float64, 2, 2)) + F = lu(B; check=false) + facstring = sprint((t, s) -> show(t, "text/plain", s), F) + @test facstring == "Failed factorization of type $(summary(F))" +end diff --git a/stdlib/Test/src/Test.jl b/stdlib/Test/src/Test.jl index 98c0e8c983e8d..8c515b3330532 100644 --- a/stdlib/Test/src/Test.jl +++ b/stdlib/Test/src/Test.jl @@ -64,6 +64,9 @@ function scrub_exc_stack(stack) return Any[ (x[1], scrub_backtrace(x[2])) for x in stack ] end +# define most of the test infrastructure without type specialization +@nospecialize + """ Result @@ -89,10 +92,10 @@ function Base.show(io::IO, t::Pass) if !(t.orig_expr === nothing) print(io, "\n Expression: ", t.orig_expr) end - if t.test_type == :test_throws + if t.test_type === :test_throws # The correct type of exception was thrown print(io, "\n Thrown: ", typeof(t.value)) - elseif t.test_type == :test && t.data !== nothing + elseif t.test_type === :test && t.data !== nothing # The test was an expression, so display the term-by-term # evaluated version as well print(io, "\n Evaluated: ", t.data) @@ -117,15 +120,15 @@ function Base.show(io::IO, t::Fail) print(io, " at ") printstyled(io, something(t.source.file, :none), ":", t.source.line, "\n"; bold=true, color=:default) print(io, " Expression: ", t.orig_expr) - if t.test_type == :test_throws_wrong + if t.test_type === :test_throws_wrong # An exception was thrown, but it was of the wrong type print(io, "\n Expected: ", t.data) print(io, "\n Thrown: ", isa(t.data, Type) ? typeof(t.value) : t.value) - elseif t.test_type == :test_throws_nothing + elseif t.test_type === :test_throws_nothing # An exception was expected, but no exception was thrown print(io, "\n Expected: ", t.data) print(io, "\n No exception thrown") - elseif t.test_type == :test && t.data !== nothing + elseif t.test_type === :test && t.data !== nothing # The test was an expression, so display the term-by-term # evaluated version as well print(io, "\n Evaluated: ", t.data) @@ -164,34 +167,32 @@ mutable struct Error <: Result end end function Base.show(io::IO, t::Error) - if t.test_type == :test_interrupted + if t.test_type === :test_interrupted printstyled(io, "Interrupted", color=Base.error_color()) return end printstyled(io, "Error During Test"; bold=true, color=Base.error_color()) print(io, " at ") printstyled(io, something(t.source.file, :none), ":", t.source.line, "\n"; bold=true, color=:default) - if t.test_type == :test_nonbool + if t.test_type === :test_nonbool println(io, " Expression evaluated to non-Boolean") println(io, " Expression: ", t.orig_expr) print( io, " Value: ", t.value) - elseif t.test_type == :test_error + elseif t.test_type === :test_error println(io, " Test threw exception") println(io, " Expression: ", t.orig_expr) # Capture error message and indent to match - print(io, join(map(line->string(" ",line), - split(t.backtrace, "\n")), "\n")) - elseif t.test_type == :test_unbroken + join(io, (" " * line for line in split(t.backtrace, "\n")), "\n") + elseif t.test_type === :test_unbroken # A test that was expected to fail did not println(io, " Unexpected Pass") println(io, " Expression: ", t.orig_expr) println(io, " Got correct result, please change to @test if no longer broken.") - elseif t.test_type == :nontest_error + elseif t.test_type === :nontest_error # we had an error outside of a @test println(io, " Got exception outside of a @test") # Capture error message and indent to match - print(io, join(map(line->string(" ",line), - split(t.backtrace, "\n")), "\n")) + join(io, (" " * line for line in split(t.backtrace, "\n")), "\n") end end @@ -207,7 +208,7 @@ mutable struct Broken <: Result end function Base.show(io::IO, t::Broken) printstyled(io, "Test Broken\n"; bold=true, color=Base.warn_color()) - if t.test_type == :skipped && !(t.orig_expr === nothing) + if t.test_type === :skipped && !(t.orig_expr === nothing) print(io, " Skipped: ", t.orig_expr) elseif !(t.orig_expr === nothing) print(io, " Expression: ", t.orig_expr) @@ -237,7 +238,7 @@ function eval_test(evaluated::Expr, quoted::Expr, source::LineNumberNode, negate quoted_args = quoted.args n = length(evaled_args) kw_suffix = "" - if evaluated.head == :comparison + if evaluated.head === :comparison args = evaled_args while i < n a, op, b = args[i], args[i+1], args[i+2] @@ -249,7 +250,7 @@ function eval_test(evaluated::Expr, quoted::Expr, source::LineNumberNode, negate i += 2 end - elseif evaluated.head == :call + elseif evaluated.head === :call op = evaled_args[1] kwargs = evaled_args[2].args # Keyword arguments from `Expr(:parameters, ...)` args = evaled_args[3:n] @@ -294,9 +295,9 @@ so that e.g. `@test a ≈ b atol=ε` means `@test ≈(a, b, atol=ε)`. test_expr!(m, ex) = ex function test_expr!(m, ex, kws...) - ex isa Expr && ex.head == :call || @goto fail + ex isa Expr && ex.head === :call || @goto fail for kw in kws - kw isa Expr && kw.head == :(=) || @goto fail + kw isa Expr && kw.head === :(=) || @goto fail kw.head = :kw push!(ex.args, kw) end @@ -410,18 +411,18 @@ function get_test_result(ex, source) negate = QuoteNode(false) orig_ex = ex # Evaluate `not` wrapped functions separately for pretty-printing failures - if isa(ex, Expr) && ex.head == :call && length(ex.args) == 2 && ex.args[1] === :! + if isa(ex, Expr) && ex.head === :call && length(ex.args) == 2 && ex.args[1] === :! negate = QuoteNode(true) ex = ex.args[2] end # Normalize non-dot comparison operator calls to :comparison expressions - is_splat = x -> isa(x, Expr) && x.head == :... - if isa(ex, Expr) && ex.head == :call && length(ex.args) == 3 && + is_splat = x -> isa(x, Expr) && x.head === :... + if isa(ex, Expr) && ex.head === :call && length(ex.args) == 3 && first(string(ex.args[1])) != '.' && !is_splat(ex.args[2]) && !is_splat(ex.args[3]) && (ex.args[1] === :(==) || Base.operator_precedence(ex.args[1]) == comparison_prec) ex = Expr(:comparison, ex.args[2], ex.args[1], ex.args[3]) end - if isa(ex, Expr) && ex.head == :comparison + if isa(ex, Expr) && ex.head === :comparison # pass all terms of the comparison to `eval_comparison`, as an Expr escaped_terms = [esc(arg) for arg in ex.args] quoted_terms = [QuoteNode(arg) for arg in ex.args] @@ -431,7 +432,7 @@ function get_test_result(ex, source) $(QuoteNode(source)), $negate, )) - elseif isa(ex, Expr) && ex.head == :call && ex.args[1] in DISPLAY_FAILED + elseif isa(ex, Expr) && ex.head === :call && ex.args[1] in DISPLAY_FAILED escaped_func = esc(ex.args[1]) quoted_func = QuoteNode(ex.args[1]) @@ -441,18 +442,18 @@ function get_test_result(ex, source) # Keywords that occur before `;`. Note that the keywords are being revised into # a form we can splat. for a in ex.args[2:end] - if isa(a, Expr) && a.head == :kw + if isa(a, Expr) && a.head === :kw push!(escaped_kwargs, Expr(:call, :(=>), QuoteNode(a.args[1]), esc(a.args[2]))) end end # Keywords that occur after ';' parameters_expr = ex.args[2] - if isa(parameters_expr, Expr) && parameters_expr.head == :parameters + if isa(parameters_expr, Expr) && parameters_expr.head === :parameters for a in parameters_expr.args - if isa(a, Expr) && a.head == :kw + if isa(a, Expr) && a.head === :kw push!(escaped_kwargs, Expr(:call, :(=>), QuoteNode(a.args[1]), esc(a.args[2]))) - elseif isa(a, Expr) && a.head == :... + elseif isa(a, Expr) && a.head === :... push!(escaped_kwargs, Expr(:..., esc(a.args[1]))) end end @@ -462,7 +463,7 @@ function get_test_result(ex, source) for a in ex.args[2:end] isa(a, Expr) && a.head in (:kw, :parameters) && continue - if isa(a, Expr) && a.head == :... + if isa(a, Expr) && a.head === :... push!(escaped_args, Expr(:..., esc(a.args[1]))) else push!(escaped_args, esc(a)) @@ -570,7 +571,7 @@ end # An internal function, called by the code generated by @test_throws # to evaluate and catch the thrown exception - if it exists -function do_test_throws(result::ExecutionResult, @nospecialize(orig_expr), @nospecialize(extype)) +function do_test_throws(result::ExecutionResult, orig_expr, extype) if isa(result, Threw) # Check that the right type of exception was thrown success = false @@ -619,6 +620,9 @@ tuple or array, checks that the error output contains/matches each item in `msg` Returns the result of evaluating `expr`. See also [`@test_nowarn`](@ref) to check for the absence of error output. + +Note: Warnings generated by `@warn` cannot be tested with this macro. Use +`@test_logs` instead. """ macro test_warn(msg, expr) quote @@ -643,6 +647,9 @@ end Test whether evaluating `expr` results in empty [`stderr`](@ref) output (no warnings or other messages). Returns the result of evaluating `expr`. + +Note: The absence of warnings generated by `@warn` cannot be tested +with this macro. Use `@test_logs expr` instead. """ macro test_nowarn(expr) quote @@ -760,7 +767,7 @@ function record(ts::DefaultTestSet, t::Union{Fail, Error}) if myid() == 1 printstyled(ts.description, ": ", color=:white) # don't print for interrupted tests - if !(t isa Error) || t.test_type != :test_interrupted + if !(t isa Error) || t.test_type !== :test_interrupted print(t) # don't print the backtrace for Errors because it gets printed in the show # method @@ -779,6 +786,8 @@ end # the results at the end of the tests record(ts::DefaultTestSet, t::AbstractTestSet) = push!(ts.results, t) +@specialize + function print_test_errors(ts::DefaultTestSet) for t in ts.results if (isa(t, Error) || isa(t, Fail)) && myid() == 1 @@ -1060,11 +1069,11 @@ macro testset(args...) tests = args[end] # Determine if a single block or for-loop style - if !isa(tests,Expr) || (tests.head != :for && tests.head != :block) + if !isa(tests,Expr) || (tests.head !== :for && tests.head !== :block) error("Expected begin/end block or for loop as argument to @testset") end - if tests.head == :for + if tests.head === :for return testset_forloop(args, tests, __source__) else return testset_beginend(args, tests, __source__) @@ -1109,7 +1118,7 @@ function testset_beginend(args, tests, source) err isa InterruptException && rethrow() # something in the test block threw an error. Count that as an # error in this test set - record(ts, Error(:nontest_error, :(), err, Base.catch_stack(), $(QuoteNode(source)))) + record(ts, Error(:nontest_error, Expr(:tuple), err, Base.catch_stack(), $(QuoteNode(source)))) finally copy!(RNG, oldrng) end @@ -1132,9 +1141,9 @@ function testset_forloop(args, testloop, source) # description and we'll definitely need them for generating the # comprehension expression at the end loopvars = Expr[] - if testloop.args[1].head == :(=) + if testloop.args[1].head === :(=) push!(loopvars, testloop.args[1]) - elseif testloop.args[1].head == :block + elseif testloop.args[1].head === :block for loopvar in testloop.args[1].args push!(loopvars, loopvar) end @@ -1182,7 +1191,7 @@ function testset_forloop(args, testloop, source) err isa InterruptException && rethrow() # Something in the test block threw an error. Count that as an # error in this test set - record(ts, Error(:nontest_error, :(), err, Base.catch_stack(), $(QuoteNode(source)))) + record(ts, Error(:nontest_error, Expr(:tuple), err, Base.catch_stack(), $(QuoteNode(source)))) end end quote @@ -1221,10 +1230,10 @@ function parse_testset_args(args) if isa(arg, Symbol) testsettype = esc(arg) # a string is the description - elseif isa(arg, AbstractString) || (isa(arg, Expr) && arg.head == :string) + elseif isa(arg, AbstractString) || (isa(arg, Expr) && arg.head === :string) desc = esc(arg) # an assignment is an option - elseif isa(arg, Expr) && arg.head == :(=) + elseif isa(arg, Expr) && arg.head === :(=) # we're building up a Dict literal here key = Expr(:quote, arg.args[1]) push!(options.args, Expr(:call, :(=>), key, arg.args[2])) diff --git a/stdlib/Test/src/logging.jl b/stdlib/Test/src/logging.jl index 66032cdc8f83b..7a254a80b9038 100644 --- a/stdlib/Test/src/logging.jl +++ b/stdlib/Test/src/logging.jl @@ -136,6 +136,11 @@ If we also wanted to test the debug messages, these need to be enabled with the @test_logs (:info,"Doing foo with n=2") (:debug,"Iteration 1") (:debug,"Iteration 2") min_level=Debug foo(2) +If you want to test that some particular messages are generated while ignoring the rest, +you can set the keyword `match_mode=:any`: + + @test_logs (:info,) (:debug,"Iteration 42") min_level=Debug match_mode=:any foo(100) + The macro may be chained with `@test` to also test the returned value: @test (@test_logs (:info,"Doing foo with n=2") foo(2)) == 42 @@ -147,7 +152,7 @@ macro test_logs(exs...) patterns = Any[] kwargs = Any[] for e in exs[1:end-1] - if e isa Expr && e.head == :(=) + if e isa Expr && e.head === :(=) push!(kwargs, esc(Expr(:kw, e.args...))) else push!(patterns, esc(e)) @@ -179,10 +184,10 @@ end function match_logs(f, patterns...; match_mode::Symbol=:all, kwargs...) logs,value = collect_test_logs(f; kwargs...) - if match_mode == :all + if match_mode === :all didmatch = length(logs) == length(patterns) && all(occursin(p, l) for (p,l) in zip(patterns, logs)) - elseif match_mode == :any + elseif match_mode === :any didmatch = all(any(occursin(p, l) for l in logs) for p in patterns) end didmatch,logs,value @@ -190,12 +195,12 @@ end # TODO: Use a version of parse_level from stdlib/Logging, when it exists. function parse_level(level::Symbol) - if level == :belowminlevel return Logging.BelowMinLevel - elseif level == :debug return Logging.Debug - elseif level == :info return Logging.Info - elseif level == :warn return Logging.Warn - elseif level == :error return Logging.Error - elseif level == :abovemaxlevel return Logging.AboveMaxLevel + if level === :belowminlevel return Logging.BelowMinLevel + elseif level === :debug return Logging.Debug + elseif level === :info return Logging.Info + elseif level === :warn return Logging.Warn + elseif level === :error return Logging.Error + elseif level === :abovemaxlevel return Logging.AboveMaxLevel else throw(ArgumentError("Unknown log level $level")) end diff --git a/test/Makefile b/test/Makefile index e4ad2880fa6c9..77a71c6d684d8 100644 --- a/test/Makefile +++ b/test/Makefile @@ -25,6 +25,10 @@ $(TESTS): @cd $(SRCDIR) && \ $(call PRINT_JULIA, $(call spawn,$(JULIA_EXECUTABLE)) --check-bounds=yes --startup-file=no ./runtests.jl $@) +$(addprefix revise-, $(TESTS)): revise-% : + @cd $(SRCDIR) && \ + $(call PRINT_JULIA, $(call spawn,$(JULIA_EXECUTABLE)) --check-bounds=yes --startup-file=no ./runtests.jl --revise $*) + embedding: @$(MAKE) -C $(SRCDIR)/$@ check $(EMBEDDING_ARGS) @@ -35,4 +39,4 @@ clean: @$(MAKE) -C embedding $@ $(EMBEDDING_ARGS) @$(MAKE) -C gcext $@ $(GCEXT_ARGS) -.PHONY: $(TESTS) embedding gcext clean +.PHONY: $(TESTS) $(addprefix revise-, $(TESTS)) embedding gcext clean diff --git a/test/abstractarray.jl b/test/abstractarray.jl index bf6c0f78a30ab..8923c5a90105d 100644 --- a/test/abstractarray.jl +++ b/test/abstractarray.jl @@ -995,3 +995,7 @@ end @test getindex(x) == getindex(x, CartesianIndex()) == 10 end end + +@testset "vcat with mixed elements" begin + @test vcat(Nothing[], [missing], [1.0], [Int8(1)]) isa Vector{Union{Missing, Nothing, Float64}} +end diff --git a/test/arrayops.jl b/test/arrayops.jl index 7a2fa864f543c..c1eb1156bad61 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -2650,3 +2650,10 @@ end # Throws ArgumentError for negative dimensions in Array @test_throws ArgumentError fill('a', -10) + +@testset "Issue 33919" begin + A = Array[rand(2, 3), rand(3, 1)] + B = Array[rand(2, 2), rand(1, 4)] + C = hcat(A, B) + @test typeof(C) == Array{Array{Float64,2},2} +end diff --git a/test/atexit.jl b/test/atexit.jl new file mode 100644 index 0000000000000..103cb1e52bca6 --- /dev/null +++ b/test/atexit.jl @@ -0,0 +1,154 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +using Test + +@testset "atexit.jl" begin + function _atexit_tests_gen_cmd_eval(expr::String) + cmd_eval = ``` + $(Base.julia_cmd()) -e $(expr) + ``` + return cmd_eval + end + function _atexit_tests_gen_cmd_script(temp_dir::String, expr::String) + script, io = mktemp(temp_dir) + println(io, expr) + close(io) + cmd_script = ``` + $(Base.julia_cmd()) $(script) + ``` + return cmd_script + end + atexit_temp_dir = mktempdir() + atexit(() -> rm(atexit_temp_dir; force = true, recursive = true)) + @testset "these should exit with exit code 0" begin + julia_expr_list = Dict( + # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + """ + atexit(() -> exit(0)) + exit(22) + """ => 0, + # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + ) + for julia_expr in keys(julia_expr_list) + cmd_eval = _atexit_tests_gen_cmd_eval(julia_expr) + cmd_script = _atexit_tests_gen_cmd_script(atexit_temp_dir, julia_expr) + expected_exit_code = julia_expr_list[julia_expr] + @test success(cmd_eval) + @test success(cmd_script) + p_eval = run(cmd_eval; wait = false) + p_script = run(cmd_script; wait = false) + wait(p_eval) + wait(p_script) + @test p_eval.exitcode == expected_exit_code + @test p_script.exitcode == expected_exit_code + end + end + @testset "these should exit with exit code 1" begin + julia_expr_list = Dict( + # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + """ + atexit(() -> exit(1)) + exit(22) + """ => 1, + # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + """ + atexit(() -> ("No error")) + atexit(() -> exit(1)) + exit(22) + """ => 1, + # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + """ + atexit(() -> exit(1)) + atexit(() -> exit(1)) + exit(22) + """ => 1, + # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + ) + for julia_expr in keys(julia_expr_list) + cmd_eval = _atexit_tests_gen_cmd_eval(julia_expr) + cmd_script = _atexit_tests_gen_cmd_script(atexit_temp_dir, julia_expr) + expected_exit_code = julia_expr_list[julia_expr] + @test_throws ProcessFailedException run(cmd_eval) + @test_throws ProcessFailedException run(cmd_script) + p_eval = run(cmd_eval; wait = false) + p_script = run(cmd_script; wait = false) + wait(p_eval) + wait(p_script) + @test p_eval.exitcode == expected_exit_code + @test p_script.exitcode == expected_exit_code + end + end + @testset "test exit codes other than 0 or 1" begin + julia_expr_list = Dict( + # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + """ + atexit(() -> exit(13)) + exit(22) + """ => 13, + # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + """ + atexit(() -> ("No error")) + atexit(() -> exit(5)) + exit(22) + """ => 5, + # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + """ + atexit(() -> exit(33)) + atexit(() -> exit(33)) + exit(22) + """ => 33, + # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + """ + atexit(() -> exit(21)) + atexit(() -> exit(21)) + exit(22) + """ => 21, + # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + ) + for julia_expr in keys(julia_expr_list) + cmd_eval = _atexit_tests_gen_cmd_eval(julia_expr) + cmd_script = _atexit_tests_gen_cmd_script(atexit_temp_dir, julia_expr) + expected_exit_code = julia_expr_list[julia_expr] + @test_throws(ProcessFailedException, run(cmd_eval)) + @test_throws(ProcessFailedException, run(cmd_script)) + p_eval = run(cmd_eval; wait = false) + p_script = run(cmd_script; wait = false) + wait(p_eval) + wait(p_script) + @test p_eval.exitcode == expected_exit_code + @test p_script.exitcode == expected_exit_code + end + end + @testset "test what happens if multiple places call exit(n)" begin + julia_expr_list = Dict( + # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + """ + atexit(() -> exit(22)) + atexit(() -> exit(11)) + atexit(() -> exit(33)) + """ => 22, + # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + """ + atexit(() -> exit(4)) + atexit(() -> exit(16)) + atexit(() -> exit(7)) + exit(22) + """ => 4, + # ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + ) + for julia_expr in keys(julia_expr_list) + cmd_eval = _atexit_tests_gen_cmd_eval(julia_expr) + cmd_script = _atexit_tests_gen_cmd_script(atexit_temp_dir, julia_expr) + expected_exit_code = julia_expr_list[julia_expr] + @test_throws(ProcessFailedException, run(cmd_eval)) + @test_throws(ProcessFailedException, run(cmd_script)) + p_eval = run(cmd_eval; wait = false) + p_script = run(cmd_script; wait = false) + wait(p_eval) + wait(p_script) + @test p_eval.exitcode == expected_exit_code + @test p_script.exitcode == expected_exit_code + end + end + rm(atexit_temp_dir; force = true, recursive = true) +end diff --git a/test/backtrace.jl b/test/backtrace.jl index 715f5d2913a9a..8b6ca94c77970 100644 --- a/test/backtrace.jl +++ b/test/backtrace.jl @@ -255,6 +255,6 @@ let code = """ bt_str = read(`$(Base.julia_cmd()) --startup-file=no --compile=min -e $code`, String) @test occursin("InterpreterIP in MethodInstance for foo", bt_str) - @test occursin("InterpreterIP in top-level CodeInfo", bt_str) + @test occursin("InterpreterIP in top-level CodeInfo for Main.A", bt_str) end diff --git a/test/broadcast.jl b/test/broadcast.jl index f2689a6b99586..5edc623222dbf 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -854,3 +854,6 @@ end # treat Pair as scalar: @test replace.(split("The quick brown fox jumps over the lazy dog"), r"[aeiou]"i => "_") == ["Th_", "q__ck", "br_wn", "f_x", "j_mps", "_v_r", "th_", "l_zy", "d_g"] + +# 28680 +@test 1 .+ 1 .+ (1, 2) == (3, 4) diff --git a/test/choosetests.jl b/test/choosetests.jl index 755b80a0bafbf..9f1469ad523d1 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -40,7 +40,7 @@ function choosetests(choices = []) "arrayops", "tuple", "reduce", "reducedim", "abstractarray", "intfuncs", "simdloop", "vecelement", "rational", "bitarray", "copy", "math", "fastmath", "functional", "iterators", - "operators", "path", "ccall", "parse", "loading", "bigint", + "operators", "path", "ccall", "parse", "loading", "gmp", "sorting", "spawn", "backtrace", "exceptions", "file", "read", "version", "namedtuple", "mpfr", "broadcast", "complex", @@ -54,12 +54,13 @@ function choosetests(choices = []) "checked", "bitset", "floatfuncs", "precompile", "boundscheck", "error", "ambiguous", "cartesian", "osutils", "channels", "iostream", "secretbuffer", "specificity", - "reinterpretarray", "syntax", "logging", "missing", "asyncmap" + "reinterpretarray", "syntax", "logging", "missing", "asyncmap", "atexit" ] tests = [] skip_tests = [] exit_on_error = false + use_revise = false seed = rand(RandomDevice(), UInt128) for (i, t) in enumerate(choices) @@ -68,6 +69,8 @@ function choosetests(choices = []) break elseif t == "--exit-on-error" exit_on_error = true + elseif t == "--revise" + use_revise = true elseif startswith(t, "--seed=") seed = parse(UInt128, t[8:end]) else @@ -183,5 +186,5 @@ function choosetests(choices = []) # Filter out tests from the test groups in the stdlibs filter!(!in(skip_tests), tests) - tests, net_on, exit_on_error, seed + tests, net_on, exit_on_error, use_revise, seed end diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index da25fac0da10f..00c3704ee8f34 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -391,3 +391,38 @@ end # Warm up f_dict_hash_alloc(); g_dict_hash_alloc(); @test (@allocated f_dict_hash_alloc()) == (@allocated g_dict_hash_alloc()) + +# returning an argument shouldn't alloc a new box +@noinline f33829(x) = (global called33829 = true; x) +g33829() = @allocated Base.inferencebarrier(f33829)(1.1,) +g33829() # warm up +@test (@allocated g33829()) == 0 +@test called33829 # make sure there was a global side effect so it's hard for this call to simply be removed +let src = get_llvm(f33829, Tuple{Float64}, true, true) + @test occursin(r"call [^(]*double @", src) + @test !occursin(r"call [^(]*\%jl_value_t", src) +end + +let io = IOBuffer() + # Test for the f(args...) = g(args...) generic codegen optimization + code_llvm(io, Base.vect, Tuple{Vararg{Union{Float64, Int64}}}) + @test !occursin("__apply", String(take!(io))) +end + +function f1_30093(r) + while r[]>0 + try + finally + end + end +end + +@test f1_30093(Ref(0)) == nothing + +# issue 33590 +function f33590(b, x) + y = b ? nothing : (x[1] + 1,) + return something(ifelse(b, x, y)) +end +@test f33590(true, (3,)) == (3,) +@test f33590(false, (3,)) == (4,) diff --git a/test/compiler/contextual.jl b/test/compiler/contextual.jl index e2b235830c2d4..302dcaa8d0223 100644 --- a/test/compiler/contextual.jl +++ b/test/compiler/contextual.jl @@ -1,3 +1,5 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + module MiniCassette # A minimal demonstration of the cassette mechanism. Doesn't support all the # fancy features, but sufficient to exercise this code path in the compiler. diff --git a/test/compiler/inference.jl b/test/compiler/inference.jl index 9609043f78652..5f78cf7c97269 100644 --- a/test/compiler/inference.jl +++ b/test/compiler/inference.jl @@ -1711,20 +1711,19 @@ g26826(x) = getfield26826(x, :a, :b) # If this test is broken (especially if inference is getting a correct, but loose result, # like a Union) then it's potentially an indication that the optimizer isn't hitting the # InferenceResult cache properly for varargs methods. -typed_code = Core.Compiler.code_typed(f26826, (Float64,))[1].first -found_well_typed_getfield_call = false -let i +let ct = Core.Compiler.code_typed(f26826, (Float64,))[1] + typed_code, retty = ct.first, ct.second + found_poorly_typed_getfield_call = false for i = 1:length(typed_code.code) stmt = typed_code.code[i] rhs = Meta.isexpr(stmt, :(=)) ? stmt.args[2] : stmt - if Meta.isexpr(rhs, :call) && rhs.args[1] == GlobalRef(Base, :getfield) && typed_code.ssavaluetypes[i] === Float64 - global found_well_typed_getfield_call = true + if Meta.isexpr(rhs, :call) && rhs.args[1] == GlobalRef(Base, :getfield) && typed_code.ssavaluetypes[i] !== Float64 + found_poorly_typed_getfield_call = true end end + @test !found_poorly_typed_getfield_call && retty === Float64 end -@test found_well_typed_getfield_call - # 27059 fix fieldtype vararg and union handling f27059(::Type{T}) where T = i -> fieldtype(T, i) @@ -2445,3 +2444,44 @@ f31974(n::Int) = f31974(1:n) f_overly_abstract_complex() = Complex(Ref{Number}(1)[]) @test Base.return_types(f_overly_abstract_complex, Tuple{}) == [Complex] + +# Issue 26724 +const IntRange = AbstractUnitRange{<:Integer} +const DenseIdx = Union{IntRange,Integer} +@inline foo_26724(result) = + (result...,) +@inline foo_26724(result, i::Integer, I::DenseIdx...) = + foo_26724(result, I...) +@inline foo_26724(result, r::IntRange, I::DenseIdx...) = + foo_26724((result..., length(r)), I...) +@test @inferred(foo_26724((), 1:4, 1:5, 1:6)) === (4, 5, 6) + +# Non uniformity in expresions with PartialTypeVar +@test Core.Compiler.:⊑(Core.Compiler.PartialTypeVar(TypeVar(:N), true, true), TypeVar) +let N = TypeVar(:N) + @test Core.Compiler.apply_type_nothrow([Core.Compiler.Const(NTuple), + Core.Compiler.PartialTypeVar(N, true, true), + Core.Compiler.Const(Any)], Type{Tuple{Vararg{Any,N}}}) +end + +# issue #33768 +function f33768() + Core._apply() +end +function g33768() + a = Any[iterate, tuple, (1,)] + Core._apply_iterate(a...) +end +function h33768() + Core._apply_iterate() +end +@test_throws ArgumentError f33768() +@test Base.return_types(f33768, ()) == Any[Union{}] +@test g33768() === (1,) +@test Base.return_types(g33768, ()) == Any[Any] +@test_throws ArgumentError h33768() +@test Base.return_types(h33768, ()) == Any[Union{}] + +# constant prop of `Symbol("")` +f_getf_computed_symbol(p) = getfield(p, Symbol("first")) +@test Base.return_types(f_getf_computed_symbol, Tuple{Pair{Int8,String}}) == [Int8] diff --git a/test/compiler/irpasses.jl b/test/compiler/irpasses.jl index 5ab548bb8c7ac..46cc4571b5616 100644 --- a/test/compiler/irpasses.jl +++ b/test/compiler/irpasses.jl @@ -255,3 +255,38 @@ let m = Meta.@lower 1 + 1 ret_2 = ir.stmts[ir.cfg.blocks[3].stmts[end]] @test isa(ret_2, Core.Compiler.ReturnNode) && ret_2.val == 2 end + +# Issue #29213 +function f_29213() + while true + try + break + finally + end + end + + while 1==1 + try + ed = (_not_defined,) + finally + break + end + end + + ed = string(ed) +end + +@test_throws UndefVarError f_29213() + +function test_29253(K) + if true + try + error() + catch e + end + end + size(K,1) +end +let K = rand(2,2) + @test test_29253(K) == 2 +end diff --git a/test/copy.jl b/test/copy.jl index 2d70726a3287c..532708e8b303d 100644 --- a/test/copy.jl +++ b/test/copy.jl @@ -193,3 +193,13 @@ end let x = Bar17149() @test deepcopy(x) !== x end + +@testset "copying CodeInfo" begin + _testfunc() = nothing + ci,_ = code_typed(_testfunc, ())[1] + ci.edges = [_testfunc] + + ci2 = copy(ci) + # Test that edges are not shared + @test ci2.edges !== ci.edges +end diff --git a/test/core.jl b/test/core.jl index bf0d8c9d45d5f..e92cb2b96eb60 100644 --- a/test/core.jl +++ b/test/core.jl @@ -5019,13 +5019,15 @@ end # when calculating total allocation size. @noinline function f17255(n) GC.enable(false) - b0 = Base.gc_bytes() + b0 = Ref{Int64}(0) + b1 = Ref{Int64}(0) + Base.gc_bytes(b0) local a for i in 1:n a, t, allocd = @timed [Ref(1) for i in 1:1000] @test allocd > 0 - b1 = Base.gc_bytes() - if b1 < b0 + Base.gc_bytes(b1) + if b1[] < b0[] return false, a end end @@ -5924,6 +5926,14 @@ let x = UnionFieldInlineStruct(1, 3.14) @test CInlineUnion[end] == x end +# issue 33709 +struct A33709 + a::Union{Nothing,A33709} +end +let a33709 = A33709(A33709(nothing)) + @test isnothing(a33709.a.a) +end + # issue 31583 a31583 = "a" f31583() = a31583 === "a" diff --git a/test/file.jl b/test/file.jl index 99f5f5dd5bba3..e15807c0adccb 100644 --- a/test/file.jl +++ b/test/file.jl @@ -1277,6 +1277,20 @@ cd(dirwalk) do end rm(dirwalk, recursive=true) +################### +# readdir # +################### +@testset "readdir is sorted" begin + mktempdir() do dir + cd(dir) do + for k in 1:10 + touch(randstring()) + end + @test issorted(readdir()) + end + end +end + ############ # Clean up # ############ diff --git a/test/float16.jl b/test/float16.jl index a283b68b81fd1..4dd581f76538f 100644 --- a/test/float16.jl +++ b/test/float16.jl @@ -129,7 +129,7 @@ end @test prevfloat(-Inf16) === -Inf16 end -@test repr(Float16(44099)) == "Float16(44100.0)" +@test repr(Float16(44099)) == "Float16(4.41e4)" @testset "signed zeros" begin for z1 in (Float16(0.0), Float16(-0.0)), z2 in (Float16(0.0), Float16(-0.0)) @@ -159,7 +159,7 @@ end end # issue #5948 -@test string(reinterpret(Float16, 0x7bff)) == "65500.0" +@test string(reinterpret(Float16, 0x7bff)) == "6.55e4" # #9939 (and #9897) @test rationalize(Float16(0.1)) == 1//10 diff --git a/test/bigint.jl b/test/gmp.jl similarity index 100% rename from test/bigint.jl rename to test/gmp.jl diff --git a/test/iterators.jl b/test/iterators.jl index 1a7b022c2dbeb..53b18284505eb 100644 --- a/test/iterators.jl +++ b/test/iterators.jl @@ -433,6 +433,85 @@ for n in [5,6] [(1,1),(2,2),(3,3),(4,4),(5,5)] end +function iterate_length(iter) + n=0 + for i in iter + n += 1 + end + return n +end +function simd_iterate_length(iter) + n=0 + @simd for i in iter + n += 1 + end + return n +end +function simd_trip_count(iter) + return sum(Base.SimdLoop.simd_inner_length(iter, i) for i in Base.SimdLoop.simd_outer_range(iter)) +end +function iterate_elements(iter) + vals = Vector{eltype(iter)}(undef, length(iter)) + i = 1 + for v in iter + @inbounds vals[i] = v + i += 1 + end + return vals +end +function simd_iterate_elements(iter) + vals = Vector{eltype(iter)}(undef, length(iter)) + i = 1 + @simd for v in iter + @inbounds vals[i] = v + i += 1 + end + return vals +end +function index_elements(iter) + vals = Vector{eltype(iter)}(undef, length(iter)) + i = 1 + for j in eachindex(iter) + @inbounds vals[i] = iter[j] + i += 1 + end + return vals +end + +@testset "CartesianPartition optimizations" for dims in ((1,), (64,), (101,), + (1,1), (8,8), (11, 13), + (1,1,1), (8, 4, 2), (11, 13, 17)), + part in (1, 7, 8, 11, 63, 64, 65, 142, 143, 144) + P = partition(CartesianIndices(dims), part) + for I in P + @test length(I) == iterate_length(I) == simd_iterate_length(I) == simd_trip_count(I) + @test collect(I) == iterate_elements(I) == simd_iterate_elements(I) == index_elements(I) + end + @test all(Base.splat(==), zip(Iterators.flatten(map(collect, P)), CartesianIndices(dims))) +end +@testset "empty/invalid partitions" begin + @test_throws ArgumentError partition(1:10, 0) + @test_throws ArgumentError partition(1:10, -1) + @test_throws ArgumentError partition(1:0, 0) + @test_throws ArgumentError partition(1:0, -1) + @test isempty(partition(1:0, 1)) + @test isempty(partition(CartesianIndices((0,1)), 1)) +end +@testset "exact partition eltypes" for a in (Base.OneTo(24), 1:24, 1:1:24, LinRange(1,10,24), .1:.1:2.4, Vector(1:24), + CartesianIndices((4, 6)), Dict((1:24) .=> (1:24))) + P = partition(a, 2) + @test eltype(P) === typeof(first(P)) + @test Iterators.IteratorEltype(P) == Iterators.HasEltype() + if a isa AbstractArray + P = partition(vec(a), 2) + @test eltype(P) === typeof(first(P)) + P = partition(reshape(a, 6, 4), 2) + @test eltype(P) === typeof(first(P)) + P = partition(reshape(a, 2, 3, 4), 2) + @test eltype(P) === typeof(first(P)) + end +end + @test join(map(x->string(x...), partition("Hello World!", 5)), "|") == "Hello| Worl|d!" @@ -701,3 +780,7 @@ end @test_throws ArgumentError only(1 for ii in 1:10 if ii > 2) @test_throws ArgumentError only(1 for ii in 1:10 if ii > 200) end + +@testset "flatten empty tuple" begin + @test isempty(collect(Iterators.flatten(()))) +end diff --git a/test/llvmpasses/fastmath.jl b/test/llvmpasses/fastmath.jl new file mode 100644 index 0000000000000..4e0d35e300d5c --- /dev/null +++ b/test/llvmpasses/fastmath.jl @@ -0,0 +1,18 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# RUN: julia --startup-file=no %s %t && llvm-link -S %t/* -o %t/module.ll +# RUN: cat %t/module.ll | FileCheck %s + +## Notes: +# This script uses the `emit` function (defined llvmpasses.jl) to emit either +# optimized or unoptimized LLVM IR. Each function is emitted individually and +# `llvm-link` is used to create a single module that can be passed to opt. +# The order in which files are emitted and linked is important since `lit` will +# process the test cases in order. + +include(joinpath("..", "testhelpers", "llvmpasses.jl")) + +import Base.FastMath + +# CHECK: call fast float @llvm.sqrt.f32(float %0) +emit(FastMath.sqrt_fast, Float32) diff --git a/test/llvmpasses/gcroots.ll b/test/llvmpasses/gcroots.ll index 61062936165b0..fbccd3592b77a 100644 --- a/test/llvmpasses/gcroots.ll +++ b/test/llvmpasses/gcroots.ll @@ -112,6 +112,7 @@ define void @select_lift(i64 %a, i64 %b) { define void @phi_lift(i64 %a, i64 %b) { top: ; CHECK-LABEL: @phi_lift +; CHECK: %gclift = phi %jl_value_t addrspace(10)* [ %aboxed, %alabel ], [ %bboxed, %blabel ], [ %gclift, %common ] %ptls = call %jl_value_t*** @julia.ptls_states() %cmp = icmp eq i64 %a, %b br i1 %cmp, label %alabel, label %blabel @@ -124,11 +125,12 @@ blabel: %bdecayed = addrspacecast %jl_value_t addrspace(10)* %bboxed to i64 addrspace(12)* br label %common common: - %phi = phi i64 addrspace(12)* [ %adecayed, %alabel ], [ %bdecayed, %blabel ] + %phi = phi i64 addrspace(12)* [ %adecayed, %alabel ], [ %bdecayed, %blabel ], [ %phi, %common ] call void @one_arg_decayed(i64 addrspace(12)* %phi) - ret void + br label %common } + define void @phi_lift_union(i64 %a, i64 %b) { top: ; CHECK-LABEL: @phi_lift_union @@ -140,15 +142,14 @@ alabel: ; CHECK: %aboxed = extractvalue { %jl_value_t addrspace(10)*, i8 } %u, 0 %aboxed = extractvalue { %jl_value_t addrspace(10)*, i8 } %u, 0 %adecayed = addrspacecast %jl_value_t addrspace(10)* %aboxed to i64 addrspace(12)* -; CHECK: extractvalue { %jl_value_t addrspace(10)*, i8 } %u, 0 -; CHECK-NEXT: br label %common +; CHECK: br label %common br label %common blabel: %bboxed = call %jl_value_t addrspace(10)* @jl_box_int64(i64 signext %b) %bdecayed = addrspacecast %jl_value_t addrspace(10)* %bboxed to i64 addrspace(12)* br label %common common: -; CHECK: %gclift = phi %jl_value_t addrspace(10)* [ %{{.*}}, %alabel ], [ %bboxed, %blabel ] +; CHECK: %gclift = phi %jl_value_t addrspace(10)* [ %aboxed, %alabel ], [ %bboxed, %blabel ] %phi = phi i64 addrspace(12)* [ %adecayed, %alabel ], [ %bdecayed, %blabel ] call void @one_arg_decayed(i64 addrspace(12)* %phi) ret void @@ -329,6 +330,19 @@ define %jl_value_t addrspace(10)* @vec_loadobj() { ret %jl_value_t addrspace(10)* %v7 } +define %jl_value_t addrspace(10)* @vec_gep() { +; CHECK-LABEL: @vec_gep +; CHECK: %gcframe = alloca %jl_value_t addrspace(10)*, i32 3 + %v4 = call %jl_value_t*** @julia.ptls_states() + %obj = call %jl_value_t addrspace(10) *@alloc() + %obj1 = bitcast %jl_value_t addrspace(10) * %obj to %jl_value_t addrspace(10)* addrspace(10)* + %v1 = getelementptr %jl_value_t addrspace(10)*, %jl_value_t addrspace(10)* addrspace(10)* %obj1, <2 x i32> < i32 0, i32 0 > + call void @jl_safepoint() + %obj2 = extractelement <2 x %jl_value_t addrspace(10)* addrspace(10)*> %v1, i32 0 + %obj3 = bitcast %jl_value_t addrspace(10)* addrspace(10)* %obj2 to %jl_value_t addrspace(10)* + ret %jl_value_t addrspace(10)* %obj3 +} + declare i1 @check_property(%jl_value_t addrspace(10)* %val) define void @loopyness(i1 %cond1, %jl_value_t addrspace(10) *%arg) { ; CHECK-LABEL: @loopyness @@ -499,7 +513,7 @@ top: %ptls = call %jl_value_t*** @julia.ptls_states() %loaded = load <2 x %jl_value_t addrspace(10)*>, <2 x %jl_value_t addrspace(10)*> *%arg call void @jl_safepoint() - %select = select i1 %cond, <2 x %jl_value_t addrspace(10)*> zeroinitializer, <2 x %jl_value_t addrspace(10)*> %loaded + %select = select i1 %cond, <2 x %jl_value_t addrspace(10)*> zeroinitializer, <2 x %jl_value_t addrspace(10)*> %loaded call void @jl_safepoint() %el1 = extractelement <2 x %jl_value_t addrspace(10)*> %select, i32 0 %el2 = extractelement <2 x %jl_value_t addrspace(10)*> %select, i32 1 @@ -508,6 +522,76 @@ top: unreachable } +define void @vecselect_lift(i1 %cond, <2 x %jl_value_t addrspace(10)*> *%arg) { +; CHECK-LABEL: @vecselect_lift +; CHECK: %gcframe = alloca %jl_value_t addrspace(10)*, i32 4 + %ptls = call %jl_value_t*** @julia.ptls_states() + %loaded = load <2 x %jl_value_t addrspace(10)*>, <2 x %jl_value_t addrspace(10)*> *%arg + %decayed = addrspacecast <2 x %jl_value_t addrspace(10)*> %loaded to <2 x i64 addrspace(12)*> + call void @jl_safepoint() +; CHECK: %gclift = select i1 %cond, %jl_value_t addrspace(10)* null, %jl_value_t addrspace(10)* %{{[0-9]+}} + %select = select i1 %cond, <2 x i64 addrspace(12)*> zeroinitializer, <2 x i64 addrspace(12)*> %decayed + call void @jl_safepoint() + %el1 = extractelement <2 x i64 addrspace(12)*> %select, i32 0 + %el2 = extractelement <2 x i64 addrspace(12)*> %select, i32 1 + call void @one_arg_decayed(i64 addrspace(12)* %el1) + call void @one_arg_decayed(i64 addrspace(12)* %el2) + unreachable +} + +define void @vecvecselect_lift(<2 x i1> %cond, <2 x %jl_value_t addrspace(10)*> *%arg) { +; CHECK-LABEL: @vecvecselect_lift +; CHECK: %gcframe = alloca %jl_value_t addrspace(10)*, i32 4 + %ptls = call %jl_value_t*** @julia.ptls_states() + %loaded = load <2 x %jl_value_t addrspace(10)*>, <2 x %jl_value_t addrspace(10)*> *%arg + %decayed = addrspacecast <2 x %jl_value_t addrspace(10)*> %loaded to <2 x i64 addrspace(12)*> + call void @jl_safepoint() +; CHECK: %gclift = select i1 %{{[0-9]+}}, %jl_value_t addrspace(10)* null, %jl_value_t addrspace(10)* %{{[0-9]+}} + %select = select <2 x i1> %cond, <2 x i64 addrspace(12)*> zeroinitializer, <2 x i64 addrspace(12)*> %decayed + call void @jl_safepoint() + %el1 = extractelement <2 x i64 addrspace(12)*> %select, i32 0 + %el2 = extractelement <2 x i64 addrspace(12)*> %select, i32 1 + call void @one_arg_decayed(i64 addrspace(12)* %el1) + call void @one_arg_decayed(i64 addrspace(12)* %el2) + unreachable +} + +define void @vecscalarselect_lift(<2 x i1> %cond, i64 %a) { +; CHECK-LABEL: @vecscalarselect_lift +; CHECK: %gcframe = alloca %jl_value_t addrspace(10)*, i32 4 + %ptls = call %jl_value_t*** @julia.ptls_states() + %aboxed = call %jl_value_t addrspace(10)* @jl_box_int64(i64 signext %a) + %adecayed = addrspacecast %jl_value_t addrspace(10)* %aboxed to i64 addrspace(12)* + %avec = getelementptr i64, i64 addrspace(12)* %adecayed, <2 x i32> zeroinitializer + call void @jl_safepoint() +; CHECK: %gclift = select i1 %{{[0-9]+}}, %jl_value_t addrspace(10)* null, %jl_value_t addrspace(10)* %aboxed + %select = select <2 x i1> %cond, <2 x i64 addrspace(12)*> zeroinitializer, <2 x i64 addrspace(12)*> %avec + call void @jl_safepoint() + %el1 = extractelement <2 x i64 addrspace(12)*> %select, i32 0 + %el2 = extractelement <2 x i64 addrspace(12)*> %select, i32 1 + call void @one_arg_decayed(i64 addrspace(12)* %el1) + call void @one_arg_decayed(i64 addrspace(12)* %el2) + unreachable +} + +define void @scalarvecselect_lift(i1 %cond, i64 %a) { +; CHECK-LABEL: @scalarvecselect_lift +; CHECK: %gcframe = alloca %jl_value_t addrspace(10)*, i32 4 + %ptls = call %jl_value_t*** @julia.ptls_states() + %aboxed = call %jl_value_t addrspace(10)* @jl_box_int64(i64 signext %a) + %adecayed = addrspacecast %jl_value_t addrspace(10)* %aboxed to i64 addrspace(12)* + %avec = getelementptr i64, i64 addrspace(12)* %adecayed, <2 x i32> zeroinitializer + call void @jl_safepoint() +; CHECK: %gclift = select i1 %cond, %jl_value_t addrspace(10)* null, %jl_value_t addrspace(10)* %aboxed + %select = select i1 %cond, <2 x i64 addrspace(12)*> zeroinitializer, <2 x i64 addrspace(12)*> %avec + call void @jl_safepoint() + %el1 = extractelement <2 x i64 addrspace(12)*> %select, i32 0 + %el2 = extractelement <2 x i64 addrspace(12)*> %select, i32 1 + call void @one_arg_decayed(i64 addrspace(12)* %el1) + call void @one_arg_decayed(i64 addrspace(12)* %el2) + unreachable +} + define i8 @select_arrayptr(i1 %cond) { ; CHECK-LABEL: @select_arrayptr ; CHECK: %gcframe = alloca %jl_value_t addrspace(10)*, i32 4 diff --git a/test/llvmpasses/late-lower-gc.ll b/test/llvmpasses/late-lower-gc.ll index e6987bd635d38..1d38522b5da9f 100644 --- a/test/llvmpasses/late-lower-gc.ll +++ b/test/llvmpasses/late-lower-gc.ll @@ -65,11 +65,11 @@ top: %v = call noalias %jl_value_t addrspace(10)* @julia.gc_alloc_obj(i8* %ptls_i8, i64 8, %jl_value_t addrspace(10)* @tag) ; CHECK-NEXT: %v64 = bitcast %jl_value_t addrspace(10)* %v to i64 addrspace(10)* %v64 = bitcast %jl_value_t addrspace(10)* %v to i64 addrspace(10)* -; CHECK-NEXT: %loadedval = load i64, i64 addrspace(10)* %v64, align 8, !range !4 +; CHECK-NEXT: %loadedval = load i64, i64 addrspace(10)* %v64, align 8, !range !5 %loadedval = load i64, i64 addrspace(10)* %v64, align 8, !range !0, !invariant.load !1 -; CHECK-NEXT: store i64 %loadedval, i64 addrspace(10)* %v64, align 8, !noalias !5 +; CHECK-NEXT: store i64 %loadedval, i64 addrspace(10)* %v64, align 8, !noalias !6 store i64 %loadedval, i64 addrspace(10)* %v64, align 8, !noalias !2 -; CHECK-NEXT: %lv2 = load i64, i64 addrspace(10)* %v64, align 8, !tbaa !6, !range !4 +; CHECK-NEXT: %lv2 = load i64, i64 addrspace(10)* %v64, align 8, !tbaa !7, !range !5 %lv2 = load i64, i64 addrspace(10)* %v64, align 8, !range !0, !tbaa !4 ; CHECK-NEXT: ret void ret void @@ -85,9 +85,10 @@ top: ; CHECK: !0 = !{!1, !1, i64 0} ; CHECK-NEXT: !1 = !{!"jtbaa_tag", !2, i64 0} ; CHECK-NEXT: !2 = !{!"jtbaa_data", !3, i64 0} -; CHECK-NEXT: !3 = !{!"jtbaa"} -; CHECK-NEXT: !4 = !{i64 0, i64 23} -; CHECK-NEXT: !5 = distinct !{!5} -; CHECK-NEXT: !6 = !{!7, !7, i64 0} -; CHECK-NEXT: !7 = !{!"jtbaa_const", !8} -; CHECK-NEXT: !8 = !{!"jtbaa"} +; CHECK-NEXT: !3 = !{!"jtbaa", !4, i64 0} +; CHECK-NEXT: !4 = !{!"jtbaa"} +; CHECK-NEXT: !5 = !{i64 0, i64 23} +; CHECK-NEXT: !6 = distinct !{!6} +; CHECK-NEXT: !7 = !{!8, !8, i64 0} +; CHECK-NEXT: !8 = !{!"jtbaa_const", !9} +; CHECK-NEXT: !9 = !{!"jtbaa"} diff --git a/test/llvmpasses/llvmcall.jl b/test/llvmpasses/llvmcall.jl new file mode 100644 index 0000000000000..974413764b7ee --- /dev/null +++ b/test/llvmpasses/llvmcall.jl @@ -0,0 +1,17 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +# RUN: julia --startup-file=no %s %t +# RUN: cat %t/* | FileCheck %s + +include(joinpath("..", "testhelpers", "llvmpasses.jl")) + +@generated foo(x)=:(ccall("extern foo", llvmcall, $x, ($x,), x)) + +# CHECK: call half @foo(half zeroext %{{[0-9]+}}) +emit(foo, Float16) + +# CHECK: call [2 x half] @foo([2 x half] %{{[0-9]+}}) +emit(foo, NTuple{2, Float16}) + +# CHECK: call <2 x half> @foo(<2 x half> %{{[0-9]+}}) +emit(foo, NTuple{2, VecElement{Float16}}) diff --git a/test/loading.jl b/test/loading.jl index c1444c4d978fe..f88c084161d01 100644 --- a/test/loading.jl +++ b/test/loading.jl @@ -282,6 +282,12 @@ module NotPkgModule; end @test pathof(NotPkgModule) === nothing end + @testset "pkgdir" begin + @test pkgdir(Foo) == normpath(abspath(@__DIR__, "project/deps/Foo1")) + @test pkgdir(Foo.SubFoo) == normpath(abspath(@__DIR__, "project/deps/Foo1")) + @test pkgdir(NotPkgModule) === nothing + end + end ## systematic generation of test environments ## diff --git a/test/math.jl b/test/math.jl index 9aa1851091d9a..4515f8ca7b862 100644 --- a/test/math.jl +++ b/test/math.jl @@ -487,17 +487,30 @@ end @testset "evalpoly" begin @test @evalpoly(2,3,4,5,6) == 3+2*(4+2*(5+2*6)) == @evalpoly(2+0im,3,4,5,6) - @test let evalcounts=0 - @evalpoly(begin - evalcounts += 1 - 4 - end, 1,2,3,4,5) - evalcounts - end == 1 a0 = 1 a1 = 2 c = 3 @test @evalpoly(c, a0, a1) == 7 + @test @evalpoly(1, 2) == 2 +end + +@testset "evalpoly real" begin + for x in -1.0:2.0, p1 in -3.0:3.0, p2 in -3.0:3.0, p3 in -3.0:3.0 + evpm = @evalpoly(x, p1, p2, p3) + @test evalpoly(x, (p1, p2, p3)) == evpm + @test evalpoly(x, [p1, p2, p3]) == evpm + end +end + +@testset "evalpoly complex" begin + for x in -1.0:2.0, y in -1.0:2.0, p1 in -3.0:3.0, p2 in -3.0:3.0, p3 in -3.0:3.0 + z = x + im * y + evpm = @evalpoly(z, p1, p2, p3) + @test evalpoly(z, (p1, p2, p3)) == evpm + @test evalpoly(z, [p1, p2, p3]) == evpm + end + @test evalpoly(1+im, (2,)) == 2 + @test evalpoly(1+im, [2,]) == 2 end @testset "cis" begin diff --git a/test/misc.jl b/test/misc.jl index 8b5afee2719af..e92f8e8b7b1bd 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -206,6 +206,11 @@ end @test summarysize(Vector{Nothing}(undef, 16)) == summarysize(Vector{Nothing}(undef, 32)) @test summarysize(S32881()) == sizeof(Int) +# issue #33675 +let vec = vcat(missing, ones(100000)) + @test length(unique(summarysize(vec) for i = 1:20)) == 1 +end + # issue #13021 let ex = try Main.x13021 = 0 @@ -433,6 +438,30 @@ let buf = IOBuffer() @test Base.prompt(IOBuffer("blah\n"), buf, "baz", default="foobar") == "blah" end +# these tests are not in a test block so that they will compile separately +@static if Sys.iswindows() + SetLastError(code) = ccall(:SetLastError, stdcall, Cvoid, (UInt32,), code) +else + SetLastError(_) = nothing +end +@test Libc.errno(0xc0ffee) === nothing +@test SetLastError(0xc0def00d) === nothing +let finalized = false + function closefunc(_) + Libc.errno(0) + SetLastError(0) + finalized = true + end + @eval (finalizer($closefunc, zeros()); nothing) + GC.gc(); GC.gc(); GC.gc(); GC.gc() + @test finalized +end +@static if Sys.iswindows() + @test ccall(:GetLastError, stdcall, UInt32, ()) == 0xc0def00d + @test Libc.GetLastError() == 0xc0def00d +end +@test Libc.errno() == 0xc0ffee + # Test that we can VirtualProtect jitted code to writable @noinline function WeVirtualProtectThisToRWX(x, y) return x + y @@ -445,7 +474,7 @@ end err18083 = ccall(:VirtualProtect, stdcall, Cint, (Ptr{Cvoid}, Csize_t, UInt32, Ptr{UInt32}), addr, 4096, PAGE_EXECUTE_READWRITE, oldPerm) - err18083 == 0 && error(Libc.GetLastError()) + err18083 == 0 && Base.windowserror(:VirtualProtect) end end diff --git a/test/offsetarray.jl b/test/offsetarray.jl index 5db5ee24062ff..480bedecf9ed8 100644 --- a/test/offsetarray.jl +++ b/test/offsetarray.jl @@ -533,15 +533,15 @@ end B = OffsetArray(reshape(1:24, 4, 3, 2), -5, 6, -7) for R in (fill(0, -4:-1), fill(0, -4:-1, 7:7), fill(0, -4:-1, 7:7, -6:-6)) @test @inferred(maximum!(R, B)) == reshape(maximum(B, dims=(2,3)), axes(R)) == reshape(21:24, axes(R)) - @test @allocated(maximum!(R, B)) <= 400 + @test @allocated(maximum!(R, B)) <= 800 @test @inferred(minimum!(R, B)) == reshape(minimum(B, dims=(2,3)), axes(R)) == reshape(1:4, axes(R)) - @test @allocated(minimum!(R, B)) <= 400 + @test @allocated(minimum!(R, B)) <= 800 end for R in (fill(0, -4:-4, 7:9), fill(0, -4:-4, 7:9, -6:-6)) @test @inferred(maximum!(R, B)) == reshape(maximum(B, dims=(1,3)), axes(R)) == reshape(16:4:24, axes(R)) - @test @allocated(maximum!(R, B)) <= 400 + @test @allocated(maximum!(R, B)) <= 800 @test @inferred(minimum!(R, B)) == reshape(minimum(B, dims=(1,3)), axes(R)) == reshape(1:4:9, axes(R)) - @test @allocated(minimum!(R, B)) <= 400 + @test @allocated(minimum!(R, B)) <= 800 end @test_throws DimensionMismatch maximum!(fill(0, -4:-1, 7:7, -6:-6, 1:1), B) @test_throws DimensionMismatch minimum!(fill(0, -4:-1, 7:7, -6:-6, 1:1), B) diff --git a/test/operators.jl b/test/operators.jl index 467d3aa755e2c..58102d7d65a32 100644 --- a/test/operators.jl +++ b/test/operators.jl @@ -105,9 +105,12 @@ Base.convert(::Type{T19714}, ::Int) = T19714() Base.promote_rule(::Type{T19714}, ::Type{Int}) = T19714 @test T19714()/1 === 1/T19714() === T19714() -# pr #17155 +# pr #17155 and #33568 @testset "function composition" begin @test (uppercase∘(x->string(x,base=16)))(239487) == "3A77F" + @test ∘(x -> x-2, x -> x-3, x -> x+5)(7) == 7 + fs = [x -> x[1:2], uppercase, lowercase] + @test ∘(fs...)("ABC") == "AB" end @testset "function negation" begin str = randstring(20) diff --git a/test/osutils.jl b/test/osutils.jl index 6f1d34e647ff9..c9e3b9d91a377 100644 --- a/test/osutils.jl +++ b/test/osutils.jl @@ -49,7 +49,7 @@ end if Sys.iswindows() @testset "path variables use correct path delimiters on windows" begin for path in (Base.SYSCONFDIR, Base.DATAROOTDIR, Base.DOCDIR, - Base.LIBDIR, Base.PRIVATE_LIBDIR, Base.INCLUDEDIR) + Base.LIBDIR, Base.PRIVATE_LIBDIR, Base.INCLUDEDIR, Base.LIBEXECDIR) @test !occursin("/", path) @test !occursin("\\\\", path) end diff --git a/test/path.jl b/test/path.jl index b61535a4031a9..e09a46ef9370e 100644 --- a/test/path.jl +++ b/test/path.jl @@ -31,8 +31,14 @@ @test isdirpath(S("..")) end @testset "joinpath" begin + @test joinpath(S("")) == "" @test joinpath(S("foo")) == "foo" @test joinpath(S("foo"), S("bar")) == "foo$(sep)bar" + @test joinpath(S("foo"), S("bar"), S("baz")) == "foo$(sep)bar$(sep)baz" + @test joinpath(S("foo"), S(""), S("baz")) == "foo$(sep)baz" + @test joinpath(S("foo"), S(""), S("")) == "foo$(sep)" + @test joinpath(S("foo"), S(""), S(""), S("bar")) == "foo$(sep)bar" + @test joinpath(S("foo"), S(homedir())) == homedir() @test joinpath(S(abspath("foo")), S(homedir())) == homedir() @@ -40,6 +46,18 @@ @test joinpath(S("foo"),S("bar:baz")) == "bar:baz" @test joinpath(S("C:"),S("foo"),S("D:"),S("bar")) == "D:bar" @test joinpath(S("C:"),S("foo"),S("D:bar"),S("baz")) == "D:bar$(sep)baz" + + # relative folders and case-insensitive drive letters + @test joinpath(S("C:\\a\\b"), S("c:c\\e")) == "C:\\a\\b\\c\\e" + + # UNC paths + @test joinpath(S("\\\\server"), S("share")) == "\\\\server\\share" + @test joinpath(S("\\\\server"), S("share"), S("a")) == "\\\\server\\share\\a" + @test joinpath(S("\\\\server\\"), S("share"), S("a")) == "\\\\server\\share\\a" + @test joinpath(S("\\\\server"), S("share"), S("a"), S("b")) == "\\\\server\\share\\a\\b" + @test joinpath(S("\\\\server\\share"),S("a")) == "\\\\server\\share\\a" + @test joinpath(S("\\\\server\\share\\"), S("a")) == "\\\\server\\share\\a" + elseif Sys.isunix() @test joinpath(S("foo"),S("bar:baz")) == "foo$(sep)bar:baz" @test joinpath(S("C:"),S("foo"),S("D:"),S("bar")) == "C:$(sep)foo$(sep)D:$(sep)bar" diff --git a/test/project/deps/Foo1/src/Foo.jl b/test/project/deps/Foo1/src/Foo.jl index 2a501ca38d292..9585752949f6c 100644 --- a/test/project/deps/Foo1/src/Foo.jl +++ b/test/project/deps/Foo1/src/Foo.jl @@ -1,6 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license module Foo +include("SubFoo.jl") import Bar, Baz, Qux this = "Foo1" which = "path" diff --git a/test/project/deps/Foo1/src/SubFoo.jl b/test/project/deps/Foo1/src/SubFoo.jl new file mode 100644 index 0000000000000..8256517774385 --- /dev/null +++ b/test/project/deps/Foo1/src/SubFoo.jl @@ -0,0 +1,5 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +module SubFoo +thissub = "Foo1.SubFoo" +end diff --git a/test/ranges.jl b/test/ranges.jl index c765900f6d78b..878cd48077db7 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -305,6 +305,8 @@ end @test findfirst(==(7), 1:2:10) == 4 @test findfirst(==(10), 1:2:10) == nothing @test findfirst(==(11), 1:2:10) == nothing + @test findfirst(==(-7), 1:-1:-10) == 9 + @test findfirst(==(2),1:-1:2) == nothing end @testset "reverse" begin @test reverse(reverse(1:10)) == 1:10 @@ -1490,8 +1492,6 @@ end end @testset "allocation of TwicePrecision call" begin - 0:286.493442:360 - 0:286:360 @test @allocated(0:286.493442:360) == 0 @test @allocated(0:286:360) == 0 end diff --git a/test/runtests.jl b/test/runtests.jl index de32d292c8fca..23a649bab9aa7 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,9 +9,13 @@ using Printf: @sprintf include("choosetests.jl") include("testenv.jl") -tests, net_on, exit_on_error, seed = choosetests(ARGS) +tests, net_on, exit_on_error, use_revise, seed = choosetests(ARGS) tests = unique(tests) +if use_revise + using Revise +end + const max_worker_rss = if haskey(ENV, "JULIA_TEST_MAXRSS_MB") parse(Int, ENV["JULIA_TEST_MAXRSS_MB"]) * 2^20 else @@ -71,26 +75,38 @@ cd(@__DIR__) do @everywhere include("testdefs.jl") + if use_revise + @everywhere begin + Revise.track(Core.Compiler) + Revise.track(Base) + Revise.revise() + end + end + #pretty print the information about gc and mem usage testgroupheader = "Test" workerheader = "(Worker)" - name_align = maximum([length(testgroupheader) + length(" ") + length(workerheader); map(x -> length(x) + 3 + ndigits(nworkers()), tests)]) - elapsed_align = length("Time (s)") - gc_align = length("GC (s)") - percent_align = length("GC %") - alloc_align = length("Alloc (MB)") - rss_align = length("RSS (MB)") + name_align = maximum([textwidth(testgroupheader) + textwidth(" ") + textwidth(workerheader); map(x -> textwidth(x) + 3 + ndigits(nworkers()), tests)]) + elapsed_align = textwidth("Time (s)") + gc_align = textwidth("GC (s)") + percent_align = textwidth("GC %") + alloc_align = textwidth("Alloc (MB)") + rss_align = textwidth("RSS (MB)") printstyled(testgroupheader, color=:white) - printstyled(lpad(workerheader, name_align - length(testgroupheader) + 1), " | ", color=:white) + printstyled(lpad(workerheader, name_align - textwidth(testgroupheader) + 1), " | ", color=:white) printstyled("Time (s) | GC (s) | GC % | Alloc (MB) | RSS (MB)\n", color=:white) - results=[] - print_lock = ReentrantLock() + results = [] + print_lock = stdout isa Base.LibuvStream ? stdout.lock : ReentrantLock() + if stderr isa Base.LibuvStream + stderr.lock = print_lock + end function print_testworker_stats(test, wrkr, resp) + @nospecialize resp lock(print_lock) try printstyled(test, color=:white) - printstyled(lpad("($wrkr)", name_align - length(test) + 1, " "), " | ", color=:white) + printstyled(lpad("($wrkr)", name_align - textwidth(test) + 1, " "), " | ", color=:white) time_str = @sprintf("%7.2f",resp[2]) printstyled(lpad(time_str, elapsed_align, " "), " | ", color=:white) gc_str = @sprintf("%5.2f", resp[5].total_time / 10^9) @@ -110,16 +126,28 @@ cd(@__DIR__) do end global print_testworker_started = (name, wrkr)->begin - lock(print_lock) + lock(print_lock) try printstyled(name, color=:white) - printstyled(lpad("($wrkr)", name_align - length(name) + 1, " "), " |", + printstyled(lpad("($wrkr)", name_align - textwidth(name) + 1, " "), " |", " "^elapsed_align, "started at $(now())\n", color=:white) finally unlock(print_lock) end end + function print_testworker_errored(name, wrkr) + lock(print_lock) + try + printstyled(name, color=:red) + printstyled(lpad("($wrkr)", name_align - textwidth(name) + 1, " "), " |", + " "^elapsed_align, " failed at $(now())\n", color=:red) + finally + unlock(print_lock) + end + end + + all_tests = [tests; node1_tests] local stdin_monitor @@ -158,25 +186,35 @@ cd(@__DIR__) do resp = remotecall_fetch(runtests, wrkr, test, test_path(test); seed=seed) catch e isa(e, InterruptException) && return - resp = [e] + resp = Any[e] end push!(results, (test, resp)) if resp[1] isa Exception + print_testworker_errored(test, wrkr) if exit_on_error skipped = length(tests) empty!(tests) - end - elseif resp[end] > max_worker_rss - if n > 1 + elseif n > 1 + # the worker encountered some failure, recycle it + # so future tests get a fresh environment rmprocs(wrkr, waitfor=30) p = addprocs_with_testenv(1)[1] remotecall_fetch(include, p, "testdefs.jl") - else # single process testing - error("Halting tests. Memory limit reached : $resp > $max_worker_rss") end - end - - !isa(resp[1], Exception) && print_testworker_stats(test, wrkr, resp) + else + print_testworker_stats(test, wrkr, resp) + if resp[end] > max_worker_rss + # the worker has reached the max-rss limit, recycle it + # so future tests start with a smaller working set + if n > 1 + rmprocs(wrkr, waitfor=30) + p = addprocs_with_testenv(1)[1] + remotecall_fetch(include, p, "testdefs.jl") + else # single process testing + error("Halting tests. Memory limit reached : $resp > $max_worker_rss") + end + end + end end if p != 1 # Free up memory =) @@ -200,7 +238,7 @@ cd(@__DIR__) do resp = eval(Expr(:call, () -> runtests(t, test_path(t), isolate, seed=seed))) # runtests is defined by the include above print_testworker_stats(t, 1, resp) catch e - resp = [e] + resp = Any[e] end push!(results, (t, resp)) end @@ -249,63 +287,65 @@ cd(@__DIR__) do o_ts = Test.DefaultTestSet("Overall") Test.push_testset(o_ts) completed_tests = Set{String}() - for res in results - push!(completed_tests, res[1]) - if isa(res[2][1], Test.DefaultTestSet) - Test.push_testset(res[2][1]) - Test.record(o_ts, res[2][1]) + for (testname, (resp,)) in results + push!(completed_tests, testname) + if isa(resp, Test.DefaultTestSet) + Test.push_testset(resp) + Test.record(o_ts, resp) Test.pop_testset() - elseif isa(res[2][1], Tuple{Int,Int}) - fake = Test.DefaultTestSet(res[1]) - for i in 1:res[2][1][1] + elseif isa(resp, Tuple{Int,Int}) + fake = Test.DefaultTestSet(testname) + for i in 1:resp[1] Test.record(fake, Test.Pass(:test, nothing, nothing, nothing)) end - for i in 1:res[2][1][2] + for i in 1:resp[2] Test.record(fake, Test.Broken(:test, nothing)) end Test.push_testset(fake) Test.record(o_ts, fake) Test.pop_testset() - elseif isa(res[2][1], RemoteException) && isa(res[2][1].captured.ex, Test.TestSetException) - println("Worker $(res[2][1].pid) failed running test $(res[1]):") - Base.showerror(stdout,res[2][1].captured) - fake = Test.DefaultTestSet(res[1]) - for i in 1:res[2][1].captured.ex.pass + elseif isa(resp, RemoteException) && isa(resp.captured.ex, Test.TestSetException) + println("Worker $(resp.pid) failed running test $(testname):") + Base.showerror(stdout, resp.captured) + println() + fake = Test.DefaultTestSet(testname) + for i in 1:resp.captured.ex.pass Test.record(fake, Test.Pass(:test, nothing, nothing, nothing)) end - for i in 1:res[2][1].captured.ex.broken + for i in 1:resp.captured.ex.broken Test.record(fake, Test.Broken(:test, nothing)) end - for t in res[2][1].captured.ex.errors_and_fails + for t in resp.captured.ex.errors_and_fails Test.record(fake, t) end Test.push_testset(fake) Test.record(o_ts, fake) Test.pop_testset() - elseif isa(res[2][1], Exception) + else + if !isa(resp, Exception) + resp = ErrorException(string("Unknown result type : ", typeof(resp))) + end # If this test raised an exception that is not a remote testset exception, # i.e. not a RemoteException capturing a TestSetException that means # the test runner itself had some problem, so we may have hit a segfault, # deserialization errors or something similar. Record this testset as Errored. - fake = Test.DefaultTestSet(res[1]) - Test.record(fake, Test.Error(:test_error, res[1], res[2][1], [], LineNumberNode(1))) + fake = Test.DefaultTestSet(testname) + Test.record(fake, Test.Error(:test_error, testname, nothing, Any[(resp, [])], LineNumberNode(1))) Test.push_testset(fake) Test.record(o_ts, fake) Test.pop_testset() - else - error(string("Unknown result type : ", typeof(res))) end end for test in all_tests (test in completed_tests) && continue fake = Test.DefaultTestSet(test) - Test.record(fake, Test.Error(:test_interrupted, test, InterruptException(), [], LineNumberNode(1))) + Test.record(fake, Test.Error(:test_interrupted, test, nothing, [("skipped", [])], LineNumberNode(1))) Test.push_testset(fake) Test.record(o_ts, fake) Test.pop_testset() end println() - Test.print_test_results(o_ts,1) + Test.print_test_results(o_ts, 1) if !o_ts.anynonpass println(" \033[32;1mSUCCESS\033[0m") else diff --git a/test/ryu.jl b/test/ryu.jl index 9a7ed91155676..9970942575e0a 100644 --- a/test/ryu.jl +++ b/test/ryu.jl @@ -1,3 +1,5 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + using Base.Ryu const maxMantissa = (UInt64(1) << 53) - 1 @@ -738,24 +740,31 @@ end # exp @testset "compact" begin - writecompact(x) = Ryu.writeshortest(x, false, false, true, -1, UInt8('e'), false, UInt8('.'), false, true) - - @test writecompact(0.49999999) == "0.5" - @test writecompact(0.459999999) == "0.46" - @test writecompact(0.20058603493384108) == "0.200586" - @test writecompact(0.9999999) == "1.0" - @test writecompact(0.1999999) == "0.2" - @test writecompact(123.4567) == "123.457" - @test writecompact(0.001234567) == "0.00123457" - @test writecompact(0.1234567) == "0.123457" - @test writecompact(1234567.0) == "123457.0" - @test writecompact(12345678910.0) == "1.23457e10" - @test writecompact(12345678.0) == "123457.0" - @test writecompact(0.10000049) == "0.1" - @test writecompact(22.89825) == "22.8983" - @test writecompact(0.646690981531646) == "0.646691" - @test writecompact(6.938893903907228e-17) == "6.93889e-17" - + stringcompact(x) = sprint(show, x; context=:compact => true) + + @test stringcompact(0.49999999) == "0.5" + @test stringcompact(0.459999999) == "0.46" + @test stringcompact(0.20058603493384108) == "0.200586" + @test stringcompact(0.9999999) == "1.0" + @test stringcompact(0.1999999) == "0.2" + @test stringcompact(123.4567) == "123.457" + @test stringcompact(0.001234567) == "0.00123457" + @test stringcompact(0.1234567) == "0.123457" + @test stringcompact(1234567.0) == "1.23457e6" + @test stringcompact(12345678910.0) == "1.23457e10" + @test stringcompact(12345678.0) == "1.23457e7" + @test stringcompact(0.10000049) == "0.1" + @test stringcompact(22.89825) == "22.8983" + @test stringcompact(0.646690981531646) == "0.646691" + @test stringcompact(6.938893903907228e-17) == "6.93889e-17" + @test stringcompact(1.015625) == "1.01562" + @test stringcompact(1.046875) == "1.04688" + @test stringcompact(0.025621074) == "0.0256211" + + # subnormals + @test stringcompact(eps(0.0)) == "5.0e-324" + @test stringcompact(eps(0f0)) == "1.0f-45" + @test stringcompact(eps(Float16(0.0))) == "6.0e-8" end end # Ryu diff --git a/test/show.jl b/test/show.jl index bdf3e5a44f797..bb791d9527060 100644 --- a/test/show.jl +++ b/test/show.jl @@ -53,21 +53,76 @@ macro test_repr(x) # this is a macro instead of function so we can avoid getting useful backtraces :) return :(test_repr($(esc(x)))) end -function test_repr(x::String) +macro weak_test_repr(x) + # this is a macro instead of function so we can avoid getting useful backtraces :) + return :(test_repr($(esc(x)), true)) +end +function test_repr(x::String, remove_linenums::Bool = false) # Note: We can't just compare x1 and x2 because interpolated # strings get converted to string Exprs by the first show(). # This could produce a few false positives, but until string # interpolation works we don't really have a choice. + # + # Rectification: comparing x1 and x2 seems to be working x1 = Meta.parse(x) x2 = eval(Meta.parse(repr(x1))) x3 = eval(Meta.parse(repr(x2))) - if x3 != x1 + if ! (x1 == x2 == x3) error(string( - "repr test failed:", + "\nrepr test (Rule 2) failed:", "\noriginal: ", x, + "\n\npreparsed: ", x1, "\n", sprint(dump, x1), "\n\nparsed: ", x2, "\n", sprint(dump, x2), - "\n\nreparsed: ", x3, "\n", sprint(dump, x3) - )) + "\n\nreparsed: ", x3, "\n", sprint(dump, x3), + "\n\n")) + end + @test x1 == x2 == x3 + + x4 = Base.remove_linenums!(Meta.parse(x)) + x5 = eval(Base.remove_linenums!(Meta.parse(repr(x4)))) + x6 = eval(Base.remove_linenums!(Meta.parse(repr(x5)))) + if ! (x4 == x5 == x6) + error(string( + "\nrepr test (Rule 2) without line numbers failed:", + "\noriginal: ", x, + "\n\npreparsed: ", x4, "\n", sprint(dump, x4), + "\n\nparsed: ", x5, "\n", sprint(dump, x5), + "\n\nreparsed: ", x6, "\n", sprint(dump, x6), + "\n\n")) + end + @test x4 == x5 == x6 + + @test Base.remove_linenums!(x1) == + Base.remove_linenums!(x2) == + Base.remove_linenums!(x3) == + x4 == x5 == x6 + + if isa(x1, Expr) && remove_linenums + if Base.remove_linenums!(Meta.parse(string(x1))) != x1 + error(string( + "\nstring test (Rule 1) failed:", + "\noriginal: ", x, + "\n\npreparsed: ", x1, "\n", sprint(dump, x4), + "\n\nstring(preparsed): ", string(x1), + "\n\nBase.remove_linenums!(Meta.parse(string(preparsed))): ", + Base.remove_linenums!(Meta.parse(string(x1))), "\n", + sprint(dump, Base.remove_linenums!(Meta.parse(string(x1)))), + "\n\n")) + end + @test Base.remove_linenums!(Meta.parse(string(x1))) == x1 + elseif isa(x1, Expr) + if Meta.parse(string(x1)) != x1 + error(string( + "\nstring test (Rule 1) failed:", + "\noriginal: ", x, + "\n\npreparsed: ", x1, "\n", sprint(dump, x4), + "\n\nstring(preparsed): ", string(x1), + "\n\nMeta.parse(string(preparsed)): ", + Meta.parse(string(x1)), "\n", + sprint(dump, Meta.parse(string(x1))), + "\n\n")) + end + @test Meta.parse(string(x1)) == x1 end end @@ -143,9 +198,29 @@ end @test_repr "import A: a, x, y.z" @test_repr "import A.B.C: a, x, y.z" @test_repr "import ..A: a, x, y.z" +@test_repr "import A.B, C.D" @test repr(Expr(:using, :Foo)) == ":(\$(Expr(:using, :Foo)))" @test repr(Expr(:using, Expr(:(.), ))) == ":(\$(Expr(:using, :(\$(Expr(:.))))))" +@test repr(Expr(:import, :Foo)) == ":(\$(Expr(:import, :Foo)))" +@test repr(Expr(:import, Expr(:(.), ))) == ":(\$(Expr(:import, :(\$(Expr(:.))))))" + +@test repr(Expr(:using, Expr(:(.), :A))) == ":(using A)" +@test repr(Expr(:using, Expr(:(.), :A), + Expr(:(.), :B))) == ":(using A, B)" +@test repr(Expr(:using, Expr(:(.), :A), + Expr(:(.), :B, :C), + Expr(:(.), :D))) == ":(using A, B.C, D)" +@test repr(Expr(:using, Expr(:(.), :A, :B), + Expr(:(.), :C, :D))) == ":(using A.B, C.D)" +@test repr(Expr(:import, Expr(:(.), :A))) == ":(import A)" +@test repr(Expr(:import, Expr(:(.), :A), + Expr(:(.), :B))) == ":(import A, B)" +@test repr(Expr(:import, Expr(:(.), :A), + Expr(:(.), :B, :(C)), + Expr(:(.), :D))) == ":(import A, B.C, D)" +@test repr(Expr(:import, Expr(:(.), :A, :B), + Expr(:(.), :C, :D))) == ":(import A.B, C.D)" # range syntax @test_repr "1:2" @@ -163,7 +238,7 @@ end # control structures (shamelessly stolen from base/bitarray.jl) -@test_repr """mutable struct BitArray{N} <: AbstractArray{Bool, N} +@weak_test_repr """mutable struct BitArray{N} <: AbstractArray{Bool, N} # line meta chunks::Vector{UInt64} # line meta @@ -210,7 +285,7 @@ end end end""" -@test_repr """function copy_chunks(dest::Vector{UInt64}, pos_d::Integer, src::Vector{UInt64}, pos_s::Integer, numbits::Integer) +@weak_test_repr """function copy_chunks(dest::Vector{UInt64}, pos_d::Integer, src::Vector{UInt64}, pos_s::Integer, numbits::Integer) # line meta if numbits == 0 # line meta @@ -289,13 +364,13 @@ end""" return end""" -@test_repr """if a +@weak_test_repr """if a # line meta b end """ -@test_repr """if a +@weak_test_repr """if a # line meta b elseif c @@ -304,7 +379,7 @@ d end """ -@test_repr """if a +@weak_test_repr """if a # line meta b elseif c @@ -316,7 +391,7 @@ e end """ -@test_repr """if a +@weak_test_repr """if a # line meta b elseif c @@ -328,7 +403,7 @@ f end """ -@test_repr """f(x, y) do z, w +@weak_test_repr """f(x, y) do z, w # line meta a # line meta @@ -336,7 +411,7 @@ b end """ -@test_repr """f(x, y) do z +@weak_test_repr """f(x, y) do z # line meta a # line meta @@ -351,6 +426,10 @@ end @test sprint(show, :+) == ":+" @test sprint(show, :end) == ":end" +# make sure :var"'" prints correctly +@test sprint(show, Symbol("'")) == "Symbol(\"'\")" +@test_repr "var\"'\" = 5" + # issue #32408: Printing of names which are invalid identifiers # Invalid identifiers which need `var` quoting: @test sprint(show, Expr(:call, :foo, Symbol("##"))) == ":(foo(var\"##\"))" @@ -393,7 +472,7 @@ end @test_repr "[1 2 3; 4 5 6; 7 8 9]'" -@test_repr "baremodule X +@weak_test_repr "baremodule X # line meta # line meta import ...B.c @@ -402,7 +481,7 @@ import D # line meta import B.C.D.E.F.g end" -@test_repr "baremodule Y +@weak_test_repr "baremodule Y # line meta # line meta export A, B, C @@ -452,9 +531,6 @@ let a = Expr(:quote,Expr(:$,:x8d003)) @test eval(eval(Meta.parse(repr(a)))) == 2 end -# issue #9865 -@test occursin(r"^Set\(\[.+….+\]\)$", replstr(Set(1:100))) - # issue #11413 @test string(:(*{1, 2})) == "*{1, 2}" @test string(:(*{1, x})) == "*{1, x}" @@ -637,7 +713,6 @@ else end # Method location correction (Revise integration) -methloc = Base.methodloc_callback[] dummyloc(m::Method) = :nofile, 123456789 Base.methodloc_callback[] = dummyloc let repr = sprint(show, "text/plain", methods(Base.inbase)) @@ -646,7 +721,7 @@ end let repr = sprint(show, "text/html", methods(Base.inbase)) @test occursin("nofile:123456789", repr) end -Base.methodloc_callback[] = methloc +Base.methodloc_callback[] = nothing @testset "matrix printing" begin # print_matrix should be able to handle small and large objects easily, test by @@ -769,6 +844,21 @@ test_mt(show_f5, "show_f5(A::AbstractArray{T,N}, indices::Vararg{$Int,N})") @test_repr "{a b c}" @test_repr "{a b; c d}" +# typed vcat and hcat +@test_repr "T[a]" +@test_repr "T[a,b]" +@test_repr "T[a;b;c]" +@test_repr "T[a b]" +@test_repr "T[a b;]" +@test_repr "T[a b c]" +@test_repr "T[a b; c d]" +@test_repr repr(Expr(:quote, Expr(:typed_vcat, Expr(:$, :a), 1))) +@test_repr repr(Expr(:quote, Expr(:typed_hcat, Expr(:$, :a), 1))) +@test_repr "Expr(:quote, Expr(:typed_vcat, Expr(:\$, :a), 1))" +@test_repr "Expr(:quote, Expr(:typed_hcat, Expr(:\$, :a), 1))" +@test repr(Expr(:quote, Expr(:typed_vcat, Expr(:$, :a), 1))) == ":(:(\$a[1;]))" +@test repr(Expr(:quote, Expr(:typed_hcat, Expr(:$, :a), 1))) == ":(:(\$a[1]))" + # Printing of :(function f end) @test sprint(show, :(function f end)) == ":(function f end)" @test_repr "function g end" @@ -778,17 +868,238 @@ test_mt(show_f5, "show_f5(A::AbstractArray{T,N}, indices::Vararg{$Int,N})") @test_repr "macro m end" @test sprint(show, Expr(:macro, Expr(:call, :m, :ex), Expr(:block, :m))) == ":(macro m(ex)\n m\n end)" -@test_repr """macro identity(ex) +@weak_test_repr """macro identity(ex) # line meta esc(ex) end""" -@test_repr """macro m(a,b) +@weak_test_repr """macro m(a,b) # line meta quote # line meta \$a + \$b end end""" +@test repr(Meta.parse( +"""macro m(a, b) + quote + \$a + \$b + end +end""")) == +""" +:(macro m(a, b) + #= none:2 =# + quote + #= none:3 =# + \$a + \$b + end + end)""" +@test repr(Base.remove_linenums!(Meta.parse( +"""macro m(a, b) + quote + \$a + \$b + end +end"""))) == +""" +:(macro m(a, b) + quote + \$a + \$b + end + end)""" +@test repr(Meta.parse( +"""macro m(a, b) + :(\$a + \$b) +end""")) == +":(macro m(a, b)\n #= none:2 =#\n :(\$a + \$b)\n end)" +@test repr(Expr(:macro, Expr(:call, :m, :x), Expr(:quote, Expr(:call, :+, Expr(:($), :x), 1)))) == +":(macro m(x)\n :(\$x + 1)\n end)" + +# nested quotes and interpolations +@test repr(Meta.parse( +"""quote + quote + \$\$x + end +end""")) == +""" +:(quote + #= none:2 =# + quote + #= none:3 =# + \$\$x + end + end)""" +@weak_test_repr """ +quote + #= none:2 =# + quote + #= none:3 =# + \$\$x + end +end""" + +# fallback printing + nested quotes and unquotes +@weak_test_repr repr(Expr(:block, LineNumberNode(0, :none), + Expr(:exotic_head, Expr(:$, :x)))) +@test_repr repr(Expr(:exotic_head, Expr(:call, :+, 1, Expr(:quote, Expr(:$, Expr(:$, :y)))))) +@test_repr repr(Expr(:quote, Expr(:$, Expr(:exotic_head, Expr(:call, :+, 1, Expr(:$, :y)))))) +@test_repr repr(Expr(:$, Expr(:exotic_head, Expr(:call, :+, 1, Expr(:$, :y))))) +@test_repr "Expr(:block, LineNumberNode(0, :none), Expr(:exotic_head, Expr(:\$, :x)))" +@test_repr "Expr(:exotic_head, Expr(:call, :+, 1, \$y))" +@test_repr "Expr(:exotic_head, Expr(:call, :+, 1, \$\$y))" +@test_repr ":(Expr(:exotic_head, Expr(:call, :+, 1, \$y)))" +@test_repr ":(:(Expr(:exotic_head, Expr(:call, :+, 1, \$\$y))))" +@test repr(Expr(:block, LineNumberNode(0, :none), + Expr(:exotic_head, Expr(:$, :x)))) == +""" +quote + #= none:0 =# + \$(Expr(:exotic_head, :(\$x))) +end""" +@test repr(Expr(:exotic_head, Expr(:call, :+, 1, :(Expr(:$, :y))))) == + ":(\$(Expr(:exotic_head, :(1 + Expr(:\$, :y)))))" +@test repr(Expr(:exotic_head, Expr(:call, :+, 1, Expr(:quote, Expr(:$, :y))))) == + ":(\$(Expr(:exotic_head, :(1 + :(\$y)))))" +@test repr(Expr(:quote, Expr(:exotic_head, Expr(:call, :+, 1, Expr(:$, :y))))) == + ":(:(\$(Expr(:exotic_head, :(1 + \$y)))))" +@test repr(Expr(:block, Expr(:(=), :y, 2), + Expr(:quote, Expr(:exotic_head, + Expr(:call, :+, 1, Expr(:$, :y)))))) == +""" +quote + y = 2 + :(\$(Expr(:exotic_head, :(1 + \$y)))) +end""" +@test repr(eval(Expr(:block, Expr(:(=), :y, 2), + Expr(:quote, Expr(:exotic_head, + Expr(:call, :+, 1, Expr(:$, :y))))))) == + ":(\$(Expr(:exotic_head, :(1 + 2))))" + +# nested quotes and blocks +@test_repr "Expr(:quote, Expr(:block, :a, :b))" +@weak_test_repr repr(Expr(:quote, Expr(:block, LineNumberNode(0, :none), :a, LineNumberNode(0, :none), :b))) +@test repr(Expr(:quote, Expr(:block, :a, :b))) == +":(quote + a + b + end)" +@test_repr "Expr(:quote, Expr(:block, :a))" +@weak_test_repr repr(Expr(:quote, Expr(:block, LineNumberNode(0, :none), :a))) +@test repr(Expr(:quote, Expr(:block, :a))) == +":(quote + a + end)" +@test_repr "Expr(:quote, Expr(:block, :(a + b)))" +@weak_test_repr repr(Expr(:quote, Expr(:block, LineNumberNode(0, :none), :(a + b)))) +@test repr(Expr(:quote, Expr(:block, :(a + b)))) == +":(quote + a + b + end)" + +# QuoteNode + quotes and unquotes +@test_repr "QuoteNode(\$x)" +@test_repr "QuoteNode(\$\$x)" +@test_repr ":(QuoteNode(\$x))" +@test_repr ":(:(QuoteNode(\$\$x)))" +@test repr(QuoteNode(Expr(:$, :x))) == ":(\$(QuoteNode(:(\$(Expr(:\$, :x))))))" +@test repr(QuoteNode(Expr(:quote, Expr(:$, :x)))) == ":(\$(QuoteNode(:(:(\$x)))))" +@test repr(Expr(:quote, QuoteNode(Expr(:$, :x)))) == ":(:(\$(QuoteNode(:(\$(Expr(:\$, :x)))))))" + +# unquoting +@test_repr "\$y" +@test_repr "\$\$y" +@weak_test_repr """ +begin + # line meta + \$y +end""" +@weak_test_repr """ +begin + # line meta + \$\$y +end""" +@test_repr ":(\$\$y)" +@test_repr repr(Expr(:$, :y)) + +# with reference to https://github.com/JuliaLang/julia/commit/9ef17207d5f99c7a0019cbbe0e58f77e7c4c1d21 +y856739 = 2 +x856739 = :y856739 +z856739 = [:a, :b] +@test_repr repr(:(:(f($$x856739)))) +@test repr(:(:(f($$x856739)))) == ":(:(f(\$y856739)))" +@test repr(eval(:(:(f($$x856739))))) == ":(f(2))" +@test_repr repr(:(:(f($x856739)))) +@test repr(:(:(f($x856739)))) == ":(:(f(\$x856739)))" +@test repr(eval(:(:(f($x856739))))) == ":(f(y856739))" +@test_repr repr(:(:(f($(($z856739)...))))) +@test repr(:(:(f($(($z856739)...))))) == ":(:(f(\$([:a, :b]...))))" +@test repr(eval(:(:(f($(($z856739)...)))))) == ":(f(a, b))" + +# string interpolation, if this is what the comment in test_rep function +# definition talk about +@test repr(Expr(:string, "foo", :x, "bar")) == ":(\"foo\$(x)bar\")" +@test Meta.parse(string(Expr(:string, "foo", :x, "bar"))) == Expr(:string, "foo", :x, "bar") +@test repr(Meta.parse("\"foo\$(x)bar\"")) == ":(\"foo\$(x)bar\")" +@test_repr "\"foo\$(x)bar\"" + +# Printing of macrocall expressions with qualified macroname argument +@test sprint(show, Expr(:macrocall, + GlobalRef(Base, Symbol("@m")), + LineNumberNode(0, :none), :a, :b)) == + ":(#= none:0 =# Base.@m a b)" +@test sprint(show, Expr(:macrocall, + Expr(:(.), :Base, Expr(:quote, Symbol("@m"))), + LineNumberNode(0, :none), :a, :b)) == + ":(#= none:0 =# Base.@m a b)" +@test sprint(show, Expr(:macrocall, + Expr(:(.), Base, Expr(:quote, Symbol("@m"))), + LineNumberNode(0, :none), :a, :b)) == + ":(#= none:0 =# (Base).@m a b)" +@test sprint(show, Expr(:macrocall, + Expr(:(.), :Base, QuoteNode(Symbol("@m"))), + LineNumberNode(0, :none), :a, :b)) == + ":(#= none:0 =# Base.@m a b)" +@test sprint(show, Expr(:macrocall, + Expr(:(.), Base, QuoteNode(Symbol("@m"))), + LineNumberNode(0, :none), :a, :b)) == + ":(#= none:0 =# (Base).@m a b)" + +# Printing of special macro syntaxes +# `a b c` +@test sprint(show, Expr(:macrocall, + GlobalRef(Core, Symbol("@cmd")), + LineNumberNode(0, :none), "a b c")) == ":(`a b c`)" +@test sprint(show, Expr(:macrocall, + Expr(:(.), :Core, Expr(:quote, Symbol("@cmd"))), + LineNumberNode(0, :none), "a b c")) == ":(#= none:0 =# Core.@cmd \"a b c\")" +@test sprint(show, Expr(:macrocall, + Expr(:(.), Core, Expr(:quote, Symbol("@cmd"))), + LineNumberNode(0, :none), "a b c")) == ":(#= none:0 =# (Core).@cmd \"a b c\")" +@test sprint(show, Expr(:macrocall, + Expr(:(.), :Core, QuoteNode(Symbol("@cmd"))), + LineNumberNode(0, :none), "a b c")) == ":(#= none:0 =# Core.@cmd \"a b c\")" +@test sprint(show, Expr(:macrocall, + Expr(:(.), Core, QuoteNode(Symbol("@cmd"))), + LineNumberNode(0, :none), "a b c")) == ":(#= none:0 =# (Core).@cmd \"a b c\")" +@test_repr "`a b c`" +@test sprint(show, Meta.parse("`a b c`")) == ":(`a b c`)" +# a"b" and a"b"c +@test_repr "a\"b\"" +@test_repr "a\"b\"c" +@test_repr "aa\"b\"" +@test_repr "a\"b\"cc" +@test sprint(show, Meta.parse("a\"b\"")) == ":(a\"b\")" +@test sprint(show, Meta.parse("a\"b\"c")) == ":(a\"b\"c)" +@test sprint(show, Meta.parse("aa\"b\"")) == ":(aa\"b\")" +@test sprint(show, Meta.parse("a\"b\"cc")) == ":(a\"b\"cc)" +# 11111111111111111111, 0xfffffffffffffffff, 1111...many digits... +@test sprint(show, Meta.parse("11111111111111111111")) == ":(11111111111111111111)" +# @test_repr "Base.@int128_str \"11111111111111111111\"" +@test sprint(show, Meta.parse("Base.@int128_str \"11111111111111111111\"")) == + ":(#= none:1 =# Base.@int128_str \"11111111111111111111\")" +@test sprint(show, Meta.parse("11111111111111111111")) == ":(11111111111111111111)" +@test sprint(show, Meta.parse("0xfffffffffffffffff")) == ":(0xfffffffffffffffff)" +@test sprint(show, Meta.parse("11111111111111111111111111111111111111111111111111111111111111")) == +":(11111111111111111111111111111111111111111111111111111111111111)" # Issue #15765 printing of continue and break @test sprint(show, :(continue)) == ":(continue)" @@ -907,6 +1218,7 @@ let a = Vector{Any}(undef, 10000) repr = sprint(dump, a; context=(:limit => true), sizehint=0) @test repr == "Array{Any}((10000,))\n 1: #undef\n 2: String \"elemA\"\n 3: #undef\n 4: String \"elemB\"\n 5: #undef\n ...\n 9996: #undef\n 9997: #undef\n 9998: #undef\n 9999: #undef\n 10000: #undef\n" end +@test occursin("NamedTuple", sprint(dump, NamedTuple)) # issue #17338 @test repr(Core.svec(1, 2)) == "svec(1, 2)" @@ -942,7 +1254,7 @@ test_repr("(+).:-") test_repr("(!).:~") test_repr("a.:(begin #= none:3 =# - end)") + end)", true) test_repr("a.:(=)") test_repr("a.:(:)") test_repr("(:).a") @@ -965,7 +1277,8 @@ test_repr("(:).a") @test isa(repr("text/plain", String(UInt8[0x00:0xff;])), String) # don't use julia-specific `f` in Float32 printing (PR #18053) -@test sprint(print, 1f-7) == "1.0f-7" +@test sprint(print, 1f-7) == "1.0e-7" +@test string(1f-7) == "1.0e-7" let d = TextDisplay(IOBuffer()) @test_throws MethodError display(d, "text/foobar", [3 1 4]) @@ -1275,7 +1588,7 @@ end @test replstr(view(A, [1], :)) == "1×1 view(::Array{Float64,2}, [1], :) with eltype Float64:\n 0.0" # issue #27680 - @test replstr(Set([(1.0,1.0), (2.0,2.0), (3.0, 3.0)])) == (sizeof(Int) == 8 ? + @test showstr(Set([(1.0,1.0), (2.0,2.0), (3.0, 3.0)])) == (sizeof(Int) == 8 ? "Set([(3.0, 3.0), (2.0, 2.0), (1.0, 1.0)])" : "Set([(1.0, 1.0), (2.0, 2.0), (3.0, 3.0)])") @@ -1586,6 +1899,21 @@ end @test sdastr(3, 3) == "[3, 4, 5]" end +@testset "show Set" begin + s = Set{Int}(1:22) + str = showstr(s) + @test startswith(str, "Set([") + @test endswith(str, "])") + @test occursin(" … ", str) + + str = replstr(s) + @test startswith(str, "Set{$Int} with 22 elements:\n") + @test endswith(str, "\n ⋮ ") + @test count(==('\n'), str) == 20 + + @test replstr(Set(['a'^100])) == "Set{String} with 1 element:\n \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa…" +end + @testset "0-dimensional Array. Issue #31481" begin for x in (zeros(Int32), collect('b'), fill(nothing), BitArray(0)) @test eval(Meta.parse(repr(x))) == x @@ -1612,3 +1940,9 @@ end @test startswith(showstr(Array{Int32, 0}(undef)), "fill(") @test startswith(replstr(Array{Int32, 0}(undef)), "0-dimensional Array{Int32,0}:\n") end + +# issue #31402, Print Symbol("true") as Symbol("true") instead of :true +@test sprint(show, Symbol(true)) == "Symbol(\"true\")" +@test sprint(show, Symbol("true")) == "Symbol(\"true\")" +@test sprint(show, Symbol(false)) == "Symbol(\"false\")" +@test sprint(show, Symbol("false")) == "Symbol(\"false\")" diff --git a/test/sorting.jl b/test/sorting.jl index e882047c10e09..1e461b2aaf2e6 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -2,39 +2,62 @@ module SortingTests -using Base.Order: Forward +using Base.Order using Random using Test -@test sort([2,3,1]) == [1,2,3] -@test sort([2,3,1], rev=true) == [3,2,1] -@test sort(['z':-1:'a';]) == ['a':'z';] -@test sort(['a':'z';], rev=true) == ['z':-1:'a';] -@test sortperm([2,3,1]) == [3,1,2] -@test sortperm!([1,2,3], [2,3,1]) == [3,1,2] -let s = view([1,2,3,4], 1:3), - r = sortperm!(s, [2,3,1]) - @test r == [3,1,2] - @test r === s +@testset "Order" begin + @test Forward == ForwardOrdering() + @test ReverseOrdering(Forward) == ReverseOrdering() == Reverse end -@test_throws ArgumentError sortperm!(view([1,2,3,4], 1:4), [2,3,1]) -@test !issorted([2,3,1]) -@test issorted([1,2,3]) -@test reverse([2,3,1]) == [1,3,2] -@test partialsort([3,6,30,1,9],3) == 6 -@test partialsort([3,6,30,1,9],3:4) == [6,9] -@test partialsortperm([3,6,30,1,9], 3:4) == [2,5] -@test partialsortperm!(Vector(1:5), [3,6,30,1,9], 3:4) == [2,5] -let a=[1:10;] - for r in Any[2:4, 1:2, 10:10, 4:2, 2:1, 4:-1:2, 2:-1:1, 10:-1:10, 4:1:3, 1:2:8, 10:-3:1] - @test partialsort(a, r) == [r;] - @test partialsortperm(a, r) == [r;] - @test partialsort(a, r, rev=true) == (11 .- [r;]) - @test partialsortperm(a, r, rev=true) == (11 .- [r;]) + + +@testset "sort" begin + @test sort([2,3,1]) == [1,2,3] == sort([2,3,1]; order=Forward) + @test sort([2,3,1], rev=true) == [3,2,1] == sort([2,3,1], order=Reverse) + @test sort(['z':-1:'a';]) == ['a':'z';] + @test sort(['a':'z';], rev=true) == ['z':-1:'a';] +end + +@testset "sortperm" begin + @test sortperm([2,3,1]) == [3,1,2] + @test sortperm!([1,2,3], [2,3,1]) == [3,1,2] + let s = view([1,2,3,4], 1:3), + r = sortperm!(s, [2,3,1]) + @test r == [3,1,2] + @test r === s + end + @test_throws ArgumentError sortperm!(view([1,2,3,4], 1:4), [2,3,1]) +end + +@testset "misc sorting" begin + @test !issorted([2,3,1]) + @test issorted([1,2,3]) + @test reverse([2,3,1]) == [1,3,2] + @test sum(randperm(6)) == 21 +end + +@testset "partialsort" begin + @test partialsort([3,6,30,1,9],3) == 6 + @test partialsort([3,6,30,1,9],3:4) == [6,9] + @test partialsortperm([3,6,30,1,9], 3:4) == [2,5] + @test partialsortperm!(Vector(1:5), [3,6,30,1,9], 3:4) == [2,5] + let a=[1:10;] + for r in Any[2:4, 1:2, 10:10, 4:2, 2:1, 4:-1:2, 2:-1:1, 10:-1:10, 4:1:3, 1:2:8, 10:-3:1, UInt(2):UInt(5)] + @test partialsort(a, r) == [r;] + @test partialsortperm(a, r) == [r;] + @test partialsort(a, r, rev=true) == (11 .- [r;]) + @test partialsortperm(a, r, rev=true) == (11 .- [r;]) + end + for i in (2, UInt(2), Int128(1), big(10)) + @test partialsort(a, i) == i + @test partialsortperm(a, i) == i + @test partialsort(a, i, rev=true) == (11 - i) + @test partialsortperm(a, i, rev=true) == (11 - i) + end end + @test_throws ArgumentError partialsortperm!([1,2], [2,3,1], 1:2) end -@test_throws ArgumentError partialsortperm!([1,2], [2,3,1], 1:2) -@test sum(randperm(6)) == 21 @testset "searchsorted" begin numTypes = [ Int8, Int16, Int32, Int64, Int128, @@ -399,4 +422,32 @@ end end end +@testset "sorting of views with strange axes" for T in (Int, UInt, Int128, UInt128, BigInt) + a = [8,6,7,5,3,0,9] + b = @view a[T(2):T(5)] + @test issorted(sort!(b)) + @test b == [3,5,6,7] + @test a == [8,3,5,6,7,0,9] + + a = [8,6,7,5,3,0,9] + b = @view a[T(2):T(5)] + c = sort(b) + @test issorted(c) + @test c == [3,5,6,7] + @test a == [8,6,7,5,3,0,9] + + a = [8,6,7,NaN,5,3,0,9] + b = @view a[T(2):T(5)] + @test issorted(sort!(b)) + @test isequal(b, [5,6,7,NaN]) + @test isequal(a, [8,5,6,7,NaN,3,0,9]) + + a = [8,6,7,NaN,5,3,0,9] + b = @view a[T(2):T(5)] + c = sort(b) + @test issorted(c) + @test isequal(c, [5,6,7,NaN]) + @test isequal(a, [8,6,7,NaN,5,3,0,9]) +end + end diff --git a/test/spawn.jl b/test/spawn.jl index d62179724097f..4e6eca4515c97 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -20,7 +20,7 @@ sleepcmd = `sleep` lscmd = `ls` havebb = false if Sys.iswindows() - busybox = download("https://frippery.org/files/busybox/busybox.exe", joinpath(tempdir(), "busybox.exe")) + busybox = download("https://cache.julialang.org/https://frippery.org/files/busybox/busybox.exe", joinpath(tempdir(), "busybox.exe")) havebb = try # use busybox-w32 on windows, if available success(`$busybox`) true @@ -675,3 +675,88 @@ end if Sys.iswindows() rm(busybox, force=true) end + + +# shell escaping on Windows +@testset "shell_escape_winsomely" begin + # Note argument A can be parsed both as A or "A". + # We do not test that the parsing satisfies either of these conditions. + # In other words, tests may fail even for valid parsing. + # This is done to avoid overly verbose tests. + + # input : + # output: "" + @test Base.shell_escape_winsomely("") == "\"\"" + + @test Base.shell_escape_winsomely("A") == "A" + + @test Base.shell_escape_winsomely(`A`) == "A" + + # input : hello world + # output: "hello world" + @test Base.shell_escape_winsomely("hello world") == "\"hello world\"" + + # input : hello world + # output: "hello world" + @test Base.shell_escape_winsomely("hello\tworld") == "\"hello\tworld\"" + + # input : hello"world + # output: "hello\"world" (also valid) hello\"world + @test Base.shell_escape_winsomely("hello\"world") == "\"hello\\\"world\"" + + # input : hello""world + # output: "hello\"\"world" (also valid) hello\"\"world + @test Base.shell_escape_winsomely("hello\"\"world") == "\"hello\\\"\\\"world\"" + + # input : hello\world + # output: hello\world + @test Base.shell_escape_winsomely("hello\\world") == "hello\\world" + + # input : hello\\world + # output: hello\\world + @test Base.shell_escape_winsomely("hello\\\\world") == "hello\\\\world" + + # input : hello\"world + # output: "hello\"world" (also valid) hello\"world + @test Base.shell_escape_winsomely("hello\\\"world") == "\"hello\\\\\\\"world\"" + + # input : hello\\"world + # output: "hello\\\\\"world" (also valid) hello\\\\\"world + @test Base.shell_escape_winsomely("hello\\\\\"world") == "\"hello\\\\\\\\\\\"world\"" + + # input : hello world\ + # output: "hello world\\" + @test Base.shell_escape_winsomely("hello world\\") == "\"hello world\\\\\"" + + # input : A\B + # output: A\B" + @test Base.shell_escape_winsomely("A\\B") == "A\\B" + + # input : [A\, B] + # output: "A\ B" + @test Base.shell_escape_winsomely("A\\", "B") == "A\\ B" + + # input : A"B + # output: "A\"B" + @test Base.shell_escape_winsomely("A\"B") == "\"A\\\"B\"" + + # input : [A B\, C] + # output: "A B\\" C + @test Base.shell_escape_winsomely("A B\\", "C") == "\"A B\\\\\" C" + + # input : [A "B, C] + # output: "A \"B" C + @test Base.shell_escape_winsomely("A \"B", "C") == "\"A \\\"B\" C" + + # input : [A B\, C] + # output: "A B\\" C + @test Base.shell_escape_winsomely("A B\\", "C") == "\"A B\\\\\" C" + + # input :[A\ B\, C] + # output: "A\ B\\" C + @test Base.shell_escape_winsomely("A\\ B\\", "C") == "\"A\\ B\\\\\" C" + + # input : [A\ B\, C, D K] + # output: "A\ B\\" C "D K" + @test Base.shell_escape_winsomely("A\\ B\\", "C", "D K") == "\"A\\ B\\\\\" C \"D K\"" +end diff --git a/test/subtype.jl b/test/subtype.jl index cd4b2040fcc5a..7077b36353528 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -1682,3 +1682,8 @@ c32703(::Type{<:Str{C}}, str::Str{C}) where {C<:CSE} = str Tuple{Type{Tuple{Vararg{V, N} where N}}, Tuple{Vararg{V, N} where N}} where V) @test issub(Tuple{Type{Any}, NTuple{4,Union{Int,Nothing}}}, Tuple{Type{V}, Tuple{Vararg{V, N} where N}} where V) + +# issue #26065 +t26065 = Ref{Tuple{T,Ref{Union{Ref{Tuple{Ref{Union{Ref{Ref{Tuple{Ref{Tuple{Union{Tuple{Ref{Ref{T}},T}, T},T}},T}}}, T}},T}}, Ref{T}, T}}}} where T +s26065 = Ref{Tuple{T,Ref{Union{Ref{Tuple{Ref{Union{Ref{Ref{Tuple{Ref{Tuple{Union{Tuple{Ref{Ref{T}},T}, T},T}},T}}}, T}},T}}, Ref{T}, T}}}} where T +@test t26065 <: s26065 diff --git a/test/syntax.jl b/test/syntax.jl index 3b4a45d3ff26f..f649cccc7548b 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -1327,9 +1327,18 @@ end @test Meta.parse("-(x;;;)^2") == Expr(:call, :-, Expr(:call, :^, Expr(:block, :x), 2)) @test Meta.parse("+((1,2))") == Expr(:call, :+, Expr(:tuple, 1, 2)) -@test_throws ParseError("space before \"(\" not allowed in \"+ (\"") Meta.parse("1 -+ (a=1, b=2)") +@test_throws ParseError("space before \"(\" not allowed in \"+ (\" at none:1") Meta.parse("1 -+ (a=1, b=2)") # issue #29781 -@test_throws ParseError("space before \"(\" not allowed in \"sin. (\"") Meta.parse("sin. (1)") +@test_throws ParseError("space before \"(\" not allowed in \"sin. (\" at none:1") Meta.parse("sin. (1)") +# Parser errors for disallowed space contain line numbers +@test_throws ParseError("space before \"[\" not allowed in \"f() [\" at none:2") Meta.parse("\nf() [i]") +@test_throws ParseError("space before \"(\" not allowed in \"f() (\" at none:2") Meta.parse("\nf() (i)") +@test_throws ParseError("space before \".\" not allowed in \"f() .\" at none:2") Meta.parse("\nf() .i") +@test_throws ParseError("space before \"{\" not allowed in \"f() {\" at none:2") Meta.parse("\nf() {i}") +@test_throws ParseError("space before \"m\" not allowed in \"@ m\" at none:2") Meta.parse("\n@ m") +@test_throws ParseError("space before \".\" not allowed in \"a .\" at none:2") Meta.parse("\nusing a .b") +@test_throws ParseError("space before \".\" not allowed in \"a .\" at none:2") Meta.parse("\nusing a .b") +@test_throws ParseError("space before \"(\" not allowed in \"+ (\" at none:2") Meta.parse("\n+ (x, y)") @test Meta.parse("1 -+(a=1, b=2)") == Expr(:call, :-, 1, Expr(:call, :+, Expr(:kw, :a, 1), Expr(:kw, :b, 2))) @@ -1845,8 +1854,7 @@ end @testset "closure conversion in testsets" begin p = (2, 3, 4) @test p == (2, 3, 4) - identity(p) - allocs = @allocated identity(p) + allocs = (() -> @allocated identity(p))() @test allocs == 0 end @@ -1949,3 +1957,15 @@ end @test Meta.lower(Main, :((a=a,b=b...))) == Expr(:error, "\"...\" expression cannot be used as named tuple field value") @test Meta.lower(Main, :(f(;a...,b...)=0)) == Expr(:error, "invalid \"...\" on non-final keyword argument") @test Meta.lower(Main, :(f(;a...,b=0)=0)) == Expr(:error, "invalid \"...\" on non-final keyword argument") + +# issue #31547 +@test Meta.lower(Main, :(a := 1)) == Expr(:error, "unsupported assignment operator \":=\"") + +# issue #33841 +let a(; b) = b + @test a(b=3) == 3 +end + +# issue #33987 +f33987(args::(Vararg{Any, N} where N); kwargs...) = args +@test f33987(1,2,3) === (1,2,3) diff --git a/test/tuple.jl b/test/tuple.jl index 26d5d5f58d953..0b017b18f5752 100644 --- a/test/tuple.jl +++ b/test/tuple.jl @@ -89,6 +89,13 @@ end # issue #28915 @test convert(Union{Tuple{}, Tuple{Int}}, (1,)) === (1,) + + @testset "one-element containers" begin + r = Ref(3) + @test (3,) === @inferred Tuple(r) + z = Array{Float64,0}(undef); z[] = 3.0 + @test (3.0,) === @inferred Tuple(z) + end end @testset "size" begin