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 @@
-
-
+
\ 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