diff --git a/Makefile b/Makefile index e1d1670f0fc18..20c659802d360 100644 --- a/Makefile +++ b/Makefile @@ -434,10 +434,10 @@ ifeq ($(OS), WINNT) cd $(BUILDROOT)/julia-$(JULIA_COMMIT) && find * | sed -e 's/\//\\/g' -e 's/$$/\r/g' > etc/uninstall.log # build nsis package - cd $(BUILDROOT) && $(call spawn,$(JULIAHOME)/dist-extras/nsis/makensis.exe) -NOCD -DVersion=$(JULIA_VERSION) -DArch=$(ARCH) -DCommit=$(JULIA_COMMIT) -DMUI_ICON="$(call cygpath_w,$(JULIAHOME)/contrib/windows/julia.ico)" $(call cygpath_w,$(JULIAHOME)/contrib/windows/build-installer.nsi) | iconv -f latin1 + cd $(BUILDROOT) && $(call spawn,$(JULIAHOME)/dist-extras/nsis/makensis.exe) -NOCD -DVersion=$(JULIA_VERSION) -DArch=$(ARCH) -DCommit=$(JULIA_COMMIT) -DJULIAHOME="$(call cygpath_w,$(JULIAHOME))" $(call cygpath_w,$(JULIAHOME)/contrib/windows/build-installer.nsi) | iconv -f latin1 # compress nsis installer and combine with 7zip self-extracting header - cd $(BUILDROOT) && $(JULIAHOME)/dist-extras/7z a -mx9 "julia-install-$(JULIA_COMMIT)-$(ARCH).7z" julia-installer.exe + cd $(BUILDROOT) && $(JULIAHOME)/dist-extras/7z a -mx=9 "julia-install-$(JULIA_COMMIT)-$(ARCH).7z" julia-installer.exe cd $(BUILDROOT) && cat $(JULIAHOME)/contrib/windows/7zS.sfx $(JULIAHOME)/contrib/windows/7zSFX-config.txt "julia-install-$(JULIA_COMMIT)-$(ARCH).7z" > "$(JULIA_BINARYDIST_FILENAME).exe" chmod a+x "$(BUILDROOT)/$(JULIA_BINARYDIST_FILENAME).exe" -rm -f $(BUILDROOT)/julia-install-$(JULIA_COMMIT)-$(ARCH).7z @@ -580,11 +580,11 @@ else $(error no win-extras target for ARCH=$(ARCH)) endif cd $(JULIAHOME)/dist-extras && \ - $(JLDOWNLOAD) http://downloads.sourceforge.net/sevenzip/7z1805-extra.7z && \ - $(JLDOWNLOAD) https://storage.googleapis.com/google-code-archive-downloads/v2/code.google.com/unsis/nsis-2.46.5-Unicode-setup.exe && \ + $(JLDOWNLOAD) https://sourceforge.net/projects/nsis/files/NSIS%203/3.04/nsis-3.04-setup.exe && \ + $(JLCHECKSUM) nsis-3.04-setup.exe && \ chmod a+x 7z.exe && \ chmod a+x 7z.dll && \ - $(call spawn,./7z.exe) x -y -onsis nsis-2.46.5-Unicode-setup.exe && \ + $(call spawn,./7z.exe) x -y -onsis nsis-3.04-setup.exe && \ chmod a+x ./nsis/makensis.exe # various statistics about the build that may interest the user diff --git a/NEWS.md b/NEWS.md index 1800a956a3b7b..44ce2dc8378ee 100644 --- a/NEWS.md +++ b/NEWS.md @@ -80,6 +80,7 @@ Standard library changes * Sparse vector outer products are more performant and maintain sparsity in products of the form `kron(u, v')`, `u * v'`, and `u .* v'` where `u` and `v` are sparse vectors or column views ([#24980]). +* The `sprand` function is now 2 to 5 times faster ([#30494]). As a consequence of this change, the random stream of matrices produced with `sprand` and `sprandn` has changed. #### Sockets @@ -116,6 +117,7 @@ External dependencies [#30323]: https://github.com/JuliaLang/julia/issues/30323 [#30372]: https://github.com/JuliaLang/julia/issues/30372 [#30382]: https://github.com/JuliaLang/julia/issues/30382 +[#30494]: https://github.com/JuliaLang/julia/issues/30494 [#30577]: https://github.com/JuliaLang/julia/issues/30577 [#30583]: https://github.com/JuliaLang/julia/issues/30583 [#30584]: https://github.com/JuliaLang/julia/issues/30584 @@ -141,3 +143,4 @@ External dependencies [#31532]: https://github.com/JuliaLang/julia/issues/31532 [#31561]: https://github.com/JuliaLang/julia/issues/31561 [#31604]: https://github.com/JuliaLang/julia/issues/31604 +[#32260]: https://github.com/JuliaLang/julia/issues/32260 diff --git a/base/compiler/ssair/ir.jl b/base/compiler/ssair/ir.jl index dc811b3c79744..0aec26d444476 100644 --- a/base/compiler/ssair/ir.jl +++ b/base/compiler/ssair/ir.jl @@ -499,24 +499,28 @@ mutable struct IncrementalCompact cur_bb = 1 for i = 1:length(bb_rename) if i != 1 && length(blocks[i].preds) == 0 - bb_rename[i] = 0 + bb_rename[i] = -1 else bb_rename[i] = cur_bb cur_bb += 1 end end for i = 1:length(bb_rename) - bb_rename[i] == 0 && continue + bb_rename[i] == -1 && continue preds, succs = blocks[i].preds, blocks[i].succs # Rename preds - for j = 1:length(preds); preds[j] = bb_rename[preds[j]]; end + for j = 1:length(preds) + if preds[j] != 0 + preds[j] = bb_rename[preds[j]] + end + end # Dead blocks get removed from the predecessor list - filter!(x->x !== 0, preds) + filter!(x->x !== -1, preds) # Rename succs for j = 1:length(succs); succs[j] = bb_rename[succs[j]]; end end let blocks=blocks - result_bbs = BasicBlock[blocks[i] for i = 1:length(blocks) if bb_rename[i] != 0] + result_bbs = BasicBlock[blocks[i] for i = 1:length(blocks) if bb_rename[i] != -1] end else bb_rename = Vector{Int}() diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index d1dfad473726b..d3519ff487e2e 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -178,7 +178,7 @@ function walk_to_defs(compact::IncrementalCompact, @nospecialize(defssa), @nospe found_def = false ## Track which PhiNodes, SSAValue intermediaries ## we forwarded through. - visited = IdSet{Any}() + visited = IdDict{Any, Any}() worklist_defs = Any[] worklist_constraints = Any[] leaves = Any[] @@ -187,7 +187,7 @@ function walk_to_defs(compact::IncrementalCompact, @nospecialize(defssa), @nospe while !isempty(worklist_defs) defssa = pop!(worklist_defs) typeconstraint = pop!(worklist_constraints) - push!(visited, defssa) + visited[defssa] = typeconstraint def = compact[defssa] if isa(def, PhiNode) push!(visited_phinodes, defssa) @@ -211,9 +211,15 @@ function walk_to_defs(compact::IncrementalCompact, @nospecialize(defssa), @nospe if isa(val, AnySSAValue) new_def, new_constraint = simple_walk_constraint(compact, val, typeconstraint) if isa(new_def, AnySSAValue) - if !(new_def in visited) + if !haskey(visited, new_def) push!(worklist_defs, new_def) push!(worklist_constraints, new_constraint) + elseif !(new_constraint <: visited[new_def]) + # We have reached the same definition via a different + # path, with a different type constraint. We may have + # to redo some work here with the wider typeconstraint + push!(worklist_defs, new_def) + push!(worklist_constraints, tmerge(new_constraint, visited[new_def])) end continue end diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index 82c95dd9d0614..48f38ff7980aa 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -92,10 +92,13 @@ function _limit_type_size(@nospecialize(t), @nospecialize(c), sources::SimpleVec return t # easy case elseif isa(t, DataType) && isempty(t.parameters) return t # fast path: unparameterized are always simple - elseif isa(unwrap_unionall(t), DataType) && isa(c, Type) && c !== Union{} && c <: t - return t # t is already wider than the comparison in the type lattice - elseif is_derived_type_from_any(unwrap_unionall(t), sources, depth) - return t # t isn't something new + else + ut = unwrap_unionall(t) + if isa(ut, DataType) && ut.name !== _va_typename && isa(c, Type) && c !== Union{} && c <: t + return t # t is already wider than the comparison in the type lattice + elseif is_derived_type_from_any(ut, sources, depth) + return t # t isn't something new + end end # peel off (and ignore) wrappers - they contribute no useful information, so we don't need to consider their size # first attempt to turn `c` into a type that contributes meaningful information diff --git a/contrib/julia.svg b/contrib/julia.svg index b0b03de8a1f09..ecdfe4c584bf9 100644 --- a/contrib/julia.svg +++ b/contrib/julia.svg @@ -1,62 +1 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Asset 2 \ No newline at end of file diff --git a/contrib/mac/app/julia.icns b/contrib/mac/app/julia.icns index 16358e19db453..7660e6650937b 100644 Binary files a/contrib/mac/app/julia.icns and b/contrib/mac/app/julia.icns differ diff --git a/contrib/mac/frameworkapp/installresources/logo_hires.png b/contrib/mac/frameworkapp/installresources/logo_hires.png new file mode 100644 index 0000000000000..d37c7ccdfd3f0 Binary files /dev/null and b/contrib/mac/frameworkapp/installresources/logo_hires.png differ diff --git a/contrib/windows/7zS.sfx b/contrib/windows/7zS.sfx index 02a964597b1ee..564c622257c8f 100755 Binary files a/contrib/windows/7zS.sfx and b/contrib/windows/7zS.sfx differ diff --git a/contrib/windows/7zSFX-config.txt b/contrib/windows/7zSFX-config.txt index f60ae3d93b27e..259e571c4acfc 100644 --- a/contrib/windows/7zSFX-config.txt +++ b/contrib/windows/7zSFX-config.txt @@ -1,4 +1,4 @@ ;!@Install@!UTF-8! -Title="The Julia Language" +Title="Julia" RunProgram="julia-installer.exe" ;!@InstallEnd@! diff --git a/contrib/windows/build-installer.nsi b/contrib/windows/build-installer.nsi index 8896b0a0b1552..f4ad3443d8500 100644 --- a/contrib/windows/build-installer.nsi +++ b/contrib/windows/build-installer.nsi @@ -1,15 +1,38 @@ +Unicode true + !include "MUI2.nsh" !include "nsDialogs.nsh" !include "winmessages.nsh" -Name "The Julia Language" OutFile "julia-installer.exe" SetCompress off -CRCCheck on +CRCCheck off SetDataBlockOptimize on -ShowInstDetails show +ShowInstDetails nevershow +ShowUninstDetails nevershow RequestExecutionLevel user -BrandingText "Julia ${Version}" +BrandingText " " + +!define /date YEAR "%Y" + +Name "Julia" +VIProductVersion "10.20.0.0" # arbitrary value since it doesn't mater, but is required; format must be X.X.X.X +VIAddVersionKey "ProductName" "Julia" +VIAddVersionKey "CompanyName " "Julia Language" +VIAddVersionKey "ProductVersion" "${Version}" +VIAddVersionKey "FileDescription" "Julia Language Installer" +VIAddVersionKey "Comments" "https://julialang.org/" +VIAddVersionKey "LegalCopyright" "Copyright (c) 2009-${YEAR} Julia Language" +VIAddVersionKey "FileVersion" "" + +Caption "Julia Installer" # title bar + +!define MUI_ICON "${JULIAHOME}\contrib\windows\julia.ico" +!define MUI_UNICON "${JULIAHOME}\contrib\windows\julia.ico" +!define MUI_WELCOMEFINISHPAGE_BITMAP "${JULIAHOME}\contrib\windows\julia-banner.bmp" +!define MUI_HEADERIMAGE +!define MUI_HEADERIMAGE_BITMAP "${JULIAHOME}\contrib\windows\julia-header.bmp" +!define MUI_HEADERIMAGE_RIGHT # Uninstall settings !define UninstLog "uninstall.log" @@ -29,7 +52,7 @@ FunctionEnd Function createDesktopLink ${NSD_GetState} $Checkbox $0 ${If} $0 <> 0 - CreateShortCut "$DESKTOP\julia.lnk" "$INSTDIR\bin\julia.exe" + CreateShortCut "$DESKTOP\julia.lnk" "$INSTDIR\bin\julia.exe" "" "$INSTDIR\bin\julia.exe" 0 ${EndIf} FunctionEnd @@ -38,25 +61,43 @@ InstallDir "$LOCALAPPDATA\Julia-${Version}" !define JuliaStartMenuFolder "Julia ${Version}" # Page settings -# Note that we repurpose the checkboxes on the FinishPage -# in order to keep it simple. -!define MUI_DIRECTORYPAGE_TEXT_TOP "Julia may be installed in any accessible directory, including a home folder or portable device. Please run as Administrator to install for system-wide use." +# Note that we repurpose the checkboxes on the FinishPage in order to keep it simple. +!define MUI_DIRECTORYPAGE_TEXT_TOP "Julia may be installed in any accessible directory.$\r$\n$\r$\nPlease run installer as Administrator to install Julia system-wide." !define MUI_FINISHPAGE_SHOWREADME !define MUI_FINISHPAGE_SHOWREADME_TEXT "Create Start Menu folder and shortcut" !define MUI_FINISHPAGE_SHOWREADME_FUNCTION AddToStartMenu + +!define MUI_WELCOMEPAGE_TITLE "Welcome to Julia ${Version}" +!define MUI_WELCOMEPAGE_TEXT "Setup will guide you through installation.$\r$\n$\r$\nClick Next to continue." +!define MUI_FINISHPAGE_TITLE "Julia installation complete" +!define MUI_FINISHPAGE_TEXT "Julia has been successfully installed.$\r$\n$\r$\nClick Finish to close the installer." + !define MUI_FINISHPAGE_RUN -!define MUI_FINISHPAGE_RUN_TEXT "Open Julia install folder" +!define MUI_FINISHPAGE_RUN_TEXT "Open the Julia install folder" !define MUI_FINISHPAGE_RUN_FUNCTION ShowInstallFolder -# Pages to show +!define MUI_UNCONFIRMPAGE_TEXT_TOP "Julia will be uninstalled from the following folder." +!define MUI_UNCONFIRMPAGE_TEXT_LOCATION "Uninstalling from" +# Pages to show +!define MUI_PAGE_HEADER_TEXT "Choose Installation Directory" +!define MUI_PAGE_HEADER_SUBTEXT "" +!insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES +Section +!insertmacro MUI_HEADER_TEXT "Installing" "" +SectionEnd !define MUI_PAGE_CUSTOMFUNCTION_SHOW desktopCheckbox !define MUI_PAGE_CUSTOMFUNCTION_LEAVE createDesktopLink !insertmacro MUI_PAGE_FINISH +!define MUI_PAGE_HEADER_TEXT "Uninstall Julia" +!define MUI_PAGE_HEADER_SUBTEXT "" +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + !insertmacro MUI_LANGUAGE "English" # Add/Remove Programs entry @@ -65,14 +106,14 @@ InstallDir "$LOCALAPPDATA\Julia-${Version}" Section "Dummy Section" SecDummy SetOutPath $INSTDIR File /a /r "julia-${Commit}\*" - WriteUninstaller "$INSTDIR\Uninstall.exe" - CreateShortcut "$INSTDIR\julia.lnk" "$INSTDIR\bin\julia.exe" + WriteUninstaller "$INSTDIR\uninstall.exe" + CreateShortcut "$INSTDIR\julia.lnk" "$INSTDIR\bin\julia.exe" "" "$INSTDIR\bin\julia.exe" 0 # ARP entries WriteRegStr HKCU "${ARP}" \ - "DisplayName" "Julia Language ${Version}" + "DisplayName" "Julia ${Version}" WriteRegStr HKCU "${ARP}" \ - "Publisher" "The Julia Project" + "Publisher" "Julia Language" WriteRegStr HKCU "${ARP}" \ "DisplayIcon" "$INSTDIR\bin\julia.exe" WriteRegStr HKCU "${ARP}" \ @@ -145,13 +186,11 @@ SectionEnd # Helper function to create Start Menu folder and shortcuts Function AddToStartMenu CreateDirectory "$SMPROGRAMS\${JuliaStartMenuFolder}" - CreateShortcut "$SMPROGRAMS\${JuliaStartMenuFolder}\julia-${Version}.lnk" "$INSTDIR\julia.lnk" "" "" "" "" "" "The Julia Language" - CreateShortcut "$SMPROGRAMS\${JuliaStartMenuFolder}\Uninstall-Julia-${Version}.lnk" "$instdir\Uninstall.exe" + CreateShortcut "$SMPROGRAMS\${JuliaStartMenuFolder}\julia-${Version}.lnk" "$INSTDIR\julia.lnk" "" "" "" "" "" "Julia" + CreateShortcut "$SMPROGRAMS\${JuliaStartMenuFolder}\Uninstall-Julia-${Version}.lnk" "$instdir\uninstall.exe" FunctionEnd # Opens the installation folder Function ShowInstallFolder ExecShell "open" $INSTDIR FunctionEnd - - diff --git a/contrib/windows/icon-readme.md b/contrib/windows/icon-readme.md new file mode 100755 index 0000000000000..08c446dc74259 --- /dev/null +++ b/contrib/windows/icon-readme.md @@ -0,0 +1,23 @@ + +Generate the logo +```julia +using Luxor +Drawing(325, 325, joinpath(pwd(), "julia-dots.svg")) +origin() +translate(0, 25) +juliacircles(100) +finish() +``` + +Create the ico file +```sh +#!/bin/bash + +for size in 16 20 24 32 40 48 64 128 256; do + rsvg-convert -w $size -h $size julia-dots.svg -o $size.png +done + +convert 256.png 128.png 64.png 48.png 40.png 32.png 24.png 20.png 16.png julia.ico + +rm 256.png 128.png 64.png 48.png 40.png 32.png 24.png 20.png 16.png +``` diff --git a/contrib/windows/julia-banner.bmp b/contrib/windows/julia-banner.bmp new file mode 100755 index 0000000000000..f1cf7ad689f69 Binary files /dev/null and b/contrib/windows/julia-banner.bmp differ diff --git a/contrib/windows/julia-header.bmp b/contrib/windows/julia-header.bmp new file mode 100755 index 0000000000000..dcf8cc63e9a11 Binary files /dev/null and b/contrib/windows/julia-header.bmp differ diff --git a/contrib/windows/julia.ico b/contrib/windows/julia.ico old mode 100644 new mode 100755 index fa78ebfadc1ff..5f340eacbd179 Binary files a/contrib/windows/julia.ico and b/contrib/windows/julia.ico differ diff --git a/contrib/windows/julia.rc b/contrib/windows/julia.rc index 3c76c59ab1f41..fb2b179401cd6 100644 --- a/contrib/windows/julia.rc +++ b/contrib/windows/julia.rc @@ -18,7 +18,7 @@ BEGIN VALUE "FileDescription", "Julia Programming Language" VALUE "FileVersion", JLVER_STR VALUE "InternalName", "julia" - VALUE "LegalCopyright", "MIT Licensed" + VALUE "LegalCopyright", "(c) 2009-2019 Julia Language" VALUE "OriginalFilename", "julia.exe" VALUE "ProductName", "Julia" VALUE "ProductVersion", JLVER_STR diff --git a/deps/checksums/nsis-3.04-setup.exe/md5 b/deps/checksums/nsis-3.04-setup.exe/md5 new file mode 100644 index 0000000000000..dd2ee5545ece6 --- /dev/null +++ b/deps/checksums/nsis-3.04-setup.exe/md5 @@ -0,0 +1 @@ +3c93427c56714478bb5a7a4bbaab934f diff --git a/deps/checksums/nsis-3.04-setup.exe/sha512 b/deps/checksums/nsis-3.04-setup.exe/sha512 new file mode 100644 index 0000000000000..4216a8ccb295e --- /dev/null +++ b/deps/checksums/nsis-3.04-setup.exe/sha512 @@ -0,0 +1 @@ +55142f5eeada65f18f6de950ead5342d78e1a4a01b7620d02dae2fbd3f0d963db482f5a63939131e81a78b458c5306f600578ab96891b0661cb0b74be1f7636d diff --git a/doc/src/assets/logo.png b/doc/src/assets/logo.png index b18f64c712821..0e4f86abdddd6 100644 Binary files a/doc/src/assets/logo.png and b/doc/src/assets/logo.png differ diff --git a/src/codegen.cpp b/src/codegen.cpp index a9e4177555457..41e0542e0e053 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -13,9 +13,6 @@ // also, use ELF because RuntimeDyld COFF X86_64 doesn't seem to work (fails to generate function pointers)? #define FORCE_ELF #endif -#if defined(_OS_WINDOWS_) || defined(_OS_FREEBSD_) -# define JL_DISABLE_FPO -#endif #if defined(_CPU_X86_) #define JL_NEED_FLOATTEMP_VAR 1 #endif @@ -4453,11 +4450,8 @@ static Function* gen_cfun_wrapper( Function *cw = Function::Create(functype, GlobalVariable::ExternalLinkage, funcName.str(), M); - jl_init_function(cw); cw->setAttributes(attributes); -#ifdef JL_DISABLE_FPO - cw->addFnAttr("no-frame-pointer-elim", "true"); -#endif + jl_init_function(cw); Function *cw_proto = into ? cw : function_proto(cw); // Save the Function object reference if (sf) { @@ -4772,11 +4766,8 @@ static Function* gen_cfun_wrapper( funcName << "_gfthunk"; Function *gf_thunk = Function::Create(returninfo.decl->getFunctionType(), GlobalVariable::InternalLinkage, funcName.str(), M); - jl_init_function(gf_thunk); gf_thunk->setAttributes(returninfo.decl->getAttributes()); -#ifdef JL_DISABLE_FPO - gf_thunk->addFnAttr("no-frame-pointer-elim", "true"); -#endif + jl_init_function(gf_thunk); // build a specsig -> jl_apply_generic converter thunk // this builds a method that calls jl_apply_generic (as a closure over a singleton function pointer), // but which has the signature of a specsig @@ -4852,9 +4843,6 @@ static Function* gen_cfun_wrapper( GlobalVariable::ExternalLinkage, funcName.str(), M); jl_init_function(cw_make); -#ifdef JL_DISABLE_FPO - cw_make->addFnAttr("no-frame-pointer-elim", "true"); -#endif BasicBlock *b0 = BasicBlock::Create(jl_LLVMContext, "top", cw_make); IRBuilder<> cwbuilder(b0); Function::arg_iterator AI = cw_make->arg_begin(); @@ -5156,9 +5144,6 @@ static Function *gen_invoke_wrapper(jl_method_instance_t *lam, jl_value_t *jlret add_return_attr(w, Attribute::NonNull); w->addFnAttr(Thunk); jl_init_function(w); -#ifdef JL_DISABLE_FPO - w->addFnAttr("no-frame-pointer-elim", "true"); -#endif Function::arg_iterator AI = w->arg_begin(); Value *methodArg = &*AI++; (void)methodArg; Value *argArray = &*AI++; @@ -5589,23 +5574,8 @@ static std::unique_ptr emit_function( } declarations->specFunctionObject = strdup(f->getName().str().c_str()); -#ifdef JL_DISABLE_FPO - f->addFnAttr("no-frame-pointer-elim", "true"); -#endif if (jlrettype == (jl_value_t*)jl_bottom_type) f->setDoesNotReturn(); -#if defined(_OS_WINDOWS_) && !defined(_CPU_X86_64_) - // tell Win32 to realign the stack to the next 16-byte boundary - // upon entry to any function. This achieves compatibility - // with both MinGW-GCC (which assumes an 16-byte-aligned stack) and - // i686 Windows (which uses a 4-byte-aligned stack) - AttrBuilder *attr = new AttrBuilder(); - attr->addStackAlignmentAttr(16); - f->addAttributes(AttributeList::FunctionIndex, *attr); -#endif -#if defined(_OS_WINDOWS_) && defined(_CPU_X86_64_) - f->setHasUWTable(); // force NeedsWinEH -#endif #ifdef USE_POLLY if (!jl_has_meta(stmts, polly_sym) || jl_options.polly == JL_OPTIONS_POLLY_OFF) { diff --git a/src/jitlayers.cpp b/src/jitlayers.cpp index e0aaa9b53bf29..3a9eda9b6191a 100644 --- a/src/jitlayers.cpp +++ b/src/jitlayers.cpp @@ -3,6 +3,10 @@ #include "llvm-version.h" #include "platform.h" #include "options.h" +#if defined(_OS_WINDOWS_) || defined(_OS_FREEBSD_) +# define JL_DISABLE_FPO +#endif + #include #include @@ -814,6 +818,23 @@ bool jl_can_finalize_function(StringRef F) // let the JIT know this function is a WIP void jl_init_function(Function *F) { + // set any attributes that *must* be set on all functions +#if defined(_OS_WINDOWS_) && !defined(_CPU_X86_64_) + // tell Win32 to realign the stack to the next 16-byte boundary + // upon entry to any function. This achieves compatibility + // with both MinGW-GCC (which assumes an 16-byte-aligned stack) and + // i686 Windows (which uses a 4-byte-aligned stack) + AttrBuilder attr; + attr.addStackAlignmentAttr(16); + F->addAttributes(AttributeList::FunctionIndex, attr); +#endif +#if defined(_OS_WINDOWS_) && defined(_CPU_X86_64_) + F->setHasUWTable(); // force NeedsWinEH +#endif +#ifdef JL_DISABLE_FPO + F->addFnAttr("no-frame-pointer-elim", "true"); +#endif + // record the WIP name incomplete_fname.insert(F->getName()); } diff --git a/src/jltypes.c b/src/jltypes.c index 780930e2354c0..c5c45662fce87 100644 --- a/src/jltypes.c +++ b/src/jltypes.c @@ -1101,10 +1101,19 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value continue; if (jl_is_datatype(pi)) continue; + if (jl_is_vararg_type(pi)) { + pi = jl_unwrap_vararg(pi); + if (jl_has_free_typevars(pi)) + continue; + } // normalize types equal to wrappers (prepare for wrapper_id) jl_value_t *tw = extract_wrapper(pi); if (tw && tw != pi && (tn != jl_type_typename || jl_typeof(pi) == jl_typeof(tw)) && jl_types_equal(pi, tw)) { + if (jl_is_vararg_type(iparams[i])) { + tw = jl_wrap_vararg(tw, jl_tparam1(jl_unwrap_unionall(iparams[i]))); + tw = jl_rewrap_unionall(tw, iparams[i]); + } iparams[i] = tw; if (p) jl_gc_wb(p, tw); } @@ -1157,6 +1166,7 @@ static jl_value_t *inst_datatype_inner(jl_datatype_t *dt, jl_svec_t *p, jl_value } int did_normalize = 0; jl_value_t *last2 = normalize_vararg(last); + assert(!jl_is_unionall(last2) || !jl_is_unionall(((jl_unionall_t*)last2)->body)); if (last2 != last) { last = last2; did_normalize = 1; diff --git a/src/julia-parser.scm b/src/julia-parser.scm index a8f35a54298b6..d8914f14525f1 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -216,10 +216,6 @@ `(with-bindings ((whitespace-newline #t)) ,@body)) -(define-macro (without-whitespace-newline . body) - `(with-bindings ((whitespace-newline #f)) - ,@body)) - ;; --- lexer --- (define (newline? c) (eqv? c #\newline)) @@ -1546,7 +1542,7 @@ (define (parse-do s) (with-bindings ((expect-end-current-line (input-port-line (ts:port s)))) - (without-whitespace-newline + (with-normal-context (let ((doargs (if (memv (peek-token s) '(#\newline #\;)) '() (parse-comma-separated s parse-range)))) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 1ae4454ce1a86..5550c13ec910d 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -2443,14 +2443,13 @@ (if scope (cond ((memq e (scope:args scope)) e) ((memq e (scope:globals scope)) `(outerref ,e)) - ((memq e (scope:sp scope)) e) (else (let ((r (assq e (scope:renames scope)))) - (if r - (cdr r) - (if (memq e (scope:locals scope)) - e - (lookup (scope:prev scope))))))) + (cond (r (cdr r)) + ((memq e (scope:locals scope)) e) + ((memq e (scope:sp scope)) e) + (else + (lookup (scope:prev scope))))))) (if (underscore-symbol? e) e `(outerref ,e))))) @@ -2525,11 +2524,12 @@ (if (memq v argnames) (error (string "local variable name \"" v "\" conflicts with an argument")))) local-decls)) - (if (eq? e (lam:body lam)) - (for-each (lambda (v) - (if (or (memq v locals-def) (memq v local-decls) (memq v implicit-locals)) - (error (string "local variable name \"" v "\" conflicts with a static parameter")))) - (scope:sp scope))) + (for-each (lambda (lst) + (for-each (lambda (v) + (if (eq? (var-kind v scope) 'static-parameter) + (error (string "local variable name \"" v "\" conflicts with a static parameter")))) + lst)) + (list local-decls implicit-locals)) (if lam (set-car! (cddr lam) (append (caddr lam) newnames newnames-def))) diff --git a/src/julia.h b/src/julia.h index 25b947d81cfec..3e5116b734dd7 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1274,6 +1274,14 @@ STATIC_INLINE jl_value_t *jl_unwrap_vararg(jl_value_t *v) JL_NOTSAFEPOINT return jl_tparam0(jl_unwrap_unionall(v)); } +STATIC_INLINE size_t jl_vararg_length(jl_value_t *v) JL_NOTSAFEPOINT +{ + assert(jl_is_vararg_type(v)); + jl_value_t *len = jl_tparam1(jl_unwrap_unionall(v)); + assert(jl_is_long(len)); + return jl_unbox_long(len); +} + STATIC_INLINE jl_vararg_kind_t jl_vararg_kind(jl_value_t *v) JL_NOTSAFEPOINT { if (!jl_is_vararg_type(v)) diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index 6d1403c2befb4..444a37e7b9ce0 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -902,26 +902,35 @@ JL_USED_FUNC static void dumpLivenessState(Function &F, State &S) { } } -// Check if this is a load from an immutable value. The easiest -// way to do so is to look at the tbaa and see if it derives from -// jtbaa_immut. -static bool isLoadFromImmut(LoadInst *LI) +static bool isTBAA(MDNode *TBAA, std::initializer_list const strset) { - if (LI->getMetadata(LLVMContext::MD_invariant_load)) - return true; - MDNode *TBAA = LI->getMetadata(LLVMContext::MD_tbaa); if (!TBAA) return false; while (TBAA->getNumOperands() > 1) { TBAA = cast(TBAA->getOperand(1).get()); auto str = cast(TBAA->getOperand(0))->getString(); - if (str == "jtbaa_immut" || str == "jtbaa_const") { - return true; + for (auto str2 : strset) { + if (str == str2) { + return true; + } } } return false; } +// Check if this is a load from an immutable value. The easiest +// way to do so is to look at the tbaa and see if it derives from +// jtbaa_immut. +static bool isLoadFromImmut(LoadInst *LI) +{ + if (LI->getMetadata(LLVMContext::MD_invariant_load)) + return true; + MDNode *TBAA = LI->getMetadata(LLVMContext::MD_tbaa); + if (isTBAA(TBAA, {"jtbaa_immut", "jtbaa_const"})) + return true; + return false; +} + // Check if this is a load from an constant global. static bool isLoadFromConstGV(LoadInst *LI) { @@ -930,14 +939,8 @@ static bool isLoadFromConstGV(LoadInst *LI) if (!isa(LI->getPointerOperand()->stripInBoundsOffsets())) return false; MDNode *TBAA = LI->getMetadata(LLVMContext::MD_tbaa); - if (!TBAA) - return false; - while (TBAA->getNumOperands() > 1) { - TBAA = cast(TBAA->getOperand(1).get()); - if (cast(TBAA->getOperand(0))->getString() == "jtbaa_const") { - return true; - } - } + if (isTBAA(TBAA, {"jtbaa_const"})) + return true; return false; } @@ -1720,6 +1723,40 @@ static inline void UpdatePtrNumbering(Value *From, Value *To, State *S) } } +#if JL_LLVM_VERSION < 80000 +MDNode *createMutableTBAAAccessTag(MDNode *Tag) { + MDNode *BaseType = cast(Tag->getOperand(0)); + MDNode *AccessType = cast(Tag->getOperand(1)); + Metadata *OffsetNode = Tag->getOperand(2); + uint64_t Offset = mdconst::extract(OffsetNode)->getZExtValue(); + + bool NewFormat = isa(AccessType->getOperand(0)); + + // See if the tag is already mutable. + unsigned ImmutabilityFlagOp = NewFormat ? 4 : 3; + if (Tag->getNumOperands() <= ImmutabilityFlagOp) + return Tag; + + // If Tag is already mutable then return it. + Metadata *ImmutabilityFlagNode = Tag->getOperand(ImmutabilityFlagOp); + if (!mdconst::extract(ImmutabilityFlagNode)->getValue()) + return Tag; + + // Otherwise, create another node. + if (!NewFormat) + return MDBuilder(Tag->getContext()).createTBAAStructTagNode(BaseType, AccessType, Offset); + + Metadata *SizeNode = Tag->getOperand(3); + uint64_t Size = mdconst::extract(SizeNode)->getZExtValue(); + return MDBuilder(Tag->getContext()).createTBAAAccessTag(BaseType, AccessType, Offset, Size); +} +#else +MDNode *createMutableTBAAAccessTag(MDNode *Tag) { + return MDBuilder(Tag->getContext()).createMutableTBAAAccessTag(TBAA); +} +#endif + + bool LateLowerGCFrame::CleanupIR(Function &F, State *S) { bool ChangesMade = false; // We create one alloca for all the jlcall frames that haven't been processed @@ -1737,6 +1774,24 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S) { SmallVector write_barriers; for (BasicBlock &BB : F) { for (auto it = BB.begin(); it != BB.end();) { + Instruction *I = &*it; + if (isa(I) || isa(I)) { + // strip all constant alias information, as it might depend on the gc having + // preserved a gc root, which stops being true after this pass (#32215) + // we'd like to call RewriteStatepointsForGC::stripNonValidData here, but + // that function asserts that the GC strategy must be named either "statepoint-example" or "coreclr", + // while we don't give a name to our GC in the IR, and C++ scope rules prohibit us from using it, + // so instead we reimplement it here badly + if (I->getMetadata(LLVMContext::MD_invariant_load)) + I->setMetadata(LLVMContext::MD_invariant_load, NULL); + if (MDNode *TBAA = I->getMetadata(LLVMContext::MD_tbaa)) { + if (TBAA->getNumOperands() == 4 && isTBAA(TBAA, {"jtbaa_const"})) { + MDNode *MutableTBAA = createMutableTBAAAccessTag(TBAA); + if (MutableTBAA != TBAA) + I->setMetadata(LLVMContext::MD_tbaa, MutableTBAA); + } + } + } auto *CI = dyn_cast(&*it); if (!CI) { ++it; @@ -1805,16 +1860,16 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S) { size_t nargs = CI->getNumArgOperands(); size_t nframeargs = nargs - (CC == JLCALL_F_CC); SmallVector ReplacementArgs; - auto it = CI->arg_begin(); + auto arg_it = CI->arg_begin(); if (CC == JLCALL_F_CC) { - assert(it != CI->arg_end()); - ReplacementArgs.push_back(*(it++)); + assert(arg_it != CI->arg_end()); + ReplacementArgs.push_back(*(arg_it++)); } maxframeargs = std::max(maxframeargs, nframeargs); int slot = 0; IRBuilder<> Builder (CI); - for (; it != CI->arg_end(); ++it) { - Builder.CreateStore(*it, Builder.CreateGEP(T_prjlvalue, Frame, + for (; arg_it != CI->arg_end(); ++arg_it) { + Builder.CreateStore(*arg_it, Builder.CreateGEP(T_prjlvalue, Frame, ConstantInt::get(T_int32, slot++))); } ReplacementArgs.push_back(nframeargs == 0 ? diff --git a/src/subtype.c b/src/subtype.c index d1c37498cb05a..e4f77f7fa4060 100644 --- a/src/subtype.c +++ b/src/subtype.c @@ -499,10 +499,13 @@ static void record_var_occurrence(jl_varbinding_t *vb, jl_stenv_t *e, int param) { if (vb != NULL && param) { // saturate counters at 2; we don't need values bigger than that - if (param == 2 && (vb->right ? e->Rinvdepth : e->invdepth) > vb->depth0 && vb->occurs_inv < 2) - vb->occurs_inv++; - else if (vb->occurs_cov < 2) + if (param == 2 && (vb->right ? e->Rinvdepth : e->invdepth) > vb->depth0) { + if (vb->occurs_inv < 2) + vb->occurs_inv++; + } + else if (vb->occurs_cov < 2) { vb->occurs_cov++; + } } } @@ -647,22 +650,10 @@ static jl_value_t *fix_inferred_var_bound(jl_tvar_t *var, jl_value_t *ty JL_MAYB static int var_occurs_inside(jl_value_t *v, jl_tvar_t *var, int inside, int want_inv) JL_NOTSAFEPOINT; -// compare UnionAll type `u` to `t`. `R==1` if `u` came from the right side of A <: B. -static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8_t R, int param) +typedef int (*tvar_callback)(void*, int8_t, jl_stenv_t *, int); + +static int with_tvar(tvar_callback callback, void *context, jl_unionall_t *u, int8_t R, jl_stenv_t *e, int param) { - 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. - while (btemp != NULL) { - if (btemp->var == u->var || - // outer var can only refer to inner var if bounds changed - (btemp->lb != btemp->var->lb && jl_has_typevar(btemp->lb, u->var)) || - (btemp->ub != btemp->var->ub && jl_has_typevar(btemp->ub, u->var))) { - u = rename_unionall(u); - break; - } - btemp = btemp->prev; - } jl_varbinding_t vb = { u->var, u->var->lb, u->var->ub, R, NULL, 0, 0, 0, 0, R ? e->Rinvdepth : e->invdepth, 0, NULL, e->vars }; JL_GC_PUSH4(&u, &vb.lb, &vb.ub, &vb.innervars); @@ -670,7 +661,7 @@ static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8 int ans; if (R) { e->envidx++; - ans = subtype(t, u->body, e, param); + ans = callback(context, R, e, param); e->envidx--; // widen Type{x} to typeof(x) in argument position if (!vb.occurs_inv) @@ -701,7 +692,7 @@ static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8 } } else { - ans = subtype(u->body, t, e, param); + ans = callback(context, R, e, param); } // handle the "diagonal dispatch" rule, which says that a type var occurring more @@ -743,7 +734,12 @@ static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8 e->vars = vb.prev; - btemp = e->vars; + if (!ans) { + JL_GC_POP(); + return 0; + } + + jl_varbinding_t *btemp = e->vars; if (vb.lb != vb.ub) { while (btemp != NULL) { jl_value_t *vu = btemp->ub; @@ -762,30 +758,66 @@ static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8 return ans; } -// unwrap <=2 layers of UnionAlls, leaving the vars in *p1 and *p2 and returning the body -static jl_value_t *unwrap_2_unionall(jl_value_t *t, jl_tvar_t **p1, jl_tvar_t **p2) JL_NOTSAFEPOINT +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. + while (btemp != NULL) { + if (btemp->var == u->var || + // outer var can only refer to inner var if bounds changed + (btemp->lb != btemp->var->lb && jl_has_typevar(btemp->lb, u->var)) || + (btemp->ub != btemp->var->ub && jl_has_typevar(btemp->ub, u->var))) { + u = rename_unionall(u); + break; + } + btemp = btemp->prev; + } + return u; +} + +struct subtype_unionall_env { + jl_value_t *t; + jl_value_t *ubody; +}; + +static int subtype_unionall_callback(struct subtype_unionall_env *env, int8_t R, jl_stenv_t *s, int param) { + if (R) { + return subtype(env->t, env->ubody, s, param); + } + else { + return subtype(env->ubody, env->t, s, param); + } +} + +// compare UnionAll type `u` to `t`. `R==1` if `u` came from the right side of A <: B. +static int subtype_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_t *e, int8_t R, int param) +{ + 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); +} + +// unwrap <=1 layers of UnionAlls, leaving the var in *p1 the body +static jl_datatype_t *unwrap_1_unionall(jl_value_t *t, jl_tvar_t **p1) JL_NOTSAFEPOINT { assert(t); if (jl_is_unionall(t)) { *p1 = ((jl_unionall_t*)t)->var; t = ((jl_unionall_t*)t)->body; - if (jl_is_unionall(t)) { - *p2 = ((jl_unionall_t*)t)->var; - t = ((jl_unionall_t*)t)->body; - } } - return t; + assert(jl_is_datatype(t)); + return (jl_datatype_t*)t; } // check n <: (length of vararg type v) static int check_vararg_length(jl_value_t *v, ssize_t n, jl_stenv_t *e) { - jl_tvar_t *va_p1=NULL, *va_p2=NULL; - jl_value_t *tail = unwrap_2_unionall(v, &va_p1, &va_p2); - assert(jl_is_datatype(tail)); + jl_tvar_t *va_p1=NULL; + jl_datatype_t *tail = unwrap_1_unionall(v, &va_p1); jl_value_t *N = jl_tparam1(tail); // only do the check if N is free in the tuple type's last parameter - if (N != (jl_value_t*)va_p1 && N != (jl_value_t*)va_p2) { + if (N != (jl_value_t*)va_p1) { jl_value_t *nn = jl_box_long(n); JL_GC_PUSH1(&nn); e->invdepth++; @@ -800,90 +832,187 @@ static int check_vararg_length(jl_value_t *v, ssize_t n, jl_stenv_t *e) return 1; } -static int subtype_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_stenv_t *e, int param) -{ - size_t lx = jl_nparams(xd), ly = jl_nparams(yd); - if (lx == 0 && ly == 0) - return 1; - size_t i=0, j=0; - int vx=0, vy=0, vvx = (lx > 0 && jl_is_vararg_type(jl_tparam(xd, lx-1))); - int vvy = (ly > 0 && jl_is_vararg_type(jl_tparam(yd, ly-1))); - if (vvx) { - if ((vvy && ly > lx) || (!vvy && ly < lx-1)) +static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e); + +struct subtype_tuple_env { + jl_datatype_t *xd, *yd; + jl_value_t *lastx, *lasty; + size_t lx, ly; + size_t i, j; + int vx, vy; + jl_value_t *vtx; + jl_value_t *vty; + jl_vararg_kind_t vvx, vvy; +}; + +static int subtype_tuple_varargs(struct subtype_tuple_env *env, jl_stenv_t *e, int param) +{ + jl_tvar_t *yv1=NULL; + jl_datatype_t *yva = unwrap_1_unionall(env->vty, &yv1); + jl_tvar_t *xv1=NULL; + jl_datatype_t *xva = unwrap_1_unionall(env->vtx, &xv1); + + jl_value_t *xp0 = jl_tparam0(xva); jl_value_t *xp1 = jl_tparam1(xva); + jl_value_t *yp0 = jl_tparam0(yva); jl_value_t *yp1 = jl_tparam1(yva); + + if (!jl_is_datatype(env->vtx)) { + // Unconstrained on the left, constrained on the right + jl_value_t *yl = yp1; + if (jl_is_typevar(yl)) { + jl_varbinding_t *ylv = lookup(e, (jl_tvar_t*)yl); + if (ylv) + yl = ylv->lb; + } + if (jl_is_long(yl)) { return 0; + } } - else if ((vvy && ly > lx+1) || (!vvy && lx != ly)) { - return 0; - } - param = (param == 0 ? 1 : param); - jl_value_t *lastx=NULL, *lasty=NULL; - while (i < lx) { - jl_value_t *xi = jl_tparam(xd, i); - if (i == lx-1 && vvx) vx = 1; - jl_value_t *yi = NULL; - if (j < ly) { - yi = jl_tparam(yd, j); - if (j == ly-1 && vvy) vy = 1; + else { + jl_value_t *xl = jl_tparam1(env->vtx); + if (jl_is_typevar(xl)) { + jl_varbinding_t *xlv = lookup(e, (jl_tvar_t*)xl); + if (xlv) + xl = xlv->lb; + } + if (jl_is_long(xl)) { + if (jl_unbox_long(xl) + 1 == env->vx) { + // LHS is exhausted. We're a subtype if the RHS is either + // exhausted as well or unbounded (in which case we need to + // set it to 0). + if (jl_is_datatype(env->vty)) { + jl_value_t *yl = jl_tparam1(env->vty); + if (jl_is_typevar(yl)) { + jl_varbinding_t *ylv = lookup(e, (jl_tvar_t*)yl); + if (ylv) + yl = ylv->lb; + } + if (jl_is_long(yl)) { + return jl_unbox_long(yl) + 1 == env->vy; + } + } + else { + // We can skip the subtype check, but we still + // need to make sure to constrain the length of y + // to 0. + goto constrain_length; + } + } } - if (vx && !vy) { - if (!check_vararg_length(xi, ly+1-lx, e)) - return 0; - jl_tvar_t *p1=NULL, *p2=NULL; - xi = unwrap_2_unionall(xi, &p1, &p2); - jl_value_t *N = jl_tparam1(xi); - if (N == (jl_value_t*)p1 || N == (jl_value_t*)p2) - return 0; - if (j >= ly) return 1; - xi = jl_tparam0(xi); + } + + // in Vararg{T1} <: Vararg{T2}, need to check subtype twice to + // simulate the possibility of multiple arguments, which is needed + // to implement the diagonal rule correctly. + if (!subtype(xp0, yp0, e, 1)) return 0; + if (!subtype(xp0, yp0, e, 1)) return 0; + +constrain_length: + if (!jl_is_datatype(env->vtx)) { + jl_value_t *yl = yp1; + if (jl_is_typevar(yl)) { + jl_varbinding_t *ylv = lookup(e, (jl_tvar_t*)yl); + if (ylv) + yl = ylv->lb; } - else if (j >= ly) { + if (jl_is_long(yl)) { + // The length of the x tuple is unconstrained, but the + // length of the y tuple is now fixed (this could have happened + // as a result of the subtype call above). return 0; } - if (!vx && vy) { - jl_tvar_t *p1=NULL, *p2=NULL; - yi = jl_tparam0(unwrap_2_unionall(yi, &p1, &p2)); - if (yi == (jl_value_t*)p1 || yi == (jl_value_t*)p2) - yi = ((jl_tvar_t*)yi)->ub; - if (!vvx && yi == (jl_value_t*)jl_any_type) - break; // if y ends in `Vararg{Any}` skip checking everything - } - if (vx && vy) { - jl_tvar_t *yp1=NULL, *yp2=NULL; - jl_value_t *yva = unwrap_2_unionall(yi, &yp1, &yp2); - jl_tvar_t *xp1=NULL, *xp2=NULL; - jl_value_t *xva = unwrap_2_unionall(xi, &xp1, &xp2); - if ((jl_value_t*)xp1 == jl_tparam1(xva) || (jl_value_t*)xp2 == jl_tparam1(xva)) { - // check for unconstrained vararg on left, constrained on right - if (jl_is_long(jl_tparam1(yva))) - return 0; - if (jl_is_typevar(jl_tparam1(yva))) { - jl_varbinding_t *ylv = lookup(e, (jl_tvar_t*)jl_tparam1(yva)); - if (ylv && jl_is_long(ylv->lb)) - return 0; + } + + // Vararg{T,N} <: Vararg{T2,N2}; equate N and N2 + e->invdepth++; + JL_GC_PUSH2(&xp1, &yp1); + if (jl_is_long(xp1) && env->vx != 1) + xp1 = jl_box_long(jl_unbox_long(xp1) - env->vx + 1); + if (jl_is_long(yp1) && env->vy != 1) + yp1 = jl_box_long(jl_unbox_long(yp1) - env->vy + 1); + int ans = forall_exists_equal(xp1, yp1, e); + JL_GC_POP(); + e->invdepth--; + return ans; +} + +static int subtype_tuple_tail(struct subtype_tuple_env *env, int8_t R, jl_stenv_t *e, int param) +{ +loop: // while (i <= lx) { + if (env->i >= env->lx) + goto done; + + /* Get the type in the current index. If necessary introduce tvars for + varargs */ + jl_value_t *xi = NULL; + if (env->i == env->lx-1 && env->vvx) { + if (!env->vtx) { + xi = jl_tparam(env->xd, env->i); + // Unbounded vararg on the LHS without vararg on the RHS should + // have been caught earlier. + assert(env->vvy || !jl_is_unionall(xi)); + if (jl_is_unionall(xi)) { + // TODO: If !var_occurs_inside(jl_tparam0(xid), p1, 0, 1), + // we could avoid introducing the tvar into the environment + jl_unionall_t *u = (jl_unionall_t*)xi; + 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); } + env->vtx = xi; } + xi = env->vtx; + } + else { + xi = jl_tparam(env->xd, env->i); + } - // skip testing element type if vararg lengths are 0 - if (jl_is_datatype(xi)) { - jl_value_t *xl = jl_tparam1(xi); - if (jl_is_typevar(xl)) { - jl_varbinding_t *xlv = lookup(e, (jl_tvar_t*)xl); - if (xlv && jl_is_long(xlv->lb) && jl_unbox_long(xlv->lb) == 0) - break; + jl_value_t *yi = NULL; + if (env->j < env->ly) { + if (env->j == env->ly-1 && env->vvy) { + if (!env->vty) { + yi = jl_tparam(env->yd, env->j); + if (jl_is_unionall(yi)) { + jl_unionall_t *u = (jl_unionall_t*)yi; + 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); + } + env->vty = yi; } + yi = env->vty; } - if (jl_is_datatype(yi)) { - jl_value_t *yl = jl_tparam1(yi); - if (jl_is_typevar(yl)) { - jl_varbinding_t *ylv = lookup(e, (jl_tvar_t*)yl); - if (ylv && jl_is_long(ylv->lb) && jl_unbox_long(ylv->lb) == 0) - break; - } + else { + yi = jl_tparam(env->yd, env->j); } } - if (xi == lastx && - ((yi == lasty && !jl_has_free_typevars(xi) && !jl_has_free_typevars(yi)) || - (yi == lasty && !vx && vy && jl_is_concrete_type(xi)))) { + + if (env->vtx) + env->vx += 1; + if (env->vty) + env->vy += 1; + + if (env->vx && env->vy) { + return subtype_tuple_varargs(env, e, param); + } + + if (env->vx) { + xi = jl_tparam0(jl_unwrap_unionall(env->vtx)); + if (env->j >= env->ly) + return 1; + } + else if (env->j >= env->ly) { + return 0; + } + if (env->vy) { + yi = jl_tparam0(jl_unwrap_unionall(env->vty)); + if (!env->vvx && yi == (jl_value_t*)jl_any_type) + goto done; // if y ends in `Vararg{Any}` skip checking everything + } + if (xi == env->lastx && + ((yi == env->lasty && !jl_has_free_typevars(xi) && !jl_has_free_typevars(yi)) || + (yi == env->lasty && !env->vx && env->vy && jl_is_concrete_type(xi)))) { // fast path for repeated elements } else if (e->Runions.depth == 0 && e->Lunions.depth == 0 && !jl_has_free_typevars(xi) && !jl_has_free_typevars(yi)) { @@ -894,24 +1023,103 @@ static int subtype_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_stenv_t *e, in else if (!subtype(xi, yi, e, param)) { return 0; } - if (vx && vy) break; - lastx = xi; lasty = yi; - if (i < lx-1 || !vx) - i++; - if (j < ly-1 || !vy) - j++; - } - // TODO: handle Vararg with explicit integer length parameter - vy = vy || (j < ly && jl_is_vararg_type(jl_tparam(yd,j))); - if (vy && !vx && lx+1 >= ly) { + env->lastx = xi; env->lasty = yi; + if (env->i < env->lx-1 || !env->vx) + env->i++; + if (env->j < env->ly-1 || !env->vy) + env->j++; + + goto loop; + // } (from loop:) + +done: + if (!env->vy && env->j < env->ly && jl_is_vararg_type(jl_tparam(env->yd, env->j))) + env->vy += 1; + if (env->vy && !env->vx && env->lx+1 >= env->ly) { // in Tuple{...,tn} <: Tuple{...,Vararg{T,N}}, check (lx+1-ly) <: N - if (!check_vararg_length(jl_tparam(yd,ly-1), lx+1-ly, e)) + if (!check_vararg_length(jl_tparam(env->yd,env->ly-1), env->lx+1-env->ly, e)) return 0; } - return (lx==ly && vx==vy) || (vy && (lx >= (vx ? ly : (ly-1)))); + return (env->lx + env->vx == env->ly + env->vy) || (env->vy && (env->lx >= (env->vx ? env->ly : (env->ly-1)))); } -static int forall_exists_equal(jl_value_t *x, jl_value_t *y, jl_stenv_t *e); +static int subtype_tuple(jl_datatype_t *xd, jl_datatype_t *yd, jl_stenv_t *e, int param) +{ + struct subtype_tuple_env env; + env.xd = xd; + env.yd = yd; + env.lx = jl_nparams(xd); + env.ly = jl_nparams(yd); + if (env.lx == 0 && env.ly == 0) + return 1; + env.i = env.j = 0; + env.vx = env.vy = 0; + env.vvx = env.vvy = JL_VARARG_NONE; + jl_varbinding_t *xbb = NULL; + if (env.lx > 0) { + env.vvx = jl_vararg_kind(jl_tparam(env.xd, env.lx-1)); + if (env.vvx == JL_VARARG_BOUND) + xbb = lookup(e, (jl_tvar_t *)jl_tparam1(jl_tparam(env.xd, env.lx - 1))); + } + if (env.ly > 0) + env.vvy = jl_vararg_kind(jl_tparam(env.yd, env.ly-1)); + if (env.vvx != JL_VARARG_NONE && env.vvx != JL_VARARG_INT && + (!xbb || !jl_is_long(xbb->lb))) { + if (env.vvx == JL_VARARG_UNBOUND || (xbb && !xbb->right)) { + // Unbounded on the LHS, bounded on the RHS + if (env.vvy == JL_VARARG_NONE || env.vvy == JL_VARARG_INT) + return 0; + else if (env.lx < env.ly) // Unbounded includes N == 0 + return 0; + } + else if (env.vvy == JL_VARARG_NONE && !check_vararg_length(jl_tparam(env.xd, env.lx-1), env.ly+1-env.lx, e)) { + return 0; + } + } + else { + size_t nx = env.lx; + if (env.vvx == JL_VARARG_INT) + nx += jl_vararg_length(jl_tparam(env.xd, env.lx-1)) - 1; + else if (xbb && jl_is_long(xbb->lb)) + nx += jl_unbox_long(xbb->lb) - 1; + else + assert(env.vvx == JL_VARARG_NONE); + size_t ny = env.ly; + if (env.vvy == JL_VARARG_INT) + ny += jl_vararg_length(jl_tparam(env.yd, env.ly-1)) - 1; + else if (env.vvy != JL_VARARG_NONE) + ny -= 1; + if (env.vvy == JL_VARARG_NONE || env.vvy == JL_VARARG_INT) { + if (nx != ny) + return 0; + } + else { + if (ny > nx) + return 0; + } + } + + param = (param == 0 ? 1 : param); + env.lastx = env.lasty=NULL; + env.vtx = env.vty=NULL; + return subtype_tuple_tail(&env, 0, e, param); +} + +static int subtype_naked_vararg(jl_datatype_t *xd, jl_datatype_t *yd, jl_stenv_t *e, int param) +{ + // Vararg: covariant in first parameter, invariant in second + jl_value_t *xp1=jl_tparam0(xd), *xp2=jl_tparam1(xd), *yp1=jl_tparam0(yd), *yp2=jl_tparam1(yd); + // in Vararg{T1} <: Vararg{T2}, need to check subtype twice to + // simulate the possibility of multiple arguments, which is needed + // to implement the diagonal rule correctly. + if (!subtype(xp1, yp1, e, 1)) return 0; + if (!subtype(xp1, yp1, e, 1)) return 0; + e->invdepth++; + // Vararg{T,N} <: Vararg{T2,N2}; equate N and N2 + int ans = forall_exists_equal(xp2, yp2, e); + e->invdepth--; + return ans; +} // `param` means we are currently looking at a parameter of a type constructor // (as opposed to being outside any type constructor, or comparing variable bounds). @@ -1028,7 +1236,15 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) jl_value_t *tp0 = jl_tparam0(yd); if (!jl_is_typevar(tp0) || !jl_is_kind(x)) return 0; - return subtype((jl_value_t*)jl_type_type, y, e, param); + // DataType.super is special, so `DataType <: Type{T}` (T free) needs special handling. + // The answer is true iff `T` has full bounds (as in `Type`), but this needs to + // be checked at the same depth where `Type{T}` occurs --- the depth of the LHS + // doesn't matter because it (e.g. `DataType`) doesn't actually contain the variable. + int saved = e->invdepth; + e->invdepth = e->Rinvdepth; + int issub = subtype((jl_value_t*)jl_type_type, y, e, param); + e->invdepth = saved; + return issub; } while (xd != jl_any_type && xd->name != yd->name) { if (xd->super == NULL) @@ -1039,20 +1255,11 @@ static int subtype(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int param) if (xd->name == jl_tuple_typename) return subtype_tuple(xd, yd, e, param); if (xd->name == jl_vararg_typename) { - // Vararg: covariant in first parameter, invariant in second - jl_value_t *xp1=jl_tparam0(xd), *xp2=jl_tparam1(xd), *yp1=jl_tparam0(yd), *yp2=jl_tparam1(yd); - // in Vararg{T1} <: Vararg{T2}, need to check subtype twice to - // simulate the possibility of multiple arguments, which is needed - // to implement the diagonal rule correctly. - if (!subtype(xp1, yp1, e, 1)) return 0; - if (!subtype(xp1, yp1, e, 1)) return 0; - // Vararg{T,N} <: Vararg{T2,N2}; equate N and N2 - e->invdepth++; - e->Rinvdepth++; - int ans = forall_exists_equal(xp2, yp2, e); - e->invdepth--; - e->Rinvdepth--; - return ans; + // N.B.: This case is only used for raw varargs that are not part + // of a tuple (those that are have special handling in subtype_tuple). + // Vararg isn't really a proper type, but it does sometimes show up + // as e.g. Type{Vararg}, so we'd like to handle that correctly. + return subtype_naked_vararg(xd, yd, e, param); } size_t i, np = jl_nparams(xd); int ans = 1; @@ -1311,24 +1518,38 @@ JL_DLLEXPORT int jl_obvious_subtype(jl_value_t *x, jl_value_t *y, int *subtype) return 1; } int i, npx = jl_nparams(x), npy = jl_nparams(y); - int vx = 0, vy = 0; + jl_vararg_kind_t vx = JL_VARARG_NONE; + jl_value_t *vxt = NULL; + int vy = 0; + int nparams_expanded_x = npx; if (istuple) { - vx = npx > 0 && jl_is_vararg_type(jl_tparam(x, npx - 1)); + if (npx > 0) { + jl_value_t *xva = jl_tparam(x, npx - 1); + vx = jl_vararg_kind(xva); + if (vx != JL_VARARG_NONE) { + vxt = jl_unwrap_vararg(xva); + nparams_expanded_x -= 1; + if (vx == JL_VARARG_INT) + nparams_expanded_x += jl_vararg_length(xva); + } + } vy = npy > 0 && jl_is_vararg_type(jl_tparam(y, npy - 1)); } - if (npx != npy || vx || vy) { - if (!vy) { + if (npx != npy || vx != JL_VARARG_NONE || vy) { + if ((vx == JL_VARARG_NONE || vx == JL_VARARG_UNBOUND) && !vy) { *subtype = 0; return 1; } - if (npx - vx < npy - vy) { + if ((vx == JL_VARARG_NONE || vx == JL_VARARG_INT) && + nparams_expanded_x < npy - vy) { *subtype = 0; return 1; // number of fixed parameters in x could be fewer than in y } + // TODO: Can do better here for the JL_VARARG_INT case. uncertain = 1; } for (i = 0; i < npy - vy; i++) { - jl_value_t *a = jl_tparam(x, i); + jl_value_t *a = i >= (npx - (vx == JL_VARARG_NONE ? 0 : 1)) ? vxt : jl_tparam(x, i); jl_value_t *b = jl_tparam(y, i); if (iscov || jl_is_typevar(b)) { if (jl_obvious_subtype(a, b, subtype)) { @@ -1690,19 +1911,35 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int int d = bb->depth0; jl_value_t *root=NULL; jl_savedenv_t se; if (param == 2) { - if (!(subtype_in_env_existential(bb->lb, a, e, 0, d) && subtype_in_env_existential(a, bb->ub, e, 1, d))) - return jl_bottom_type; - jl_value_t *ub = a; + jl_value_t *ub = NULL; + JL_GC_PUSH2(&ub, &root); + if (!jl_has_free_typevars(a)) { + save_env(e, &root, &se); + int issub = subtype_in_env_existential(bb->lb, a, e, 0, d) && subtype_in_env_existential(a, bb->ub, e, 1, d); + restore_env(e, root, &se); + free(se.buf); + if (!issub) { + JL_GC_POP(); + return jl_bottom_type; + } + ub = a; + } + else { + ub = R ? intersect_aside(a, bb->ub, e, 1, d) : intersect_aside(bb->ub, a, e, 0, d); + // TODO: we should probably check `bb->lb <: ub` here; find a test case for that + } if (ub != (jl_value_t*)b) { if (jl_has_free_typevars(ub)) { // constraint X == Ref{X} is unsatisfiable. also check variables set equal to X. if (var_occurs_inside(ub, b, 0, 0)) { + JL_GC_POP(); return jl_bottom_type; } jl_varbinding_t *btemp = e->vars; while (btemp != NULL) { if (btemp->lb == (jl_value_t*)b && btemp->ub == (jl_value_t*)b && var_occurs_inside(ub, btemp->var, 0, 0)) { + JL_GC_POP(); return jl_bottom_type; } btemp = btemp->prev; @@ -1711,6 +1948,7 @@ static jl_value_t *intersect_var(jl_tvar_t *b, jl_value_t *a, jl_stenv_t *e, int bb->ub = ub; bb->lb = ub; } + JL_GC_POP(); return ub; } else if (bb->constraintkind == 0) { @@ -2039,12 +2277,11 @@ static jl_value_t *intersect_unionall(jl_value_t *t, jl_unionall_t *u, jl_stenv_ // check n = (length of vararg type v) static int intersect_vararg_length(jl_value_t *v, ssize_t n, jl_stenv_t *e, int8_t R) { - jl_tvar_t *va_p1=NULL, *va_p2=NULL; - jl_value_t *tail = unwrap_2_unionall(v, &va_p1, &va_p2); - assert(jl_is_datatype(tail)); + jl_tvar_t *va_p1=NULL; + jl_datatype_t *tail = unwrap_1_unionall(v, &va_p1); jl_value_t *N = jl_tparam1(tail); // only do the check if N is free in the tuple type's last parameter - if (jl_is_typevar(N) && N != (jl_value_t*)va_p1 && N != (jl_value_t*)va_p2) { + if (jl_is_typevar(N) && N != (jl_value_t*)va_p1) { jl_value_t *len = jl_box_long(n); JL_GC_PUSH1(&len); jl_value_t *il = R ? intersect(len, N, e, 2) : intersect(N, len, e, 2); @@ -2222,26 +2459,16 @@ static jl_value_t *intersect_invariant(jl_value_t *x, jl_value_t *y, jl_stenv_t flip_vars(e); return jl_bottom_type; } - /* - TODO: This is a band-aid for issue #23685. A better solution would be to - first normalize types so that all `where` expressions in covariant position - are pulled out to the top level. - */ - if ((jl_is_typevar(x) && !jl_is_typevar(y) && lookup(e, (jl_tvar_t*)x) == NULL) || - (jl_is_typevar(y) && !jl_is_typevar(x) && lookup(e, (jl_tvar_t*)y) == NULL)) - return ii; jl_value_t *root=NULL; jl_savedenv_t se; JL_GC_PUSH2(&ii, &root); save_env(e, &root, &se); - if (!subtype_in_env(x, y, e)) { + if (!subtype_in_env_existential(x, y, e, 0, e->invdepth)) { ii = NULL; } else { - flip_vars(e); - if (!subtype_in_env(y, x, e)) + if (!subtype_in_env_existential(y, x, e, 0, e->invdepth)) ii = NULL; - flip_vars(e); } restore_env(e, root, &se); free(se.buf); @@ -2368,7 +2595,7 @@ static jl_value_t *intersect(jl_value_t *x, jl_value_t *y, jl_stenv_t *e, int pa record_var_occurrence(lookup(e, (jl_tvar_t*)y), e, param); return intersect_var((jl_tvar_t*)y, x, e, 1, param); } - if (!jl_has_free_typevars(x) && !jl_has_free_typevars(y)) { + if (!jl_has_free_typevars(x) && !jl_has_free_typevars(y) && !jl_is_vararg_type(x) && !jl_is_vararg_type(y)) { if (jl_subtype(x, y)) return x; if (jl_subtype(y, x)) return y; } @@ -2571,6 +2798,14 @@ static jl_value_t *intersect_types(jl_value_t *x, jl_value_t *y, int emptiness_o jl_stenv_t e; if (obviously_disjoint(x, y, 0)) return jl_bottom_type; + if (jl_is_dispatch_tupletype(x) || jl_is_dispatch_tupletype(y)) { + if (jl_subtype(x, y)) + return x; + else if (jl_subtype(y, x)) + return y; + else + return jl_bottom_type; + } init_stenv(&e, NULL, 0); e.intersection = e.ignore_free = 1; e.emptiness_only = emptiness_only; diff --git a/stdlib/LinearAlgebra/src/bidiag.jl b/stdlib/LinearAlgebra/src/bidiag.jl index 8547e1c2e5f0f..79dce0dd36fd7 100644 --- a/stdlib/LinearAlgebra/src/bidiag.jl +++ b/stdlib/LinearAlgebra/src/bidiag.jl @@ -248,6 +248,7 @@ iszero(M::Bidiagonal) = iszero(M.dv) && iszero(M.ev) isone(M::Bidiagonal) = all(isone, M.dv) && iszero(M.ev) istriu(M::Bidiagonal) = M.uplo == 'U' || iszero(M.ev) istril(M::Bidiagonal) = M.uplo == 'L' || iszero(M.ev) +isdiag(M::Bidiagonal) = iszero(M.ev) function tril!(M::Bidiagonal, k::Integer=0) n = length(M.dv) diff --git a/stdlib/LinearAlgebra/src/cholesky.jl b/stdlib/LinearAlgebra/src/cholesky.jl index 14444b6d00f4f..86f91fa80b75d 100644 --- a/stdlib/LinearAlgebra/src/cholesky.jl +++ b/stdlib/LinearAlgebra/src/cholesky.jl @@ -424,21 +424,30 @@ end function ldiv!(C::CholeskyPivoted, B::StridedVector) if C.uplo == 'L' ldiv!(adjoint(LowerTriangular(C.factors)), - ldiv!(LowerTriangular(C.factors), B[C.piv]))[invperm(C.piv)] + ldiv!(LowerTriangular(C.factors), permute!(B, C.piv))) else ldiv!(UpperTriangular(C.factors), - ldiv!(adjoint(UpperTriangular(C.factors)), B[C.piv]))[invperm(C.piv)] + ldiv!(adjoint(UpperTriangular(C.factors)), permute!(B, C.piv))) end + invpermute!(B, C.piv) end function ldiv!(C::CholeskyPivoted, B::StridedMatrix) + n = size(C, 1) + for i in 1:size(B, 2) + permute!(view(B, 1:n, i), C.piv) + end if C.uplo == 'L' ldiv!(adjoint(LowerTriangular(C.factors)), - ldiv!(LowerTriangular(C.factors), B[C.piv,:]))[invperm(C.piv),:] + ldiv!(LowerTriangular(C.factors), B)) else ldiv!(UpperTriangular(C.factors), - ldiv!(adjoint(UpperTriangular(C.factors)), B[C.piv,:]))[invperm(C.piv),:] + ldiv!(adjoint(UpperTriangular(C.factors)), B)) end + for i in 1:size(B, 2) + invpermute!(view(B, 1:n, i), C.piv) + end + B end isposdef(C::Union{Cholesky,CholeskyPivoted}) = C.info == 0 diff --git a/stdlib/LinearAlgebra/src/diagonal.jl b/stdlib/LinearAlgebra/src/diagonal.jl index 77c9b924cae43..23180477bb2c5 100644 --- a/stdlib/LinearAlgebra/src/diagonal.jl +++ b/stdlib/LinearAlgebra/src/diagonal.jl @@ -481,7 +481,7 @@ function ldiv!(D::Diagonal, B::StridedVecOrMat) if di == 0 throw(SingularException(i)) end - B[i,j] /= di + B[i,j] = di \ B[i,j] end end return B diff --git a/stdlib/LinearAlgebra/test/bidiag.jl b/stdlib/LinearAlgebra/test/bidiag.jl index 1a1dcd4fa0ba7..43b0dc338d388 100644 --- a/stdlib/LinearAlgebra/test/bidiag.jl +++ b/stdlib/LinearAlgebra/test/bidiag.jl @@ -150,6 +150,10 @@ Random.seed!(1) @test triu!(bidiagcopy(dv,ev,:U)) == Bidiagonal(dv,ev,:U) @test_throws ArgumentError triu!(bidiagcopy(dv, ev, :U), -n) @test_throws ArgumentError triu!(bidiagcopy(dv, ev, :U), n + 2) + @test !isdiag(Bidiagonal(dv,ev,:U)) + @test !isdiag(Bidiagonal(dv,ev,:L)) + @test isdiag(Bidiagonal(dv,zerosev,:U)) + @test isdiag(Bidiagonal(dv,zerosev,:L)) end @testset "iszero and isone" begin diff --git a/stdlib/LinearAlgebra/test/cholesky.jl b/stdlib/LinearAlgebra/test/cholesky.jl index 558b1439dbb25..c00d3a823b8e2 100644 --- a/stdlib/LinearAlgebra/test/cholesky.jl +++ b/stdlib/LinearAlgebra/test/cholesky.jl @@ -164,7 +164,31 @@ end lpapd = cholesky(apdhL, Val(true)) @test norm(apd * (lpapd\b) - b)/norm(b) <= ε*κ*n # Ad hoc, revisit @test norm(apd * (lpapd\b[1:n]) - b[1:n])/norm(b[1:n]) <= ε*κ*n + end + end + end + + for eltyb in (Float64, ComplexF64) + Breal = convert(Matrix{BigFloat}, randn(n,n)/2) + Bimg = convert(Matrix{BigFloat}, randn(n,n)/2) + B = (eltya <: Complex || eltyb <: Complex) ? complex.(Breal, Bimg) : Breal + εb = eps(abs(float(one(eltyb)))) + ε = max(εa,εb) + + for B in (B, view(B, 1:n, 1:n)) # Array and SubArray + # Test error bound on linear solver: LAWNS 14, Theorem 2.1 + # This is a surprisingly loose bound + BB = copy(B) + ldiv!(capd, BB) + @test norm(apd \ B - BB, 1) / norm(BB, 1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ + @test norm(apd * BB - B, 1) / norm(B, 1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ + if eltya != BigFloat + cpapd = cholesky(apdh, Val(true)) + BB = copy(B) + ldiv!(cpapd, BB) + @test norm(apd \ B - BB, 1) / norm(BB, 1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ + @test norm(apd * BB - B, 1) / norm(B, 1) <= (3n^2 + n + n^3*ε)*ε/(1-(n+1)*ε)*κ end end end diff --git a/stdlib/LinearAlgebra/test/diagonal.jl b/stdlib/LinearAlgebra/test/diagonal.jl index 4dc697336d75a..8b92539ab1ad3 100644 --- a/stdlib/LinearAlgebra/test/diagonal.jl +++ b/stdlib/LinearAlgebra/test/diagonal.jl @@ -441,6 +441,15 @@ end @test det(D) == 4 end +@testset "linear solve for block diagonal matrices" begin + D = Diagonal([rand(2,2) for _ in 1:5]) + b = [rand(2,2) for _ in 1:5] + B = [rand(2,2) for _ in 1:5, _ in 1:5] + @test ldiv!(D, copy(b)) ≈ Diagonal(inv.(D.diag)) * b + @test ldiv!(D, copy(B)) ≈ Diagonal(inv.(D.diag)) * B + @test rdiv!(copy(B), D) ≈ B * Diagonal(inv.(D.diag)) +end + @testset "multiplication with Symmetric/Hermitian" begin for T in (Float64, ComplexF64) D = Diagonal(randn(T, n)) diff --git a/stdlib/SparseArrays/src/abstractsparse.jl b/stdlib/SparseArrays/src/abstractsparse.jl index 50ca7e6845e5a..2ebea517f2292 100644 --- a/stdlib/SparseArrays/src/abstractsparse.jl +++ b/stdlib/SparseArrays/src/abstractsparse.jl @@ -64,21 +64,31 @@ end # The following two methods should be overloaded by concrete types to avoid # allocating the I = findall(...) -_sparse_findnextnz(v::AbstractSparseArray, i::Integer) = (I = findall(!iszero, v); n = searchsortedfirst(I, i); n<=length(I) ? I[n] : nothing) -_sparse_findprevnz(v::AbstractSparseArray, i::Integer) = (I = findall(!iszero, v); n = searchsortedlast(I, i); !iszero(n) ? I[n] : nothing) - -function findnext(f::typeof(!iszero), v::AbstractSparseArray, i::Integer) +_sparse_findnextnz(v::AbstractSparseArray, i) = (I = findall(!iszero, v); n = searchsortedfirst(I, i); n<=length(I) ? I[n] : nothing) +_sparse_findprevnz(v::AbstractSparseArray, i) = (I = findall(!iszero, v); n = searchsortedlast(I, i); !iszero(n) ? I[n] : nothing) + +function findnext(f::Function, v::AbstractSparseArray, i) + # short-circuit the case f == !iszero because that avoids + # allocating e.g. zero(BigInt) for the f(zero(...)) test. + if nnz(v) == length(v) || (f != (!iszero) && f(zero(eltype(v)))) + return invoke(findnext, Tuple{Function,Any,Any}, f, v, i) + end j = _sparse_findnextnz(v, i) while j !== nothing && !f(v[j]) - j = _sparse_findnextnz(v, j+1) + j = _sparse_findnextnz(v, nextind(v, j)) end return j end -function findprev(f::typeof(!iszero), v::AbstractSparseArray, i::Integer) +function findprev(f::Function, v::AbstractSparseArray, i) + # short-circuit the case f == !iszero because that avoids + # allocating e.g. zero(BigInt) for the f(zero(...)) test. + if nnz(v) == length(v) || (f != (!iszero) && f(zero(eltype(v)))) + return invoke(findprev, Tuple{Function,Any,Any}, f, v, i) + end j = _sparse_findprevnz(v, i) while j !== nothing && !f(v[j]) - j = _sparse_findprevnz(v, j-1) + j = _sparse_findprevnz(v, prevind(v, j)) end return j end diff --git a/stdlib/SparseArrays/src/sparsematrix.jl b/stdlib/SparseArrays/src/sparsematrix.jl index b1dfb9dfc10fe..71fc6ec6fda73 100644 --- a/stdlib/SparseArrays/src/sparsematrix.jl +++ b/stdlib/SparseArrays/src/sparsematrix.jl @@ -1312,36 +1312,34 @@ function findnz(S::SparseMatrixCSC{Tv,Ti}) where {Tv,Ti} return (I, J, V) end -function _sparse_findnextnz(m::SparseMatrixCSC, i::Integer) - if i > length(m) - return nothing - end - row, col = Tuple(CartesianIndices(m)[i]) +function _sparse_findnextnz(m::SparseMatrixCSC, ij::CartesianIndex{2}) + row, col = Tuple(ij) + col > m.n && return nothing + lo, hi = m.colptr[col], m.colptr[col+1] n = searchsortedfirst(m.rowval, row, lo, hi-1, Base.Order.Forward) if lo <= n <= hi-1 - return LinearIndices(m)[m.rowval[n], col] + return CartesianIndex(m.rowval[n], col) end - nextcol = findnext(c->(c>hi), m.colptr, col+1) - nextcol === nothing && return nothing + nextcol = searchsortedfirst(m.colptr, hi + 1, col + 1, length(m.colptr), Base.Order.Forward) + nextcol > length(m.colptr) && return nothing nextlo = m.colptr[nextcol-1] - return LinearIndices(m)[m.rowval[nextlo], nextcol-1] + return CartesianIndex(m.rowval[nextlo], nextcol - 1) end -function _sparse_findprevnz(m::SparseMatrixCSC, i::Integer) - if iszero(i) - return nothing - end - row, col = Tuple(CartesianIndices(m)[i]) +function _sparse_findprevnz(m::SparseMatrixCSC, ij::CartesianIndex{2}) + row, col = Tuple(ij) + iszero(col) && return nothing + lo, hi = m.colptr[col], m.colptr[col+1] n = searchsortedlast(m.rowval, row, lo, hi-1, Base.Order.Forward) if lo <= n <= hi-1 - return LinearIndices(m)[m.rowval[n], col] + return CartesianIndex(m.rowval[n], col) end - prevcol = findprev(c->(c 0 - nzv = nzval[idx] - !isequal(nzv, z) && return idx, nzv - idx -= 1 - end - return zero(idx), z -end - -function _idx_to_cartesian(A::SparseMatrixCSC, idx::Integer) - rowval = rowvals(A) - i = rowval[idx] - j = searchsortedlast(A.colptr, idx, 1, size(A, 2), Base.Order.Forward) - return CartesianIndex(i, j) -end - -function Base.findnext(pred::Function, A::SparseMatrixCSC, ij::CartesianIndex{2}) - if nnz(A) == length(A) || pred(zero(eltype(A))) - return invoke(findnext, Tuple{Function,Any,Any}, pred, A, ij) - end - idx, nzv = _idxfirstnz(A, ij) - while idx > 0 - if pred(nzv) - return _idx_to_cartesian(A, idx) - end - idx, nzv = _idxnextnz(A, idx + 1) - end - return nothing -end - -function Base.findprev(pred::Function, A::SparseMatrixCSC, ij::CartesianIndex{2}) - if nnz(A) == length(A) || pred(zero(eltype(A))) - return invoke(findprev, Tuple{Function,Any,Any}, pred, A, ij) - end - idx, nzv = _idxlastnz(A, ij) - while idx > 0 - if pred(nzv) - return _idx_to_cartesian(A, idx) - end - idx, nzv = _idxprevnz(A, idx - 1) - end - return nothing -end - """ sprand([rng],[type],m,[n],p::AbstractFloat,[rfn]) diff --git a/stdlib/SparseArrays/test/sparse.jl b/stdlib/SparseArrays/test/sparse.jl index f752982437a7e..ee43a4b7fa9e9 100644 --- a/stdlib/SparseArrays/test/sparse.jl +++ b/stdlib/SparseArrays/test/sparse.jl @@ -94,7 +94,10 @@ do33 = fill(1.,3) end @testset "Issue #30006" begin - SparseMatrixCSC{Float64,Int32}(spzeros(3,3))[:, 1] == [1, 2, 3] + A = SparseMatrixCSC{Float64,Int32}(spzeros(3,3)) + A[:, 1] = [1, 2, 3] + @test nnz(A) == 3 + @test nonzeros(A) == [1, 2, 3] end @testset "concatenation tests" begin diff --git a/stdlib/Statistics/src/Statistics.jl b/stdlib/Statistics/src/Statistics.jl index 22d78bd3de65f..b6561670296c3 100644 --- a/stdlib/Statistics/src/Statistics.jl +++ b/stdlib/Statistics/src/Statistics.jl @@ -620,8 +620,8 @@ function corm(x::AbstractVector, mx, y::AbstractVector, my) @inbounds begin # Initialize the accumulators - xx = zero(sqrt(abs2(x[1]))) - yy = zero(sqrt(abs2(y[1]))) + 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) diff --git a/stdlib/Statistics/test/runtests.jl b/stdlib/Statistics/test/runtests.jl index 1e080de32b895..4ca32e43c5af6 100644 --- a/stdlib/Statistics/test/runtests.jl +++ b/stdlib/Statistics/test/runtests.jl @@ -455,6 +455,8 @@ 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 diff --git a/test/compiler/ssair.jl b/test/compiler/ssair.jl index 88ce5985a23ac..30f53b124ed66 100644 --- a/test/compiler/ssair.jl +++ b/test/compiler/ssair.jl @@ -96,3 +96,39 @@ let Meta.isexpr(ex, :meta) end end + +# PR #32145 +# Make sure IncrementalCompact can handle blocks with predecessors of index 0 +# while removing blocks with no predecessors. +let cfg = CFG(BasicBlock[ + make_bb([] , [2, 4]), + make_bb([1] , [4, 5]), + make_bb([] , [4] ), # should be removed + make_bb([0, 1, 2] , [5] ), # 0 predecessor should be preserved + make_bb([2, 3] , [] ), +], Int[]) + code = Compiler.IRCode( + [], [], Int32[], UInt8[], cfg, LineInfoNode[], [], [], []) + compact = Compiler.IncrementalCompact(code, true) + @test length(compact.result_bbs) == 4 && 0 in compact.result_bbs[3].preds +end + +# Issue #32579 - Optimizer bug involving type constraints +function f32579(x::Int, b::Bool) + if b + x = nothing + end + if isa(x, Int) + y = x + else + y = x + end + if isa(y, Nothing) + z = y + else + z = y + end + return z === nothing +end +@test f32579(0, true) === true +@test f32579(0, false) === false diff --git a/test/missing.jl b/test/missing.jl index 8815daa093641..a266058b6fe8e 100644 --- a/test/missing.jl +++ b/test/missing.jl @@ -16,6 +16,7 @@ end @test convert(Union{Int, Missing}, 1.0) === 1 @test convert(Union{Nothing, Missing}, missing) === missing @test convert(Union{Nothing, Missing}, nothing) === nothing + @test convert(Union{Missing, Nothing, Float64}, 1) === 1.0 @test_throws MethodError convert(Missing, 1) @test_throws MethodError convert(Union{Nothing, Missing}, 1) @@ -239,6 +240,14 @@ end @test isa(x, Vector{Union{Int, Missing}}) @test isequal(x, [missing]) @test eltype(adjoint([1, missing])) == Union{Int, Missing} + # issue #32777 + let a = [0, nothing, 0.0, missing] + @test a[1] === 0.0 + @test a[2] === nothing + @test a[3] === 0.0 + @test a[4] === missing + @test a isa Vector{Union{Missing, Nothing, Float64}} + end end @testset "== and != on arrays" begin diff --git a/test/subtype.jl b/test/subtype.jl index 571509d110d18..a8cc6351695f6 100644 --- a/test/subtype.jl +++ b/test/subtype.jl @@ -73,6 +73,8 @@ function test_2() @test !issub(Tuple{Tuple{Int,Int},Tuple{Int,}}, Tuple{NTuple{N,Int},NTuple{N,Int}} where N) @test NTuple{0} === Tuple{} + @test !issub(Tuple{Val{3}, Vararg{Val{3}}}, Tuple{Vararg{Val{N}, N} where N}) + @test issub_strict(Tuple{Int,Int}, Tuple{Int,Int,Vararg{Int,N}} where N) @test issub_strict(Tuple{Int,Int}, Tuple{E,E,Vararg{E,N}} where E where N) @@ -989,6 +991,17 @@ function test_intersection() @test_broken typeintersect(Tuple{Type{Z},Z} where Z, Tuple{Type{Ref{T}} where T, Ref{Float64}}) == Tuple{Type{Ref{Float64}},Ref{Float64}} + + # issue #32607 + @testintersect(Type{<:Tuple{Integer,Integer}}, + Type{Tuple{Int,T}} where T, + Type{Tuple{Int,T}} where T<:Integer) + @testintersect(Type{<:Tuple{Any,Vararg{Any}}}, + Type{Tuple{Vararg{Int,N}}} where N, + Type{Tuple{Int,Vararg{Int,N}}} where N) + @testintersect(Type{<:Array}, + Type{AbstractArray{T}} where T, + Bottom) end function test_intersection_properties() @@ -1175,7 +1188,7 @@ end struct TT20103{X,Y} end f20103(::Type{TT20103{X,Y}},x::X,y::Y) where {X,Y} = 1 f20103(::Type{TT20103{X,X}},x::X) where {X} = 100 -@test_broken typeintersect(Type{NTuple{N,E}} where E where N, Type{NTuple{N,E} where N} where E) == Union{} # use @testintersect once fixed +@testintersect(Type{NTuple{N,E}} where E where N, Type{NTuple{N,E} where N} where E, Union{}) let ints = (Int, Int32, UInt, UInt32) Ints = Union{ints...} vecs = [] @@ -1390,6 +1403,25 @@ end @testintersect((Tuple{Int, Array{T}} where T), (Tuple{Any, Vector{Union{Missing,Nothing,T}}} where T), (Tuple{Int, Vector{Union{Missing,Nothing,T}}} where T)) +# issue #32582 +let A = Tuple{Any, Type{Union{Nothing, Int64}}}, + B = Tuple{T, Type{Union{Nothing, T}}} where T, + I = typeintersect(A, B), + J = typeintersect(B, A) + # TODO: improve precision + @test I >: Tuple{Int64,Type{Union{Nothing, Int64}}} + @test J >: Tuple{Int64,Type{Union{Nothing, Int64}}} +end +@testintersect(Union{Array{T,1},Array{T,2}} where T<:Union{Float32,Float64}, + Union{AbstractMatrix{Float32},AbstractVector{Float32}}, + Union{Array{Float32,2}, Array{Float32,1}}) +let A = Tuple{Type{Union{Missing,T}},Any} where T, + B = Tuple{Type{Union{Nothing,T}},Any} where T + I = typeintersect(A, B) + J = typeintersect(B, A) + @test I >: Tuple{Type{Union{Nothing,Missing,T}}, Any} where T + @test J >: Tuple{Type{Union{Nothing,Missing,T}}, Any} where T +end # issue #29955 struct M29955{T, TV<:AbstractVector{T}} @@ -1479,7 +1511,9 @@ CovType{T} = Union{AbstractArray{T,2}, # issue #31703 @testintersect(Pair{<:Any, Ref{Tuple{Ref{Ref{Tuple{Int}}},Ref{Float64}}}}, Pair{T, S} where S<:(Ref{A} where A<:(Tuple{C,Ref{T}} where C<:(Ref{D} where D<:(Ref{E} where E<:Tuple{FF}) where FF<:B)) where B) where T, - Pair{Float64, Ref{Tuple{Ref{Ref{Tuple{Int}}},Ref{Float64}}}}) + Pair{T, Ref{Tuple{Ref{Ref{Tuple{Int}}},Ref{Float64}}}} where T) +# TODO: should be able to get this result +# Pair{Float64, Ref{Tuple{Ref{Ref{Tuple{Int}}},Ref{Float64}}}} module I31703 using Test, LinearAlgebra @@ -1545,6 +1579,80 @@ let X = LinearAlgebra.Symmetric{T, S} where S<:(AbstractArray{U, 2} where U<:T) end # issue #32386 +@test typeintersect(Type{S} where S<:(Vector{Pair{_A,N} where N} where _A), + Type{Vector{T}} where T) == Type{Vector{Pair{_A,N} where N}} where _A + +# issue #32488 +struct S32488{S <: Tuple, T, N, L} + data::NTuple{L,T} +end +@testintersect(Tuple{Type{T} where T<:(S32488{Tuple{_A}, Int64, 1, _A} where _A), Tuple{Vararg{Int64, D}} where D}, + Tuple{Type{S32488{S, T, N, L}}, Tuple{Vararg{T, L}}} where L where N where T where S, + Tuple{Type{S32488{Tuple{L},Int64,1,L}},Tuple{Vararg{Int64,L}}} where L) + # TODO: intersect currently returns a bad answer here (it has free typevars) @test typeintersect(Type{S} where S<:(Array{Pair{_A,N} where N, 1} where _A), Type{Vector{T}} where T) != Union{} + +# Various nasty varargs +let T1 = Tuple{Int, Tuple{T}, Vararg{T, 3}} where T <: Int, + T2 = Tuple{Int, Any, Any, Any, Integer}, + T3 = Tuple{Int, Any, Any, Any, Integer, Vararg{Integer, N} where N} + + @test issub_strict(T1, T2) + @test issub_strict(T2, T3) + @test issub_strict(T1, T3) +end +let A = Tuple{Float64, Vararg{Int64, 2}}, + B1 = Tuple{Float64, Vararg{T, 2}} where T <: Int64, + B2 = Tuple{Float64, T, T} where T <: Int64, + C = Tuple{Float64, Any, Vararg{Integer, N} where N} + + @test A == B1 == B2 + @test issub_strict(A, C) + @test issub_strict(B1, C) + @test issub_strict(B2, C) +end +let A = Tuple{Vararg{Val{N}, N} where N}, + B = Tuple{Vararg{Val{N}, N}} where N, + C = Tuple{Val{2}, Val{2}} + + @test isequal_type(A, B) + @test issub(C, B) + @test issub(C, A) +end +@test isequal_type(Tuple{T, Vararg{T, 2}} where T<:Real, Tuple{Vararg{T, 3}} where T<: Real) +@test !issub(Tuple{Vararg{T, 3}} where T<:Real, Tuple{Any, Any, Any, Any, Vararg{Any, N} where N}) +@test !issub(Tuple{Vararg{T, 3}} where T<:Real, Tuple{Any, Any, Any, Any, Vararg{Any, N}} where N) +@test issub_strict(Ref{Tuple{Int, Vararg{Int, N}}} where N, Ref{Tuple{Vararg{Int, N}}} where N) +let T31805 = Tuple{Type{Tuple{}}, Tuple{Vararg{Int8, A}}} where A, + S31805 = Tuple{Type{Tuple{Vararg{Int32, A}}}, Tuple{Vararg{Int16, A}}} where A + @test !issub(T31805, S31805) +end +@testintersect( + Tuple{Array{Tuple{Vararg{Int64,N}},N},Tuple{Vararg{Array{Int64,1},N}}} where N, + Tuple{Array{Tuple{Int64},1}, Tuple}, + Tuple{Array{Tuple{Int64},1},Tuple{Array{Int64,1}}}) + +# issue #32703 +struct Str{C} <: AbstractString +end +struct CSE{X} +end +const UTF16CSE = CSE{1} +const UTF16Str = Str{UTF16CSE} +const ASCIIStr = Str{CSE{2}} +c32703(::Type{<:Str{UTF16CSE}}, str::AbstractString) = 42 +c32703(::Type{<:Str{C}}, str::Str{C}) where {C<:CSE} = str + +@testintersect(Tuple{Type{UTF16Str},ASCIIStr}, + Tuple{Type{<:Str{C}}, Str{C}} where {C<:CSE}, + Union{}) +@test c32703(UTF16Str, ASCIIStr()) == 42 +@test_broken typeintersect(Tuple{Vector{Vector{Float32}},Matrix,Matrix}, + Tuple{Vector{V},Matrix{Int},Matrix{S}} where {S, V<:AbstractVector{S}}) == + Tuple{Array{Array{Float32,1},1},Array{Int,2},Array{Float32,2}} + +@testintersect(Tuple{Pair{Int, DataType}, Any}, + Tuple{Pair{A, B} where B<:Type, Int} where A, + Tuple{Pair{Int, DataType}, Int}) diff --git a/test/syntax.jl b/test/syntax.jl index 479ba13d4c3ca..2599d0d3ce79e 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -1563,6 +1563,23 @@ end return convert(B, b) end end) == Expr(:error, "local variable name \"B\" conflicts with a static parameter") +# issue #32620 +@test Meta.lower(@__MODULE__, quote + function foo(a::T) where {T} + for i = 1:1 + T = 0 + end + end +end) == Expr(:error, "local variable name \"T\" conflicts with a static parameter") +function f32620(x::T) where T + local y + let T = 3 + T = 2 + y = T + end + return (T, y) +end +@test f32620(0) === (Int, 2) # issue #28044 code28044(x) = 10x @@ -1875,3 +1892,14 @@ x32499 = begin S32499(x=2) end @test x32499 == 2 + +# issue #32467 +let f = identity(identity() do + x = 0 + @inbounds for i = 1:2 + x += i + end + x + end) + @test f() == 3 +end