diff --git a/base/broadcast.jl b/base/broadcast.jl index 1cb04fb6329bc..156a02891e388 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -180,7 +180,7 @@ function Broadcasted{Style}(f::F, args::Args, axes=nothing) where {Style, F, Arg end struct AndAnd end -andand = AndAnd() +const andand = AndAnd() broadcasted(::AndAnd, a, b) = broadcasted((a, b) -> a && b, a, b) function broadcasted(::AndAnd, a, bc::Broadcasted) bcf = flatten(bc) diff --git a/base/cmd.jl b/base/cmd.jl index 0d66cb932a04a..7d079e5efb986 100644 --- a/base/cmd.jl +++ b/base/cmd.jl @@ -230,7 +230,7 @@ byteenv(env::Union{AbstractVector{Pair{T,V}}, Tuple{Vararg{Pair{T,V}}}}) where { String[cstr(k*"="*string(v)) for (k,v) in env] """ - setenv(command::Cmd, env; dir="") + setenv(command::Cmd, env; dir) Set environment variables to use when running the given `command`. `env` is either a dictionary mapping strings to strings, an array of strings of the form `"var=val"`, or @@ -239,13 +239,15 @@ existing environment, create `env` through `copy(ENV)` and then setting `env["va as desired, or use [`addenv`](@ref). The `dir` keyword argument can be used to specify a working directory for the command. +`dir` defaults to the currently set `dir` for `command` (which is the current working +directory if not specified already). See also [`Cmd`](@ref), [`addenv`](@ref), [`ENV`](@ref), [`pwd`](@ref). """ -setenv(cmd::Cmd, env; dir="") = Cmd(cmd; env=byteenv(env), dir=dir) -setenv(cmd::Cmd, env::Pair{<:AbstractString}...; dir="") = +setenv(cmd::Cmd, env; dir=cmd.dir) = Cmd(cmd; env=byteenv(env), dir=dir) +setenv(cmd::Cmd, env::Pair{<:AbstractString}...; dir=cmd.dir) = setenv(cmd, env; dir=dir) -setenv(cmd::Cmd; dir="") = Cmd(cmd; dir=dir) +setenv(cmd::Cmd; dir=cmd.dir) = Cmd(cmd; dir=dir) """ addenv(command::Cmd, env...; inherit::Bool = true) diff --git a/base/error.jl b/base/error.jl index 0657865bb8831..83ff3981fe11f 100644 --- a/base/error.jl +++ b/base/error.jl @@ -128,7 +128,7 @@ struct ExceptionStack <: AbstractArray{Any,1} end """ - current_exceptions(task=current_task(); [inclue_bt=true]) + current_exceptions(task::Task=current_task(); [backtrace::Bool=true]) Get the stack of exceptions currently being handled. For nested catch blocks there may be more than one current exception in which case the most recently @@ -145,7 +145,7 @@ uncaught exceptions. This function went by the experimental name `catch_stack()` in Julia 1.1–1.6, and had a plain Vector-of-tuples as a return type. """ -function current_exceptions(task=current_task(); backtrace=true) +function current_exceptions(task::Task=current_task(); backtrace::Bool=true) raw = ccall(:jl_get_excstack, Any, (Any,Cint,Cint), task, backtrace, typemax(Cint))::Vector{Any} formatted = Any[] stride = backtrace ? 3 : 1 diff --git a/base/expr.jl b/base/expr.jl index 69097b5d33be4..436f26f124361 100644 --- a/base/expr.jl +++ b/base/expr.jl @@ -441,12 +441,16 @@ macro generated(f) if isa(f, Expr) && (f.head === :function || is_short_function_def(f)) body = f.args[2] lno = body.args[1] + tmp = gensym("tmp") return Expr(:escape, Expr(f.head, f.args[1], Expr(:block, lno, Expr(:if, Expr(:generated), - body, + # https://github.com/JuliaLang/julia/issues/25678 + Expr(:block, + :(local $tmp = $body), + :(if $tmp isa $(GlobalRef(Core, :CodeInfo)); return $tmp; else $tmp; end)), Expr(:block, Expr(:meta, :generated_only), Expr(:return, nothing)))))) @@ -464,7 +468,7 @@ Mark `var` or `ex` as being performed atomically, if `ex` is a supported express @atomic a.b.x = new @atomic a.b.x += addend - @atomic :acquire_release a.b.x = new + @atomic :release a.b.x = new @atomic :acquire_release a.b.x += addend Perform the store operation expressed on the right atomically and return the diff --git a/base/reducedim.jl b/base/reducedim.jl index c04a6c1b984f6..02b2345038bcc 100644 --- a/base/reducedim.jl +++ b/base/reducedim.jl @@ -139,7 +139,7 @@ for (f1, f2, initval, typeextreme) in ((:min, :max, :Inf, :typemax), (:max, :min if isempty(A1) # If the slice is empty just return non-view version as the initial array - return copy(A1) + return map(f, A1) else # otherwise use the min/max of the first slice as initial value v0 = mapreduce(f, $f2, A1) @@ -148,9 +148,9 @@ for (f1, f2, initval, typeextreme) in ((:min, :max, :Inf, :typemax), (:max, :min Tr = v0 isa T ? T : typeof(v0) # but NaNs and missing need to be avoided as initial values - if (v0 == v0) === false + if v0 isa Number && isnan(v0) # v0 is NaN - v0 = $initval + v0 = oftype(v0, $initval) elseif isunordered(v0) # v0 is missing or a third-party unordered value Tnm = nonmissingtype(Tr) diff --git a/base/util.jl b/base/util.jl index 08827665635eb..7b342e3283684 100644 --- a/base/util.jl +++ b/base/util.jl @@ -622,6 +622,9 @@ function runtests(tests = ["all"]; ncores::Int = ceil(Int, Sys.CPU_THREADS::Int seed !== nothing && push!(tests, "--seed=0x$(string(seed % UInt128, base=16))") # cast to UInt128 to avoid a minus sign ENV2 = copy(ENV) ENV2["JULIA_CPU_THREADS"] = "$ncores" + ENV2["JULIA_DEPOT_PATH"] = mktempdir(; cleanup = true) + delete!(ENV2, "JULIA_LOAD_PATH") + delete!(ENV2, "JULIA_PROJECT") try run(setenv(`$(julia_cmd()) $(joinpath(Sys.BINDIR::String, Base.DATAROOTDIR, "julia", "test", "runtests.jl")) $tests`, ENV2)) diff --git a/cli/loader_lib.c b/cli/loader_lib.c index 1a6ef5ab19087..dca408e94e3d3 100644 --- a/cli/loader_lib.c +++ b/cli/loader_lib.c @@ -182,6 +182,18 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) { } (*jl_exported_func_addrs[symbol_idx]) = addr; } + // Next, if we're on Linux/FreeBSD, set up fast TLS. +#if !defined(_OS_WINDOWS_) && !defined(_OS_DARWIN_) + void (*jl_pgcstack_setkey)(void*, void*(*)(void)) = lookup_symbol(libjulia_internal, "jl_pgcstack_setkey"); + if (jl_pgcstack_setkey == NULL) { + jl_loader_print_stderr("ERROR: Cannot find jl_pgcstack_setkey() function within libjulia-internal!\n"); + exit(1); + } + void *fptr = lookup_symbol(RTLD_DEFAULT, "jl_get_pgcstack_static"); + void *(*key)(void) = lookup_symbol(RTLD_DEFAULT, "jl_pgcstack_addr_static"); + if (fptr != NULL && key != NULL) + jl_pgcstack_setkey(fptr, key); +#endif // jl_options must be initialized very early, in case an embedder sets some // values there before calling jl_init @@ -199,22 +211,6 @@ JL_DLLEXPORT int jl_load_repl(int argc, char * argv[]) { exit(1); } } - // Next, if we're on Linux/FreeBSD, set up fast TLS. -#if !defined(_OS_WINDOWS_) && !defined(_OS_DARWIN_) - void (*jl_pgcstack_setkey)(void*, void*(*)(void)) = lookup_symbol(libjulia_internal, "jl_pgcstack_setkey"); - if (jl_pgcstack_setkey == NULL) { - jl_loader_print_stderr("ERROR: Cannot find jl_pgcstack_setkey() function within libjulia-internal!\n"); - exit(1); - } - void *fptr = lookup_symbol(RTLD_DEFAULT, "jl_get_pgcstack_static"); - void *(*key)(void) = lookup_symbol(RTLD_DEFAULT, "jl_pgcstack_addr_static"); - if (fptr == NULL || key == NULL) { - jl_loader_print_stderr("ERROR: Cannot find jl_get_pgcstack_static(), must define this symbol within calling executable!\n"); - exit(1); - } - jl_pgcstack_setkey(fptr, key); -#endif - // Load the repl entrypoint symbol and jump into it! int (*entrypoint)(int, char **) = (int (*)(int, char **))lookup_symbol(libjulia_internal, "jl_repl_entrypoint"); if (entrypoint == NULL) { diff --git a/contrib/generate_precompile.jl b/contrib/generate_precompile.jl index bb760f70ab867..6915ecfc4b99a 100644 --- a/contrib/generate_precompile.jl +++ b/contrib/generate_precompile.jl @@ -417,6 +417,7 @@ generate_precompile_statements() # As a last step in system image generation, # remove some references to build time environment for a more reproducible build. +Base.Filesystem.temp_cleanup_purge(force=true) @eval Base PROGRAM_FILE = "" @eval Sys begin BINDIR = "" diff --git a/src/Makefile b/src/Makefile index 479d1652f6808..1a9af2fa7c439 100644 --- a/src/Makefile +++ b/src/Makefile @@ -119,6 +119,7 @@ endif CLANG_LDFLAGS := $(LLVM_LDFLAGS) ifeq ($(OS), Darwin) CLANG_LDFLAGS += -Wl,-undefined,dynamic_lookup +OSLIBS += $(SRCDIR)/mach_dyld_atfork.tbd endif COMMON_LIBPATHS := -L$(build_libdir) -L$(build_shlibdir) diff --git a/src/builtins.c b/src/builtins.c index 01723bf81733e..126b96ef44ebc 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -861,9 +861,9 @@ JL_CALLABLE(jl_f_getfield) enum jl_memory_order order = jl_memory_order_unspecified; JL_NARGS(getfield, 2, 4); if (nargs == 4) { - JL_TYPECHK(getfield, symbol, args[3]); - JL_TYPECHK(getfield, bool, args[4]); - order = jl_get_atomic_order_checked((jl_sym_t*)args[3], 1, 0); + JL_TYPECHK(getfield, symbol, args[2]); + JL_TYPECHK(getfield, bool, args[3]); + order = jl_get_atomic_order_checked((jl_sym_t*)args[2], 1, 0); } else if (nargs == 3) { if (!jl_is_bool(args[2])) { diff --git a/src/debuginfo.cpp b/src/debuginfo.cpp index ad9ed659cbe0d..fc9bc4535e86a 100644 --- a/src/debuginfo.cpp +++ b/src/debuginfo.cpp @@ -1077,6 +1077,14 @@ bool jl_dylib_DI_for_fptr(size_t pointer, object::SectionRef *Section, int64_t * struct link_map *extra_info; dladdr_success = dladdr1((void*)pointer, &dlinfo, (void**)&extra_info, RTLD_DL_LINKMAP) != 0; #else +#ifdef _OS_DARWIN_ + // On macOS 12, dladdr(-1, …) succeeds and returns the main executable image, + // despite there never actually being an image there. This is not what we want, + // as we use -1 as a known-invalid value e.g. in the test suite. + if (pointer == ~(size_t)0) { + return false; + } +#endif dladdr_success = dladdr((void*)pointer, &dlinfo) != 0; #endif if (!dladdr_success || !dlinfo.dli_fname) diff --git a/src/llvm-final-gc-lowering.cpp b/src/llvm-final-gc-lowering.cpp index bc68edda2cad7..b65cd26f59865 100644 --- a/src/llvm-final-gc-lowering.cpp +++ b/src/llvm-final-gc-lowering.cpp @@ -218,10 +218,11 @@ bool FinalLowerGC::doInitialization(Module &M) { bool FinalLowerGC::doFinalization(Module &M) { + GlobalValue *functionList[] = {queueRootFunc, poolAllocFunc, bigAllocFunc}; + queueRootFunc = poolAllocFunc = bigAllocFunc = nullptr; auto used = M.getGlobalVariable("llvm.compiler.used"); if (!used) return false; - GlobalValue *functionList[] = {queueRootFunc, poolAllocFunc, bigAllocFunc}; SmallPtrSet InitAsSet( functionList, functionList + sizeof(functionList) / sizeof(void*)); diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index d47f6e3d0b114..ab7f011ffcf05 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -1129,12 +1129,14 @@ static bool isConstGV(GlobalVariable *gv) return gv->isConstant() || gv->getMetadata("julia.constgv"); } -static bool isLoadFromConstGV(LoadInst *LI, bool &task_local); -static bool isLoadFromConstGV(Value *v, bool &task_local) +typedef llvm::SmallPtrSet PhiSet; + +static bool isLoadFromConstGV(LoadInst *LI, bool &task_local, PhiSet *seen = nullptr); +static bool isLoadFromConstGV(Value *v, bool &task_local, PhiSet *seen = nullptr) { v = v->stripInBoundsOffsets(); if (auto LI = dyn_cast(v)) - return isLoadFromConstGV(LI, task_local); + return isLoadFromConstGV(LI, task_local, seen); if (auto gv = dyn_cast(v)) return isConstGV(gv); // null pointer @@ -1145,12 +1147,19 @@ static bool isLoadFromConstGV(Value *v, bool &task_local) return (CE->getOpcode() == Instruction::IntToPtr && isa(CE->getOperand(0))); if (auto SL = dyn_cast(v)) - return (isLoadFromConstGV(SL->getTrueValue(), task_local) && - isLoadFromConstGV(SL->getFalseValue(), task_local)); + return (isLoadFromConstGV(SL->getTrueValue(), task_local, seen) && + isLoadFromConstGV(SL->getFalseValue(), task_local, seen)); if (auto Phi = dyn_cast(v)) { + PhiSet ThisSet(&Phi, &Phi); + if (!seen) + seen = &ThisSet; + else if (seen->count(Phi)) + return true; + else + seen->insert(Phi); auto n = Phi->getNumIncomingValues(); for (unsigned i = 0; i < n; ++i) { - if (!isLoadFromConstGV(Phi->getIncomingValue(i), task_local)) { + if (!isLoadFromConstGV(Phi->getIncomingValue(i), task_local, seen)) { return false; } } @@ -1182,7 +1191,7 @@ static bool isLoadFromConstGV(Value *v, bool &task_local) // // The white list implemented here and above in `isLoadFromConstGV(Value*)` should // cover all the cases we and LLVM generates. -static bool isLoadFromConstGV(LoadInst *LI, bool &task_local) +static bool isLoadFromConstGV(LoadInst *LI, bool &task_local, PhiSet *seen) { // We only emit single slot GV in codegen // but LLVM global merging can change the pointer operands to GEPs/bitcasts @@ -1192,7 +1201,7 @@ static bool isLoadFromConstGV(LoadInst *LI, bool &task_local) {"jtbaa_immut", "jtbaa_const", "jtbaa_datatype"})) { if (gv) return true; - return isLoadFromConstGV(load_base, task_local); + return isLoadFromConstGV(load_base, task_local, seen); } if (gv) return isConstGV(gv); @@ -2237,6 +2246,18 @@ bool LateLowerGCFrame::CleanupIR(Function &F, State *S) { I->setMetadata(LLVMContext::MD_tbaa, MutableTBAA); } } + // FCA chains created by SROA start with an undef value + // if the type contains an tracked pointer that can lead to a partial + // initialisation and LateLower might have inserted an extractvalue + // of an undef field. Fix this by changing it to start with an zero-init + if (auto *IV = dyn_cast(*&it)) { + Value *SourceAggregate = IV->getAggregateOperand(); + if (isa(SourceAggregate)) { + IV->setOperand(IV->getAggregateOperandIndex(), ConstantAggregateZero::get(IV->getType())); + ChangesMade = true; + } + } + auto *CI = dyn_cast(&*it); if (!CI) { ++it; diff --git a/src/mach_dyld_atfork.tbd b/src/mach_dyld_atfork.tbd new file mode 100644 index 0000000000000..9a5d18099dbcf --- /dev/null +++ b/src/mach_dyld_atfork.tbd @@ -0,0 +1,25 @@ +--- !tapi-tbd +# copied from XCode's libSystem.tbd (current-version: 1311) +# to provide weak-linkage info for new symbols on old systems +tbd-version: 4 +targets: [ x86_64-macos, x86_64-maccatalyst, arm64-macos, arm64-maccatalyst, + arm64e-macos, arm64e-maccatalyst ] +uuids: + - target: x86_64-macos + value: AFE6C76A-B47A-35F5-91D0-4E9FC439E90D + - target: x86_64-maccatalyst + value: AFE6C76A-B47A-35F5-91D0-4E9FC439E90D + - target: arm64-macos + value: 2EA09BDB-811B-33AA-BB58-4B53AA2DB522 + - target: arm64-maccatalyst + value: 2EA09BDB-811B-33AA-BB58-4B53AA2DB522 + - target: arm64e-macos + value: 09AB3723-C26D-3762-93BA-98E9C38B89C1 + - target: arm64e-maccatalyst + value: 09AB3723-C26D-3762-93BA-98E9C38B89C1 +install-name: '/usr/lib/libSystem.B.dylib' +exports: + - targets: [ arm64-macos, arm64e-macos, x86_64-macos, x86_64-maccatalyst, + arm64-maccatalyst, arm64e-maccatalyst ] + symbols: [ __dyld_atfork_parent, __dyld_atfork_prepare ] +... diff --git a/src/signals-mach.c b/src/signals-mach.c index 93da46f7cef1e..fa583493a410d 100644 --- a/src/signals-mach.c +++ b/src/signals-mach.c @@ -31,6 +31,11 @@ extern void *_keymgr_get_and_lock_processwide_ptr(unsigned int key); extern int _keymgr_get_and_lock_processwide_ptr_2(unsigned int key, void **result); extern int _keymgr_set_lockmode_processwide_ptr(unsigned int key, unsigned int mode); +// private dyld3/dyld4 stuff +extern void _dyld_atfork_prepare(void) __attribute__((weak_import)); +extern void _dyld_atfork_parent(void) __attribute__((weak_import)); +//extern void _dyld_fork_child(void) __attribute__((weak_import)); + static void attach_exception_port(thread_port_t thread, int segv_only); // low 16 bits are the thread id, the next 8 bits are the original gc_state @@ -521,6 +526,31 @@ static kern_return_t profiler_segv_handler } #endif +// WARNING: we are unable to handle sigsegv while the dlsymlock is held +static int jl_lock_profile_mach(int dlsymlock) +{ + jl_lock_profile(); + // workaround for old keymgr bugs + void *unused = NULL; + int keymgr_locked = _keymgr_get_and_lock_processwide_ptr_2(KEYMGR_GCC3_DW2_OBJ_LIST, &unused) == 0; + // workaround for new dlsym4 bugs (API and bugs introduced in macOS 12.1) + if (dlsymlock && _dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) + _dyld_atfork_prepare(); + return keymgr_locked; +} + +static void jl_unlock_profile_mach(int dlsymlock, int keymgr_locked) +{ + if (dlsymlock && _dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) \ + _dyld_atfork_parent(); \ + if (keymgr_locked) + _keymgr_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST); + jl_unlock_profile(); +} + +#define jl_lock_profile() int keymgr_locked = jl_lock_profile_mach(1) +#define jl_unlock_profile() jl_unlock_profile_mach(1, keymgr_locked) + void *mach_profile_listener(void *arg) { (void)arg; @@ -538,9 +568,7 @@ void *mach_profile_listener(void *arg) HANDLE_MACH_ERROR("mach_msg", ret); // sample each thread, round-robin style in reverse order // (so that thread zero gets notified last) - jl_lock_profile(); - void *unused = NULL; - int keymgr_locked = _keymgr_get_and_lock_processwide_ptr_2(KEYMGR_GCC3_DW2_OBJ_LIST, &unused) == 0; + int keymgr_locked = jl_lock_profile_mach(0); for (i = jl_n_threads; i-- > 0; ) { // if there is no space left, break early if (jl_profile_is_buffer_full()) { @@ -548,9 +576,13 @@ void *mach_profile_listener(void *arg) break; } + if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) + _dyld_atfork_prepare(); // briefly acquire the dlsym lock host_thread_state_t state; jl_thread_suspend_and_get_state2(i, &state); unw_context_t *uc = (unw_context_t*)&state; + if (_dyld_atfork_prepare != NULL && _dyld_atfork_parent != NULL) + _dyld_atfork_parent(); // quickly release the dlsym lock if (running) { #ifdef LLVMLIBUNWIND @@ -593,9 +625,7 @@ void *mach_profile_listener(void *arg) // We're done! Resume the thread. jl_thread_resume(i, 0); } - if (keymgr_locked) - _keymgr_unlock_processwide_ptr(KEYMGR_GCC3_DW2_OBJ_LIST); - jl_unlock_profile(); + jl_unlock_profile_mach(0, keymgr_locked); if (running) { // Reset the alarm kern_return_t ret = clock_alarm(clk, TIME_RELATIVE, timerprof, profile_port); diff --git a/src/support/win32_ucontext.c b/src/support/win32_ucontext.c index df50eb209341e..c67bcdf642e4d 100644 --- a/src/support/win32_ucontext.c +++ b/src/support/win32_ucontext.c @@ -73,7 +73,7 @@ void jl_makecontext(win32_ucontext_t *ucp, void (*func)(void)) jmpbuf->Rip = (unsigned long long)func; jmpbuf->Rsp = (unsigned long long)stack_top; jmpbuf->Rbp = 0; - jmpbuf->Frame = 0; // SEH frame + jmpbuf->Frame = ~(uint64_t)0; // SEH frame #elif defined(_CPU_X86_) jmpbuf->Eip = (unsigned long)func; jmpbuf->Esp = (unsigned long)stack_top; diff --git a/stdlib/Dates/src/io.jl b/stdlib/Dates/src/io.jl index 1901661fff0e2..ba0958b57f613 100644 --- a/stdlib/Dates/src/io.jl +++ b/stdlib/Dates/src/io.jl @@ -332,6 +332,23 @@ const CONVERSION_TRANSLATIONS = IdDict{Type, Any}( Time => (Hour, Minute, Second, Millisecond, Microsecond, Nanosecond, AMPM), ) +# The `DateFormat(format, locale)` method just below consumes the following Regex. +# Constructing this Regex is fairly expensive; doing so in the method itself can +# consume half or better of `DateFormat(format, locale)`'s runtime. So instead we +# construct and cache it outside the method body. Note, however, that when +# `keys(CONVERSION_SPECIFIERS)` changes, the cached Regex must be updated accordingly; +# hence the mutability (Ref-ness) of the cache, the helper method with which to populate +# the cache, the cache of the hash of `keys(CONVERSION_SPECIFIERS)` (to facilitate checking +# for changes), and the lock (to maintain consistency of these objects across threads when +# threads simultaneously modify `CONVERSION_SPECIFIERS` and construct `DateFormat`s). +function compute_dateformat_regex(conversion_specifiers) + letters = String(collect(keys(conversion_specifiers))) + return Regex("(? DateFormat @@ -379,8 +396,20 @@ function DateFormat(f::AbstractString, locale::DateLocale=ENGLISH) prev = () prev_offset = 1 - letters = String(collect(keys(CONVERSION_SPECIFIERS))) - for m in eachmatch(Regex("(? s"\1") if !isempty(prev) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index 289b927c44212..1dcc10127372b 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -193,7 +193,9 @@ function modules_to_be_loaded(ast::Expr, mods::Vector{Symbol} = Symbol[]) end end for arg in ast.args - arg isa Expr && modules_to_be_loaded(arg, mods) + if arg isa Expr && arg.head in [:block, :if, :using, :import] + modules_to_be_loaded(arg, mods) + end end filter!(mod -> !in(String(mod), ["Base", "Main", "Core"]), mods) # Exclude special non-package modules return unique(mods) diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index 3fbf6d8825bba..03f6856720b1a 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -1364,5 +1364,14 @@ end mods = REPL.modules_to_be_loaded(Base.parse_input_line("Foo")) @test isempty(mods) + + mods = REPL.modules_to_be_loaded(Base.parse_input_line("@eval using Foo")) + @test isempty(mods) + mods = REPL.modules_to_be_loaded(Base.parse_input_line("begin using Foo; @eval using Bar end")) + @test mods == [:Foo] + mods = REPL.modules_to_be_loaded(Base.parse_input_line("Core.eval(Main,\"using Foo\")")) + @test isempty(mods) + mods = REPL.modules_to_be_loaded(Base.parse_input_line("begin using Foo; Core.eval(Main,\"using Foo\") end")) + @test mods == [:Foo] end end diff --git a/stdlib/SharedArrays/src/SharedArrays.jl b/stdlib/SharedArrays/src/SharedArrays.jl index 85f1eb4fff150..8a111957da4f9 100644 --- a/stdlib/SharedArrays/src/SharedArrays.jl +++ b/stdlib/SharedArrays/src/SharedArrays.jl @@ -693,9 +693,15 @@ function _shm_mmap_array(T, dims, shm_seg_name, mode) end shm_unlink(shm_seg_name) = ccall(:shm_unlink, Cint, (Cstring,), shm_seg_name) -shm_open(shm_seg_name, oflags, permissions) = ccall(:shm_open, Cint, - (Cstring, Cint, Base.Cmode_t), shm_seg_name, oflags, permissions) - +function shm_open(shm_seg_name, oflags, permissions) + # On macOS, `shm_open()` is a variadic function, so to properly match + # calling ABI, we must declare our arguments as variadic as well. + @static if Sys.isapple() + return ccall(:shm_open, Cint, (Cstring, Cint, Base.Cmode_t...), shm_seg_name, oflags, permissions) + else + return ccall(:shm_open, Cint, (Cstring, Cint, Base.Cmode_t), shm_seg_name, oflags, permissions) + end +end end # os-test end # module diff --git a/stdlib/SparseArrays/src/sparsematrix.jl b/stdlib/SparseArrays/src/sparsematrix.jl index 62243264baa99..52ae19fd53709 100644 --- a/stdlib/SparseArrays/src/sparsematrix.jl +++ b/stdlib/SparseArrays/src/sparsematrix.jl @@ -2818,6 +2818,7 @@ end # Nonscalar A[I,J] = B: Convert B to a SparseMatrixCSC of the appropriate shape first _to_same_csc(::AbstractSparseMatrixCSC{Tv, Ti}, V::AbstractMatrix, I...) where {Tv,Ti} = convert(SparseMatrixCSC{Tv,Ti}, V) +_to_same_csc(::AbstractSparseMatrixCSC{Tv, Ti}, V::AbstractMatrix, i::Integer, J) where {Tv,Ti} = convert(SparseMatrixCSC{Tv,Ti}, reshape(V, (1, length(J)))) _to_same_csc(::AbstractSparseMatrixCSC{Tv, Ti}, V::AbstractVector, I...) where {Tv,Ti} = convert(SparseMatrixCSC{Tv,Ti}, reshape(V, map(length, I))) setindex!(A::AbstractSparseMatrixCSC{Tv}, B::AbstractVecOrMat, I::Integer, J::Integer) where {Tv} = _setindex_scalar!(A, B, I, J) @@ -2826,12 +2827,20 @@ function setindex!(A::AbstractSparseMatrixCSC{Tv,Ti}, V::AbstractVecOrMat, Ix::U require_one_based_indexing(A, V, Ix, Jx) (I, J) = Base.ensure_indexable(to_indices(A, (Ix, Jx))) checkbounds(A, I, J) - Base.setindex_shape_check(V, length(I), length(J)) + nJ = length(J) + Base.setindex_shape_check(V, length(I), nJ) B = _to_same_csc(A, V, I, J) + m, n = size(A) + if (!isempty(I) && (I[1] < 1 || I[end] > m)) || (!isempty(J) && (J[1] < 1 || J[end] > n)) + throw(BoundsError(A, (I, J))) + end + if isempty(I) || isempty(J) + return A + end + issortedI = issorted(I) issortedJ = issorted(J) - if !issortedI && !issortedJ pI = sortperm(I); @inbounds I = I[pI] pJ = sortperm(J); @inbounds J = J[pJ] @@ -2844,20 +2853,6 @@ function setindex!(A::AbstractSparseMatrixCSC{Tv,Ti}, V::AbstractVecOrMat, Ix::U B = B[:, pJ] end - m, n = size(A) - mB, nB = size(B) - - if (!isempty(I) && (I[1] < 1 || I[end] > m)) || (!isempty(J) && (J[1] < 1 || J[end] > n)) - throw(BoundsError(A, (I, J))) - end - - if isempty(I) || isempty(J) - return A - end - - nI = length(I) - nJ = length(J) - colptrA = getcolptr(A); rowvalA = rowvals(A); nzvalA = nonzeros(A) colptrB = getcolptr(B); rowvalB = rowvals(B); nzvalB = nonzeros(B) @@ -2871,7 +2866,6 @@ function setindex!(A::AbstractSparseMatrixCSC{Tv,Ti}, V::AbstractVecOrMat, Ix::U resize!(nzvalA, nnzS) colB = 1 - asgn_col = J[colB] I_asgn = falses(m) fill!(view(I_asgn, I), true) diff --git a/test/arrayops.jl b/test/arrayops.jl index a37d93ee90473..bc91900a3fec0 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -2938,3 +2938,18 @@ end @test [fill(1); fill(2, (2,1,1))] == reshape([1; 2; 2], (3, 1, 1)) @test_throws DimensionMismatch [fill(1); rand(2, 2, 2)] end + +@testset "Allow assignment of singleton array to sparse array #43644" begin + K = spzeros(3,3) + b = zeros(3,3) + b[3,:] = [1,2,3] + K[3,1:3] += [1.0 2.0 3.0]' + @test K == b + K[3:3,1:3] += zeros(1, 3) + @test K == b + K[3,1:3] += zeros(3) + @test K == b + K[3,:] += zeros(3,1) + @test K == b + @test_throws DimensionMismatch K[3,1:2] += [1.0 2.0 3.0]' +end diff --git a/test/atomics.jl b/test/atomics.jl index c53471ed0da26..15ffd84a2c0a2 100644 --- a/test/atomics.jl +++ b/test/atomics.jl @@ -370,3 +370,14 @@ let a = ARefxy(1, -1) @test_throws ConcurrencyViolationError @atomicreplace :not_atomic a.x xchg @test_throws ConcurrencyViolationError @atomicreplace :monotonic :acquire a.x xchg end + +# atomic getfield with boundcheck +# via codegen +getx(a, boundcheck) = getfield(a, :x, :sequentially_consistent, boundcheck) +@test getx(ARefxy{Any}(42, 42), true) == 42 +@test getx(ARefxy{Any}(42, 42), false) == 42 +# via interpreter +ans = getfield(ARefxy{Any}(42, 42), :x, :sequentially_consistent, true) +@test ans == 42 +ans = getfield(ARefxy{Any}(42, 42), :x, :sequentially_consistent, false) +@test ans == 42 diff --git a/test/broadcast.jl b/test/broadcast.jl index 2a68b261ec3dd..1f070330126ef 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -1054,3 +1054,12 @@ end @test Broadcast.BroadcastFunction(+)(2:3, 2:3) == 4:2:6 @test Broadcast.BroadcastFunction(+)(2:3, 2:3) isa AbstractRange end + +@testset "Fix type unstable .&& #43470" begin + function test(x, y) + return (x .> 0.0) .&& (y .> 0.0) + end + x = randn(2) + y = randn(2) + @inferred(test(x, y)) == [0, 0] +end diff --git a/test/cmdlineargs.jl b/test/cmdlineargs.jl index fb206acf03477..8d85b1a1cc5c8 100644 --- a/test/cmdlineargs.jl +++ b/test/cmdlineargs.jl @@ -119,7 +119,7 @@ let exename = `$(Base.julia_cmd()) --startup-file=no --color=no` # handling of @projectname in --project and JULIA_PROJECT let expanded = abspath(Base.load_path_expand("@foo")) @test expanded == readchomp(`$exename --project='@foo' -e 'println(Base.active_project())'`) - @test expanded == readchomp(setenv(`$exename -e 'println(Base.active_project())'`, "JULIA_PROJECT" => "@foo", "HOME" => homedir())) + @test expanded == readchomp(addenv(`$exename -e 'println(Base.active_project())'`, "JULIA_PROJECT" => "@foo", "HOME" => homedir())) end # --quiet, --banner diff --git a/test/core.jl b/test/core.jl index 74edc7cddf7f4..3561762023ab8 100644 --- a/test/core.jl +++ b/test/core.jl @@ -3516,9 +3516,10 @@ end @test_throws TypeError Union{Int, 1} @test_throws ErrorException Vararg{Any,-2} -@test_throws ErrorException Vararg{Int, N} where N<:T where T -@test_throws ErrorException Vararg{Int, N} where N<:Integer -@test_throws ErrorException Vararg{Int, N} where N>:Integer +# Disabled due to #39698, see src/jltypes.c +#@test_throws ErrorException Vararg{Int, N} where N<:T where T +#@test_throws ErrorException Vararg{Int, N} where N<:Integer +#@test_throws ErrorException Vararg{Int, N} where N>:Integer mutable struct FooNTuple{N} z::Tuple{Integer, Vararg{Int, N}} diff --git a/test/reducedim.jl b/test/reducedim.jl index 93287efc5eb1c..9c4dff6b80bcd 100644 --- a/test/reducedim.jl +++ b/test/reducedim.jl @@ -444,8 +444,8 @@ end @testset "argmin/argmax" begin B = reshape(3^3:-1:1, (3, 3, 3)) - @test B[argmax(B, dims=[2, 3])] == maximum(B, dims=[2, 3]) - @test B[argmin(B, dims=[2, 3])] == minimum(B, dims=[2, 3]) + @test B[argmax(B, dims=[2, 3])] == @inferred(maximum(B, dims=[2, 3])) + @test B[argmin(B, dims=[2, 3])] == @inferred(minimum(B, dims=[2, 3])) end @testset "in-place reductions with mismatched dimensionalities" begin @@ -505,3 +505,7 @@ end @test r_red == [3] end end + +@testset "type stability (issue #43461)" begin + @test (@inferred maximum(Float64, reshape(1:4,2,:); dims = 2)) == reshape([3,4],2,1) +end diff --git a/test/spawn.jl b/test/spawn.jl index fe6912faf0447..24cd6dad1fa1d 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -805,6 +805,25 @@ end end end +@testset "setenv with dir (with tests for #42131)" begin + dir1 = joinpath(pwd(), "dir1") + dir2 = joinpath(pwd(), "dir2") + cmd = Cmd(`julia`; dir=dir1) + @test cmd.dir == dir1 + @test Cmd(cmd).dir == dir1 + @test Cmd(cmd; dir=dir2).dir == dir2 + @test Cmd(cmd; dir="").dir == "" + @test setenv(cmd).dir == dir1 + @test setenv(cmd; dir=dir2).dir == dir2 + @test setenv(cmd; dir="").dir == "" + @test setenv(cmd, "FOO"=>"foo").dir == dir1 + @test setenv(cmd, "FOO"=>"foo"; dir=dir2).dir == dir2 + @test setenv(cmd, "FOO"=>"foo"; dir="").dir == "" + @test setenv(cmd, Dict("FOO"=>"foo")).dir == dir1 + @test setenv(cmd, Dict("FOO"=>"foo"); dir=dir2).dir == dir2 + @test setenv(cmd, Dict("FOO"=>"foo"); dir="").dir == "" +end + # clean up busybox download if Sys.iswindows() diff --git a/test/syntax.jl b/test/syntax.jl index afea3b6fcb2e1..6e0922a9c1331 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -2954,6 +2954,21 @@ end @test f() == (2, 3) end +# issue 25678: module of name `Core` +# https://github.com/JuliaLang/julia/pull/40778/files#r784416018 +@test @eval Module() begin + Core = 1 + @generated f() = 1 + f() == 1 +end + +# issue 25678: argument of name `tmp` +# https://github.com/JuliaLang/julia/pull/43823#discussion_r785365312 +@test @eval Module() begin + @generated f(tmp) = tmp + f(1) === Int +end + # issue 42220 macro m42220() return quote @@ -2982,3 +2997,10 @@ function checkUserAccess(u::User) return false end """) + +# issue 25678 +@generated f25678(x::T) where {T} = code_lowered(sin, Tuple{x})[] +@test f25678(pi/6) === sin(pi/6) + +@generated g25678(x) = return :x +@test g25678(7) === 7