From 78f810bf81669c2ce0f4a45cd77ca6351a4196e4 Mon Sep 17 00:00:00 2001 From: Oscar Smith Date: Sat, 22 Mar 2025 21:55:11 -0400 Subject: [PATCH 01/13] fix `mod` for mixes of `Signed` and `Unsigned` (#57853) Previously this was just overfowing producing wrong answers (both for the sign convention and just the wrong modulo class) fixes https://github.com/JuliaLang/julia/issues/57851 (cherry picked from commit 056891701e8ec629834def549f565cf80c46ccf9) --- base/int.jl | 10 ++++++++-- test/int.jl | 17 +++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/base/int.jl b/base/int.jl index 9bd4df6bd3866..24b7abc646281 100644 --- a/base/int.jl +++ b/base/int.jl @@ -286,8 +286,14 @@ function mod(x::T, y::T) where T<:Integer y == -1 && return T(0) # avoid potential overflow in fld return x - fld(x, y) * y end -mod(x::BitSigned, y::Unsigned) = rem(y + unsigned(rem(x, y)), y) -mod(x::Unsigned, y::Signed) = rem(y + signed(rem(x, y)), y) +function mod(x::BitSigned, y::Unsigned) + remval = rem(x, y) # correct iff remval>=0 + return unsigned(remval + (remval0 so correct iff y>0 or remval==0 + return remval + (!iszero(remval) && y Date: Sun, 23 Mar 2025 11:49:22 -0400 Subject: [PATCH 02/13] REPL: call display on the backend (#57773) (cherry picked from commit c75cf3ff62baf98edefa95e05307a6ea525eb6e3) --- stdlib/REPL/src/REPL.jl | 84 +++++++++++++++++++++++++++------------- stdlib/REPL/test/repl.jl | 2 +- 2 files changed, 59 insertions(+), 27 deletions(-) diff --git a/stdlib/REPL/src/REPL.jl b/stdlib/REPL/src/REPL.jl index c621fbbb0836e..ae2fec4ee10e0 100644 --- a/stdlib/REPL/src/REPL.jl +++ b/stdlib/REPL/src/REPL.jl @@ -120,6 +120,22 @@ mutable struct REPLBackend end REPLBackend() = REPLBackend(Channel(1), Channel(1), false) +# A reference to a backend that is not mutable +struct REPLBackendRef + repl_channel::Channel{Any} + response_channel::Channel{Any} +end +REPLBackendRef(backend::REPLBackend) = REPLBackendRef(backend.repl_channel, backend.response_channel) + +function destroy(ref::REPLBackendRef, state::Task) + if istaskfailed(state) + close(ref.repl_channel, TaskFailedException(state)) + close(ref.response_channel, TaskFailedException(state)) + end + close(ref.repl_channel) + close(ref.response_channel) +end + """ softscope(ex) @@ -418,12 +434,23 @@ function repl_backend_loop(backend::REPLBackend, get_module::Function) while true tls = task_local_storage() tls[:SOURCE_PATH] = nothing - ast, show_value = take!(backend.repl_channel) + ast_or_func, show_value = take!(backend.repl_channel) if show_value == -1 # exit flag break end - eval_user_input(ast, backend, get_module()) + if show_value == 2 # 2 indicates a function to be called + f = ast_or_func + try + ret = f() + put!(backend.response_channel, Pair{Any, Bool}(ret, false)) + catch err + put!(backend.response_channel, Pair{Any, Bool}(err, true)) + end + else + ast = ast_or_func + eval_user_input(ast, backend, get_module()) + end end return nothing end @@ -526,7 +553,7 @@ function print_response(repl::AbstractREPL, response, show_value::Bool, have_col repl.waserror = response[2] with_repl_linfo(repl) do io io = IOContext(io, :module => Base.active_module(repl)::Module) - print_response(io, response, show_value, have_color, specialdisplay(repl)) + print_response(io, response, backend(repl), show_value, have_color, specialdisplay(repl)) end return nothing end @@ -543,7 +570,7 @@ function repl_display_error(errio::IO, @nospecialize errval) return nothing end -function print_response(errio::IO, response, show_value::Bool, have_color::Bool, specialdisplay::Union{AbstractDisplay,Nothing}=nothing) +function print_response(errio::IO, response, backend::Union{REPLBackendRef,Nothing}, show_value::Bool, have_color::Bool, specialdisplay::Union{AbstractDisplay,Nothing}=nothing) Base.sigatomic_begin() val, iserr = response while true @@ -555,15 +582,19 @@ function print_response(errio::IO, response, show_value::Bool, have_color::Bool, repl_display_error(errio, val) else if val !== nothing && show_value - try - if specialdisplay === nothing + val2, iserr = if specialdisplay === nothing + # display calls may require being run on the main thread + eval_with_backend(backend) do Base.invokelatest(display, val) - else + end + else + eval_with_backend(backend) do Base.invokelatest(display, specialdisplay, val) end - catch + end + if iserr println(errio, "Error showing value of type ", typeof(val), ":") - rethrow() + throw(val2) end end end @@ -593,21 +624,7 @@ function print_response(errio::IO, response, show_value::Bool, have_color::Bool, nothing end -# A reference to a backend that is not mutable -struct REPLBackendRef - repl_channel::Channel{Any} - response_channel::Channel{Any} -end -REPLBackendRef(backend::REPLBackend) = REPLBackendRef(backend.repl_channel, backend.response_channel) -function destroy(ref::REPLBackendRef, state::Task) - if istaskfailed(state) - close(ref.repl_channel, TaskFailedException(state)) - close(ref.response_channel, TaskFailedException(state)) - end - close(ref.repl_channel) - close(ref.response_channel) -end """ run_repl(repl::AbstractREPL) @@ -1128,12 +1145,27 @@ find_hist_file() = get(ENV, "JULIA_HISTORY", !isempty(DEPOT_PATH) ? joinpath(DEPOT_PATH[1], "logs", "repl_history.jl") : error("DEPOT_PATH is empty and ENV[\"JULIA_HISTORY\"] not set.")) -backend(r::AbstractREPL) = r.backendref +backend(r::AbstractREPL) = hasproperty(r, :backendref) ? r.backendref : nothing -function eval_with_backend(ast, backend::REPLBackendRef) - put!(backend.repl_channel, (ast, 1)) + +function eval_with_backend(ast::Expr, backend::REPLBackendRef) + put!(backend.repl_channel, (ast, 1)) # (f, show_value) + return take!(backend.response_channel) # (val, iserr) +end +function eval_with_backend(f, backend::REPLBackendRef) + put!(backend.repl_channel, (f, 2)) # (f, show_value) 2 indicates function (rather than ast) return take!(backend.response_channel) # (val, iserr) end +# if no backend just eval (used by tests) +function eval_with_backend(f, backend::Nothing) + try + ret = f() + return (ret, false) # (val, iserr) + catch err + return (err, true) + end +end + function respond(f, repl, main; pass_empty::Bool = false, suppress_on_semicolon::Bool = true) return function do_respond(s::MIState, buf, ok::Bool) diff --git a/stdlib/REPL/test/repl.jl b/stdlib/REPL/test/repl.jl index 241464ca48942..d1233ee00da1b 100644 --- a/stdlib/REPL/test/repl.jl +++ b/stdlib/REPL/test/repl.jl @@ -925,7 +925,7 @@ function test19864() @eval Base.showerror(io::IO, e::Error19864) = print(io, "correct19864") buf = IOBuffer() fake_response = (Base.ExceptionStack([(exception=Error19864(),backtrace=Ptr{Cvoid}[])]),true) - REPL.print_response(buf, fake_response, false, false, nothing) + REPL.print_response(buf, fake_response, nothing, false, false, nothing) return String(take!(buf)) end @test occursin("correct19864", test19864()) From bf76bab785a02c9c0f9520c808092ec6809e3d10 Mon Sep 17 00:00:00 2001 From: Diogo Netto <61364108+d-netto@users.noreply.github.com> Date: Mon, 24 Mar 2025 20:20:44 -0300 Subject: [PATCH 03/13] [Linux] Prevent GC from running during process teardown (#57832) ## Context We send a signal 15 to shutdown our servers. We noticed that some of our servers that receive the termination signal are segfaulting in GC, which leads to false alarms in our internal monitors that track GC-related crashes. ## Hypothesis We suspect this pathological case may be happening: - Process receives signal 15, which is captured by the signal listener thread. - Signal listener initiates process' teardown (e.g. through `raise`). - IIRC such operation is not atomic in Linux, i.e. the kernel will gradually kill the threads, but it's possible for us to spent a few ms in a state where part of the threads in the system are alive, and part have already been killed (this point needs some confirmation). - With part of the process alive, and part of the process dead, we try to enter a GC, see a bunch of Julia data structures in an intermediate/corrupted state, which leads us to crash when running the GC. ## Mitigation Since our main goal is to get rid of the GC crashes that happen around server shutdown, we believe that it would be sufficient to just prevent the last bullet point. I.e. we prevent the system from even running a GC when we're about to kill the process, and we wait for any ongoing GC to finish. Co-debugged with @kpamnany. (cherry picked from commit e1e3a46a3ae17337d7a6671ba05ea4ae72ff9209) --- src/signals-unix.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/signals-unix.c b/src/signals-unix.c index 1f4ad647a87af..2db397050420e 100644 --- a/src/signals-unix.c +++ b/src/signals-unix.c @@ -559,6 +559,7 @@ static int thread0_exit_signo = 0; static void JL_NORETURN jl_exit_thread0_cb(void) { CFI_NORETURN + jl_atomic_fetch_add(&jl_gc_disable_counter, -1); jl_critical_error(thread0_exit_signo, 0, NULL, jl_current_task); jl_atexit_hook(128); jl_raise(thread0_exit_signo); @@ -1089,6 +1090,10 @@ static void *signal_listener(void *arg) //#if defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 199309L && !HAVE_KEVENT // si_code = info.si_code; //#endif + // Let's forbid threads from running GC while we're trying to exit, + // also let's make sure we're not in the middle of GC. + jl_atomic_fetch_add(&jl_gc_disable_counter, 1); + jl_safepoint_wait_gc(NULL); jl_exit_thread0(sig, signal_bt_data, signal_bt_size); } else if (critical) { From 3761029c8402862c8d88079863e365657edd1fef Mon Sep 17 00:00:00 2001 From: Gabriel Baraldi Date: Mon, 24 Mar 2025 15:55:15 -0300 Subject: [PATCH 04/13] Add option to force emission of const return function (#57824) This is mostly for GPUCompiler (cherry picked from commit e78cf6c240c9f668235b2ea97d65598f736deac0) --- base/reflection.jl | 11 +++++------ src/aotcompile.cpp | 2 +- src/cgutils.cpp | 3 ++- src/init.c | 3 ++- src/julia.h | 1 + 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/base/reflection.jl b/base/reflection.jl index 50b4413f01b59..528202a9196ba 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -149,23 +149,22 @@ struct CodegenParams use_jlplt::Cint """ - If enabled, only provably reachable code (from functions marked with `entrypoint`) is included - in the output system image. Errors or warnings can be given for call sites too dynamic to handle. - The option is disabled by default. (0=>disabled, 1=>safe (static errors), 2=>unsafe, 3=>unsafe plus warnings) + If enabled emit LLVM IR for all functions even if wouldn't be compiled + for some reason (i.e functions that return a constant value). """ - trim::Cint + force_emit_all::Cint function CodegenParams(; track_allocations::Bool=true, code_coverage::Bool=true, prefer_specsig::Bool=false, gnu_pubnames::Bool=true, debug_info_kind::Cint = default_debug_info_kind(), debug_info_level::Cint = Cint(JLOptions().debug_level), safepoint_on_entry::Bool=true, - gcstack_arg::Bool=true, use_jlplt::Bool=true, trim::Cint=Cint(0)) + gcstack_arg::Bool=true, use_jlplt::Bool=true, force_emit_all::Bool=false) return new( Cint(track_allocations), Cint(code_coverage), Cint(prefer_specsig), Cint(gnu_pubnames), debug_info_kind, debug_info_level, Cint(safepoint_on_entry), - Cint(gcstack_arg), Cint(use_jlplt), Cint(trim)) + Cint(gcstack_arg), Cint(use_jlplt), Cint(force_emit_all)) end end diff --git a/src/aotcompile.cpp b/src/aotcompile.cpp index 9fab5b11d97c4..688aeca1b242c 100644 --- a/src/aotcompile.cpp +++ b/src/aotcompile.cpp @@ -775,7 +775,7 @@ void *jl_emit_native_impl(jl_array_t *codeinfos, LLVMOrcThreadSafeModuleRef llvm params.tsctx, clone.getModuleUnlocked()->getDataLayout(), Triple(clone.getModuleUnlocked()->getTargetTriple())); jl_llvm_functions_t decls; - if (jl_atomic_load_relaxed(&codeinst->invoke) == jl_fptr_const_return_addr) + if (!(params.params->force_emit_all) && jl_atomic_load_relaxed(&codeinst->invoke) == jl_fptr_const_return_addr) decls.functionObject = "jl_fptr_const_return"; else decls = jl_emit_codeinst(result_m, codeinst, src, params); diff --git a/src/cgutils.cpp b/src/cgutils.cpp index bd6670c322ef6..9e70b2f3e6679 100644 --- a/src/cgutils.cpp +++ b/src/cgutils.cpp @@ -4410,7 +4410,8 @@ static int compare_cgparams(const jl_cgparams_t *a, const jl_cgparams_t *b) (a->debug_info_kind == b->debug_info_kind) && (a->safepoint_on_entry == b->safepoint_on_entry) && (a->gcstack_arg == b->gcstack_arg) && - (a->use_jlplt == b->use_jlplt); + (a->use_jlplt == b->use_jlplt) && + (a->force_emit_all == b->force_emit_all); } #endif diff --git a/src/init.c b/src/init.c index 333c469226fcf..3ac4f8f7d770b 100644 --- a/src/init.c +++ b/src/init.c @@ -739,7 +739,8 @@ JL_DLLEXPORT jl_cgparams_t jl_default_cgparams = { /* debug_info_level */ 0, // later jl_options.debug_level, /* safepoint_on_entry */ 1, /* gcstack_arg */ 1, - /* use_jlplt*/ 1 }; + /* use_jlplt*/ 1 , + /*force_emit_all=*/ 0}; static void init_global_mutexes(void) { JL_MUTEX_INIT(&jl_modules_mutex, "jl_modules_mutex"); diff --git a/src/julia.h b/src/julia.h index 30e4eb71f2320..56b61d2a04000 100644 --- a/src/julia.h +++ b/src/julia.h @@ -2727,6 +2727,7 @@ typedef struct { int gcstack_arg; // Pass the ptls value as an argument with swiftself int use_jlplt; // Whether to use the Julia PLT mechanism or emit symbols directly + int force_emit_all; // Force emission of code for const return functions } jl_cgparams_t; extern JL_DLLEXPORT int jl_default_debug_info_kind; extern JL_DLLEXPORT jl_cgparams_t jl_default_cgparams; From e2d37cb42cf3dfc5f150c0298dfb42a99d92c1e8 Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Tue, 25 Mar 2025 13:56:07 +0100 Subject: [PATCH 05/13] `Base`: `PCRE`: `exec`: type assert `String` after construction (#57868) Should prevent some invalidation in the sysimage. (cherry picked from commit f49f46d240df335233fa49d1b204c7df7de30540) --- base/pcre.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/pcre.jl b/base/pcre.jl index e4567fe03e8f8..213fc1890f51d 100644 --- a/base/pcre.jl +++ b/base/pcre.jl @@ -199,7 +199,7 @@ end exec(re, subject::Union{String,SubString{String}}, offset, options, match_data) = _exec(re, subject, offset, options, match_data) exec(re, subject, offset, options, match_data) = - _exec(re, String(subject), offset, options, match_data) + _exec(re, String(subject)::String, offset, options, match_data) function _exec(re, subject, offset, options, match_data) rc = ccall((:pcre2_match_8, PCRE_LIB), Cint, From 4f6c665f624c81a1c6fbb9898bbbbabd2a9cd01e Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Tue, 25 Mar 2025 13:56:37 +0100 Subject: [PATCH 06/13] `Base`: `macro b_str`: restrict argument to `String` (#57863) Should make the sysimage less vulnerable to invalidation. As far as I understand this change can't break any code, because: * the macro definition requires `AbstractString` * the way Julia macros work, the argument is either a literal or an `Expr` * `String` is the only `AbstractString` with literals (cherry picked from commit 87f4d3a344e7efe49fc41870f5e3e3901fca8b00) --- base/strings/io.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/strings/io.jl b/base/strings/io.jl index b4a3c7ad3e0c2..b27a6049f0b0e 100644 --- a/base/strings/io.jl +++ b/base/strings/io.jl @@ -589,7 +589,7 @@ julia> v[2] 0x32 ``` """ -macro b_str(s) +macro b_str(s::String) v = codeunits(unescape_string(s)) QuoteNode(v) end From bf9f1e72592dd818750183e0fd092f7ed1624729 Mon Sep 17 00:00:00 2001 From: Neven Sajko <4944410+nsajko@users.noreply.github.com> Date: Tue, 25 Mar 2025 13:56:52 +0100 Subject: [PATCH 07/13] `Base`: `macro cmd`: restrict argument to `String` (#57862) Should make the sysimage less vulnerable to invalidation. As far as I understand this change can't break any code, because: * the macro definition requires `AbstractString` * the way Julia macros work, the argument is either a literal or an `Expr` * `String` is the only `AbstractString` with literals (cherry picked from commit 7acb2a6aa4299b38cddb52117b21dcdbd1c8268d) --- base/cmd.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/base/cmd.jl b/base/cmd.jl index b46c8293cdf3c..12d576e0e4c4b 100644 --- a/base/cmd.jl +++ b/base/cmd.jl @@ -504,7 +504,7 @@ julia> run(cm) Process(`echo 1`, ProcessExited(0)) ``` """ -macro cmd(str) +macro cmd(str::String) cmd_ex = shell_parse(str, special=shell_special, filename=String(__source__.file))[1] return :(cmd_gen($(esc(cmd_ex)))) end From da859883c7cc8639b7fb29c696b8d52cfdd28d3e Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Tue, 25 Mar 2025 08:59:40 -0400 Subject: [PATCH 08/13] Profile: remove scope from profile macros (#57858) Fixes this, which breaks expectations from the way `@time` doesn't introduce a new scope. ``` julia> using Profile julia> @profile x = 1 1 julia> x ERROR: UndefVarError: `x` not defined in `Main` Suggestion: check for spelling errors or missing imports. ``` (cherry picked from commit d6af199c5d489d5964cddc73425bfdc1a002ddfa) --- stdlib/Profile/src/Allocs.jl | 6 +++--- stdlib/Profile/src/Profile.jl | 16 ++++++++-------- stdlib/Profile/test/runtests.jl | 14 ++++++++++++++ 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/stdlib/Profile/src/Allocs.jl b/stdlib/Profile/src/Allocs.jl index 9d0b18cb468ca..93c9d3392626f 100644 --- a/stdlib/Profile/src/Allocs.jl +++ b/stdlib/Profile/src/Allocs.jl @@ -79,11 +79,11 @@ end function _prof_expr(expr, opts) quote $start(; $(esc(opts))) - try + Base.@__tryfinally( $(esc(expr)) - finally + , $stop() - end + ) end end diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index 2e4092fb22c24..27f32fc453d55 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -56,12 +56,12 @@ appended to an internal buffer of backtraces. """ macro profile(ex) return quote - try - start_timer() + start_timer() + Base.@__tryfinally( $(esc(ex)) - finally + , stop_timer() - end + ) end end @@ -78,12 +78,12 @@ it can be used to diagnose performance issues such as lock contention, IO bottle """ macro profile_walltime(ex) return quote - try - start_timer(true) + start_timer(true); + Base.@__tryfinally( $(esc(ex)) - finally + , stop_timer() - end + ) end end diff --git a/stdlib/Profile/test/runtests.jl b/stdlib/Profile/test/runtests.jl index e7877b949a17e..b487d8963f156 100644 --- a/stdlib/Profile/test/runtests.jl +++ b/stdlib/Profile/test/runtests.jl @@ -155,6 +155,20 @@ end @test z == 10 end +@testset "@profile no scope" begin + @profile no_scope_57858_1 = 1 + @test @isdefined no_scope_57858_1 + Profile.clear() + + @profile_walltime no_scope_57858_1 = 1 + @test @isdefined no_scope_57858_1 + Profile.clear() + + Profile.Allocs.@profile no_scope_57858_2 = 1 + @test @isdefined no_scope_57858_2 + Profile.Allocs.clear() +end + @testset "setting sample count and delay in init" begin n_, delay_ = Profile.init() n_original = n_ From 145ff43da41ef025bd37f49092c577a85222088f Mon Sep 17 00:00:00 2001 From: Martijn Visser Date: Tue, 25 Mar 2025 16:02:59 +0100 Subject: [PATCH 09/13] Don't error when initializing LibGit2 with CA roots path (#56924) When SSL_CERT_FILE or SSL_CERT_DIR is set, it is [impossible to set this location](https://github.com/libgit2/libgit2/blob/4dcdb64c6844d76776745cdc25071a72c1af84d6/src/libgit2/settings.c#L206-L222) in LibGit2_jll on Apple and Windows because [it isn't built with support for that](https://github.com/JuliaPackaging/Yggdrasil/blob/7123a60a68102ba6cd953e13a4e45845dc37fd82/L/LibGit2/build_tarballs.jl#L67). Until now we've errored out with a message telling users to set JULIA_SSL_CA_ROOTS_PATH to an empty string, which is a somewhat problematic workaround because the Windows environment variables UI doesn't allow empty values, and [setting it to an empty string from PowerShell unsets it](https://discourse.julialang.org/t/how-to-fix-ssl-cert-issues-in-pkg/115495/7?u=visr). This PR changes the behavior to allow this expected error. Variables like SSL_CERT_FILE are for instance [set by the Conda OpenSSL package on environment activation](https://github.com/conda-forge/openssl-feedstock/blob/83b5e2a793bc95d19e6cc2d9d28068f1a6ff6b79/recipe/activate-win.ps1) used by e.g. Python, ensuring many people cannot use Pkg operations that use LibGit2, like `dev Example`, `add Example#master`. See more user reports [on Discourse](https://discourse.julialang.org/search?q=JULIA_SSL_CA_ROOTS_PATH). Together with https://github.com/JuliaLang/NetworkOptions.jl/pull/37 this should improve the experience of users trying out Julia from a Conda environment. This should also be fine to backport. (cherry picked from commit 7fa969ab3ce9c1ab6b8b67969b1b8de04a671094) --- stdlib/LibGit2/src/LibGit2.jl | 16 ++++++---------- stdlib/LibGit2/test/bad_ca_roots.jl | 26 +++++++++++++++----------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/stdlib/LibGit2/src/LibGit2.jl b/stdlib/LibGit2/src/LibGit2.jl index 04435dd577c19..4eed62331bdbc 100644 --- a/stdlib/LibGit2/src/LibGit2.jl +++ b/stdlib/LibGit2/src/LibGit2.jl @@ -1042,24 +1042,20 @@ function set_ssl_cert_locations(cert_loc) else # files, /dev/null, non-existent paths, etc. cert_file = cert_loc end - ret = @ccall libgit2.git_libgit2_opts( + ret = @ccall libgit2.git_libgit2_opts( Consts.SET_SSL_CERT_LOCATIONS::Cint; cert_file::Cstring, cert_dir::Cstring)::Cint ret >= 0 && return ret + # On macOS and Windows LibGit2_jll is built without a TLS backend that supports + # certificate locations; don't throw on this expected error so we allow certificate + # location environment variables to be set for other purposes. + # We still try doing so to support other LibGit2 builds. err = Error.GitError(ret) err.class == Error.SSL && err.msg == "TLS backend doesn't support certificate locations" || throw(err) - var = nothing - for v in NetworkOptions.CA_ROOTS_VARS - haskey(ENV, v) && (var = v) - end - @assert var !== nothing # otherwise we shouldn't be here - msg = """ - Your Julia is built with a SSL/TLS engine that libgit2 doesn't know how to configure to use a file or directory of certificate authority roots, but your environment specifies one via the $var variable. If you believe your system's root certificates are safe to use, you can `export JULIA_SSL_CA_ROOTS_PATH=""` in your environment to use those instead. - """ - throw(Error.GitError(err.class, err.code, chomp(msg))) + return ret end """ diff --git a/stdlib/LibGit2/test/bad_ca_roots.jl b/stdlib/LibGit2/test/bad_ca_roots.jl index 4882065167bdb..4caed4ed90beb 100644 --- a/stdlib/LibGit2/test/bad_ca_roots.jl +++ b/stdlib/LibGit2/test/bad_ca_roots.jl @@ -12,20 +12,24 @@ const CAN_SET_CA_ROOTS_PATH = !Sys.isapple() && !Sys.iswindows() # Given this is a sub-processed test file, not using @testsets avoids # leaking the report print into the Base test runner report begin # empty CA roots file - # these fail for different reasons on different platforms: - # - on Apple & Windows you cannot set the CA roots path location - # - on Linux & FreeBSD you you can but these are invalid files + # different behavior on different platforms: + # - on Apple & Windows you cannot set the CA roots path location; don't error + # - on Linux & FreeBSD you can but these are invalid files + ENV["JULIA_SSL_CA_ROOTS_PATH"] = "/dev/null" - @test_throws LibGit2.GitError LibGit2.ensure_initialized() + if CAN_SET_CA_ROOTS_PATH + @test_throws LibGit2.GitError LibGit2.ensure_initialized() + else + @test LibGit2.ensure_initialized() === nothing + end + ENV["JULIA_SSL_CA_ROOTS_PATH"] = tempname() - @test_throws LibGit2.GitError LibGit2.ensure_initialized() - # test that it still fails if called a second time - @test_throws LibGit2.GitError LibGit2.ensure_initialized() - if !CAN_SET_CA_ROOTS_PATH - # test that this doesn't work on macOS & Windows - ENV["JULIA_SSL_CA_ROOTS_PATH"] = NetworkOptions.bundled_ca_roots() + if CAN_SET_CA_ROOTS_PATH + @test_throws LibGit2.GitError LibGit2.ensure_initialized() + # test that it still fails if called a second time @test_throws LibGit2.GitError LibGit2.ensure_initialized() - delete!(ENV, "JULIA_SSL_CA_ROOTS_PATH") + else + @test LibGit2.ensure_initialized() === nothing @test LibGit2.ensure_initialized() === nothing end end From 85f67490dca3d404b2fb1e9de938cad5b0a40997 Mon Sep 17 00:00:00 2001 From: Em Chu <61633163+mlechu@users.noreply.github.com> Date: Tue, 25 Mar 2025 13:29:30 -0700 Subject: [PATCH 10/13] Disallow non-lhs all-underscore variable names (#57626) Addresses part of #57547. Currently, we only show the "all-underscore variable name not allowed" error when the offending symbol is in value position according to `compile`, but an earlier pass would keep them as raw symbols regardless. This led to raw symbols making their way out of lowering in an edge case. This change: Reject all-underscore variables unless they're being written to. Also, improve an error message slighly. (cherry picked from commit 3360a44a4d342cead9d0a9c07815a6f5dd488aeb) --- src/julia-syntax.scm | 6 +++--- src/method.c | 3 ++- test/syntax.jl | 14 ++++++++++++++ 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 8bc313610bbfa..739fa45e088ca 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -4657,9 +4657,9 @@ f(x) = yt(x) (let ((e1 (if (and arg-map (symbol? e)) (get arg-map e e) e))) - (if (and value (or (underscore-symbol? e) - (and (pair? e) (eq? (car e) 'globalref) - (underscore-symbol? (cadr e))))) + (if (or (underscore-symbol? e) + (and (pair? e) (eq? (car e) 'globalref) + (underscore-symbol? (cadr e)))) (error (string "all-underscore identifiers are write-only and their values cannot be used in expressions" (format-loc current-loc)))) (cond (tail (emit-return tail e1)) (value e1) diff --git a/src/method.c b/src/method.c index 353fbdaf0ecff..abf039d9bce08 100644 --- a/src/method.c +++ b/src/method.c @@ -84,7 +84,8 @@ static jl_value_t *resolve_definition_effects(jl_value_t *expr, jl_module_t *mod int binding_effects, int eager_resolve) { if (jl_is_symbol(expr)) { - jl_error("Found raw symbol in code returned from lowering. Expected all symbols to have been resolved to GlobalRef or slots."); + jl_errorf("Found raw symbol %s in code returned from lowering. Expected all symbols to have been resolved to GlobalRef or slots.", + jl_symbol_name((jl_sym_t*)expr)); } if (!jl_is_expr(expr)) { diff --git a/test/syntax.jl b/test/syntax.jl index 7e09f4747f939..3986126ff59ac 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -2827,6 +2827,20 @@ end @m38386 @test isempty(methods(f38386)) +@testset "non-lhs all-underscore vars should fail in lowering" begin + # OK + @test (_ = 1) === 1 + @test ((_, _) = (1, 2)) == (1, 2) + @test Meta.isexpr(Meta.lower(Main, :(for _ in 1:2; 1; end)), :thunk) + @test (try; throw(1); catch _; 2; end) === 2 + @test (let _ = 1; 2; end) === 2 + # ERROR: syntax: all-underscore identifiers are write-only and their values cannot be used in expressions + @test Meta.isexpr(Meta.lower(Main, :(_ = 1; a = _)), :error) + @test Meta.isexpr(Meta.lower(Main, :(let; function f(); _; end; end)), :error) + @test Meta.isexpr(Meta.lower(Main, :(let; function f(); _; 1; end; end)), :error) + @test Meta.isexpr(Meta.lower(Main, :(begin; _; 1; end)), :error) +end + @testset "all-underscore varargs on the rhs" begin @test ncalls_in_lowered(quote _..., = a end, GlobalRef(Base, :rest)) == 0 @test ncalls_in_lowered(quote ___..., = a end, GlobalRef(Base, :rest)) == 0 From 5ae693e54ca681a332fbea66f2973a8567b66955 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 26 Mar 2025 09:15:07 -0400 Subject: [PATCH 11/13] Fix corner case of double module import (#57892) Fixes a regression introduced in #57755 seen on PkgEval in https://github.com/JuliaLang/julia/pull/57755#issuecomment-2745196165 (cherry picked from commit 1d2e165f8c1b8a210b0a60a2b71397e4cbf130e5) --- src/toplevel.c | 2 +- test/syntax.jl | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/toplevel.c b/src/toplevel.c index 73b65ecf2e3fb..174bb0ef4dd22 100644 --- a/src/toplevel.c +++ b/src/toplevel.c @@ -662,7 +662,7 @@ static void import_module(jl_task_t *ct, jl_module_t *JL_NONNULL m, jl_module_t if (!jl_bkind_is_some_implicit(kind) && kind != PARTITION_KIND_DECLARED) { // Unlike regular constant declaration, we allow this as long as we eventually end up at a constant. jl_walk_binding_inplace(&b, &bpart, ct->world_age); - if (jl_binding_kind(bpart) == PARTITION_KIND_CONST || jl_binding_kind(bpart) == PARTITION_KIND_BACKDATED_CONST || jl_binding_kind(bpart) == PARTITION_KIND_CONST_IMPORT) { + if (jl_bkind_is_some_constant(jl_binding_kind(bpart))) { // Already declared (e.g. on another thread) or imported. if (bpart->restriction == (jl_value_t*)import) return; diff --git a/test/syntax.jl b/test/syntax.jl index 3986126ff59ac..b53a3b4357dc4 100644 --- a/test/syntax.jl +++ b/test/syntax.jl @@ -4250,3 +4250,10 @@ out = let end end @test M57574.out === M57574.A + +# Double import of CONST_IMPORT symbol +module DoubleImport + import Test: Random + import Random +end +@test DoubleImport.Random === Test.Random From 6411e26e6da393088773942e4549493933060197 Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Wed, 26 Mar 2025 09:15:25 -0400 Subject: [PATCH 12/13] Fix typo in codegen for `isdefinedglobal` (#57889) Fixes #57872 (cherry picked from commit 400d0b12d28e183ef3e31780b6afe0e3eb88ba89) --- Compiler/test/codegen.jl | 5 +++++ src/codegen.cpp | 13 ++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Compiler/test/codegen.jl b/Compiler/test/codegen.jl index 4e0a1b1f88997..fd8bbae70a346 100644 --- a/Compiler/test/codegen.jl +++ b/Compiler/test/codegen.jl @@ -1027,3 +1027,8 @@ module TurnedIntoExplicit @test !occursin("jl_apply_generic", get_llvm(f, Tuple{UInt})) end + +# Test codegen for `isdefinedglobal` of constant (#57872) +const x57872 = "Hello" +f57872() = (Core.isdefinedglobal(@__MODULE__, Base.compilerbarrier(:const, :x57872)), x57872) # Extra globalref here to force world age bounds +@test f57872() == (true, "Hello") diff --git a/src/codegen.cpp b/src/codegen.cpp index e6c116f029f31..8c9f61d511a06 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -3824,12 +3824,11 @@ static bool emit_f_opfield(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, static jl_cgval_t emit_isdefinedglobal(jl_codectx_t &ctx, jl_module_t *modu, jl_sym_t *name, int allow_import, enum jl_memory_order order) { - Value *isnull = NULL; jl_binding_t *bnd = allow_import ? jl_get_binding(modu, name) : jl_get_module_binding(modu, name, 0); struct restriction_kind_pair rkp = { NULL, NULL, PARTITION_KIND_GUARD, 0 }; if (allow_import && jl_get_binding_leaf_partitions_restriction_kind(bnd, &rkp, ctx.min_world, ctx.max_world)) { - if (jl_bkind_is_some_constant(rkp.kind)) - return mark_julia_const(ctx, rkp.restriction); + if (jl_bkind_is_some_constant(rkp.kind) && rkp.restriction) + return mark_julia_const(ctx, jl_true); if (rkp.kind == PARTITION_KIND_GLOBAL) { Value *bp = julia_binding_gv(ctx, rkp.binding_if_global); bp = julia_binding_pvalue(ctx, bp); @@ -3837,17 +3836,17 @@ static jl_cgval_t emit_isdefinedglobal(jl_codectx_t &ctx, jl_module_t *modu, jl_ jl_aliasinfo_t ai = jl_aliasinfo_t::fromTBAA(ctx, ctx.tbaa().tbaa_binding); ai.decorateInst(v); v->setOrdering(get_llvm_atomic_order(order)); - isnull = ctx.builder.CreateICmpNE(v, Constant::getNullValue(ctx.types().T_prjlvalue)); + Value *isnull = ctx.builder.CreateICmpNE(v, Constant::getNullValue(ctx.types().T_prjlvalue)); return mark_julia_type(ctx, isnull, false, jl_bool_type); } } - Value *v = ctx.builder.CreateCall(prepare_call(jlboundp_func), { + Value *isdef = ctx.builder.CreateCall(prepare_call(jlboundp_func), { literal_pointer_val(ctx, (jl_value_t*)modu), literal_pointer_val(ctx, (jl_value_t*)name), ConstantInt::get(getInt32Ty(ctx.builder.getContext()), allow_import) }); - isnull = ctx.builder.CreateICmpNE(v, ConstantInt::get(getInt32Ty(ctx.builder.getContext()), 0)); - return mark_julia_type(ctx, isnull, false, jl_bool_type); + isdef = ctx.builder.CreateTrunc(isdef, getInt1Ty(ctx.builder.getContext())); + return mark_julia_type(ctx, isdef, false, jl_bool_type); } static bool emit_f_opmemory(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, From ea3f2ea14d7002c0c545c0231a288f613ff01183 Mon Sep 17 00:00:00 2001 From: Shuhei Kadowaki <40514306+aviatesk@users.noreply.github.com> Date: Thu, 27 Mar 2025 01:54:27 +0900 Subject: [PATCH 13/13] overload lattice ops within `abstract_eval_isdefinedglobal` (#57897) --- Compiler/src/abstractinterpretation.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/Compiler/src/abstractinterpretation.jl b/Compiler/src/abstractinterpretation.jl index f394542877e12..1c12bbf0c5e64 100644 --- a/Compiler/src/abstractinterpretation.jl +++ b/Compiler/src/abstractinterpretation.jl @@ -3283,6 +3283,7 @@ function abstract_eval_isdefinedglobal(interp::AbstractInterpreter, @nospecializ exct = Union{exct, ConcurrencyViolationError} end end + ⊑ = partialorder(typeinf_lattice(interp)) if M isa Const && s isa Const M, s = M.val, s.val if M isa Module && s isa Symbol