diff --git a/.github/workflows/Whitespace.yml b/.github/workflows/Whitespace.yml index 5706f6148dc33..37c9dbfd39a3c 100644 --- a/.github/workflows/Whitespace.yml +++ b/.github/workflows/Whitespace.yml @@ -18,6 +18,9 @@ jobs: uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7 with: persist-credentials: false + - uses: julia-actions/setup-julia@9b79636afcfb07ab02c256cede01fe2db6ba808c # v2.6.0 + with: + version: '1' - name: Check whitespace run: | contrib/check-whitespace.jl diff --git a/base/Base.jl b/base/Base.jl index 3b56dca166cee..874cec56329d1 100644 --- a/base/Base.jl +++ b/base/Base.jl @@ -354,14 +354,14 @@ include("set.jl") include("char.jl") function array_new_memory(mem::Memory{UInt8}, newlen::Int) # add an optimization to array_new_memory for StringVector - if (@assume_effects :total @ccall jl_genericmemory_owner(mem::Any,)::Any) isa String + if (@assume_effects :total @ccall jl_genericmemory_owner(mem::Any,)::Any) === mem + # TODO: when implemented, this should use a memory growing call + return typeof(mem)(undef, newlen) + else # If data is in a String, keep it that way. # When implemented, this could use jl_gc_expand_string(oldstr, newlen) as an optimization str = _string_n(newlen) return (@assume_effects :total !:consistent @ccall jl_string_to_genericmemory(str::Any,)::Memory{UInt8}) - else - # TODO: when implemented, this should use a memory growing call - return typeof(mem)(undef, newlen) end end include("strings/basic.jl") diff --git a/base/abstractarray.jl b/base/abstractarray.jl index cbbae8e852b2e..5413f4e177518 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -1584,7 +1584,7 @@ their component parts. A typical definition for an array that wraps a parent is `Base.dataids(C::CustomArray) = dataids(C.parent)`. """ dataids(A::AbstractArray) = (UInt(objectid(A)),) -dataids(A::Memory) = (B = ccall(:jl_genericmemory_owner, Any, (Any,), A); (UInt(pointer(B isa typeof(A) ? B : A)),)) +dataids(A::Memory) = (UInt(A.ptr),) dataids(A::Array) = dataids(A.ref.mem) dataids(::AbstractRange) = () dataids(x) = () diff --git a/base/array.jl b/base/array.jl index 40907b2b00317..7a9649f20dded 100644 --- a/base/array.jl +++ b/base/array.jl @@ -3141,10 +3141,6 @@ function _wrap(ref::MemoryRef{T}, dims::NTuple{N, Int}) where {T, N} mem_len = length(mem) + 1 - memoryrefoffset(ref) len = Core.checked_dims(dims...) @boundscheck mem_len >= len || invalid_wrap_err(mem_len, dims, len) - if N != 1 && !(ref === GenericMemoryRef(mem) && len === mem_len) - mem = ccall(:jl_genericmemory_slice, Memory{T}, (Any, Ptr{Cvoid}, Int), mem, ref.ptr_or_offset, len) - ref = memoryref(mem) - end return ref end diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index f7f7e80a0ebe1..dbfe3bb9ccac4 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -2466,7 +2466,7 @@ function abstract_eval_replaceglobal!(interp::AbstractInterpreter, sv::AbsIntSta end end -function args_are_actually_getglobal(argtypes) +function argtypes_are_actually_getglobal(argtypes::Vector{Any}) length(argtypes) in (3, 4) || return false M = argtypes[2] s = argtypes[3] @@ -2506,21 +2506,21 @@ function abstract_call_known(interp::AbstractInterpreter, @nospecialize(f), return Future(abstract_eval_setglobalonce!(interp, sv, argtypes)) elseif f === Core.replaceglobal! return Future(abstract_eval_replaceglobal!(interp, sv, argtypes)) - elseif f === Core.getfield && args_are_actually_getglobal(argtypes) + elseif f === Core.getfield && argtypes_are_actually_getglobal(argtypes) return Future(abstract_eval_getglobal(interp, sv, argtypes)) - elseif f === Core.isdefined && args_are_actually_getglobal(argtypes) + elseif f === Core.isdefined && argtypes_are_actually_getglobal(argtypes) exct = Bottom if length(argtypes) == 4 order = argtypes[4] - exct = global_order_exct(order, true, false) - if !(isa(order, Const) && get_atomic_order(order.val, true, false).x >= MEMORY_ORDER_UNORDERED.x) + exct = global_order_exct(order, #=loading=#true, #=storing=#false) + if !(isa(order, Const) && get_atomic_order(order.val, #=loading=#true, #=storing=#false).x >= MEMORY_ORDER_UNORDERED.x) exct = Union{exct, ConcurrencyViolationError} end end return Future(merge_exct(CallMeta(abstract_eval_isdefined( interp, - GlobalRef((argtypes[2]::Const).val, - (argtypes[3]::Const).val), + GlobalRef((argtypes[2]::Const).val::Module, + (argtypes[3]::Const).val::Symbol), sv), NoCallInfo()), exct)) elseif f === Core.get_binding_type @@ -3048,7 +3048,7 @@ function abstract_eval_copyast(interp::AbstractInterpreter, e::Expr, vtypes::Uni end function abstract_eval_isdefined_expr(interp::AbstractInterpreter, e::Expr, vtypes::Union{VarTable,Nothing}, - sv::AbsIntState) + sv::AbsIntState) sym = e.args[1] if isa(sym, SlotNumber) && vtypes !== nothing vtyp = vtypes[slot_id(sym)] @@ -3315,16 +3315,13 @@ function abstract_eval_binding_partition!(interp::AbstractInterpreter, g::Global end function abstract_eval_partition_load(interp::AbstractInterpreter, partition::Core.BindingPartition) - consistent = inaccessiblememonly = ALWAYS_FALSE - nothrow = false - generic_effects = Effects(EFFECTS_TOTAL; consistent, nothrow, inaccessiblememonly) if is_some_guard(binding_kind(partition)) if InferenceParams(interp).assume_bindings_static return RTEffects(Union{}, UndefVarError, EFFECTS_THROWS) else # We do not currently assume an invalidation for guard -> defined transitions # return RTEffects(Union{}, UndefVarError, EFFECTS_THROWS) - return RTEffects(Any, UndefVarError, generic_effects) + return RTEffects(Any, UndefVarError, generic_getglobal_effects) end end @@ -3335,20 +3332,20 @@ function abstract_eval_partition_load(interp::AbstractInterpreter, partition::Co rt = partition_restriction(partition) - if InferenceParams(interp).assume_bindings_static + return RTEffects(rt, UndefVarError, generic_getglobal_effects) +end + +function abstract_eval_globalref(interp::AbstractInterpreter, g::GlobalRef, sv::AbsIntState) + partition = abstract_eval_binding_partition!(interp, g, sv) + ret = abstract_eval_partition_load(interp, partition) + if ret.rt !== Union{} && ret.exct === UndefVarError && InferenceParams(interp).assume_bindings_static if isdefined(g, :binding) && isdefined(g.binding, :value) - return RTEffects(rt, Union{}, Effecst(generic_effects, nothrow=true)) + return RTEffects(ret.rt, Union{}, Effects(generic_getglobal_effects, nothrow=true)) end # We do not assume in general that assigned global bindings remain assigned. # The existence of pkgimages allows them to revert in practice. end - - return RTEffects(rt, UndefVarError, generic_effects) -end - -function abstract_eval_globalref(interp::AbstractInterpreter, g::GlobalRef, sv::AbsIntState) - partition = abstract_eval_binding_partition!(interp, g, sv) - return abstract_eval_partition_load(interp, partition) + return ret end function global_assignment_exct(interp::AbstractInterpreter, sv::AbsIntState, g::GlobalRef, @nospecialize(newty)) @@ -4045,7 +4042,6 @@ function typeinf(interp::AbstractInterpreter, frame::InferenceState) takeprev = 0 while takenext >= frame.frameid callee = takenext == 0 ? frame : callstack[takenext]::InferenceState - interp = callee.interp if !isempty(callstack) if length(callstack) - frame.frameid >= minwarn topmethod = callstack[1].linfo @@ -4059,6 +4055,7 @@ function typeinf(interp::AbstractInterpreter, frame::InferenceState) takenext = length(callstack) end end + interp = callee.interp nextstateid = takenext + 1 - frame.frameid while length(nextstates) < nextstateid push!(nextstates, CurrentState()) diff --git a/base/compiler/inferencestate.jl b/base/compiler/inferencestate.jl index 43ada89f23133..fd421af733943 100644 --- a/base/compiler/inferencestate.jl +++ b/base/compiler/inferencestate.jl @@ -227,10 +227,24 @@ struct HandlerInfo handler_at::Vector{Tuple{Int,Int}} # tuple of current (handler, exception stack) value at the pc end +struct WorldWithRange + this::UInt + valid_worlds::WorldRange + function WorldWithRange(world::UInt, valid_worlds::WorldRange) + if !(world in valid_worlds) + error("invalid age range update") + end + return new(world, valid_worlds) + end +end + +intersect(world::WorldWithRange, valid_worlds::WorldRange) = + WorldWithRange(world.this, intersect(world.valid_worlds, valid_worlds)) + mutable struct InferenceState #= information about this method instance =# linfo::MethodInstance - world::UInt + world::WorldWithRange mod::Module sptypes::Vector{VarState} slottypes::Vector{Any} @@ -265,7 +279,6 @@ mutable struct InferenceState #= results =# result::InferenceResult # remember where to put the result unreachable::BitSet # statements that were found to be statically unreachable - valid_worlds::WorldRange bestguess #::Type exc_bestguess ipo_effects::Effects @@ -353,10 +366,10 @@ mutable struct InferenceState parentid = frameid = cycleid = 0 this = new( - mi, world, mod, sptypes, slottypes, src, cfg, spec_info, + mi, WorldWithRange(world, valid_worlds), mod, sptypes, slottypes, src, cfg, spec_info, currbb, currpc, ip, handler_info, ssavalue_uses, bb_vartables, ssavaluetypes, edges, stmt_info, tasks, pclimitations, limitations, cycle_backedges, callstack, parentid, frameid, cycleid, - result, unreachable, valid_worlds, bestguess, exc_bestguess, ipo_effects, + result, unreachable, bestguess, exc_bestguess, ipo_effects, restrict_abstract_call_sites, cache_mode, insert_coverage, interp) @@ -372,7 +385,7 @@ mutable struct InferenceState # Apply generated function restrictions if src.min_world != 1 || src.max_world != typemax(UInt) # From generated functions - this.valid_worlds = WorldRange(src.min_world, src.max_world) + update_valid_age!(this, WorldRange(src.min_world, src.max_world)) end return this @@ -772,14 +785,13 @@ mutable struct IRInterpretationState const spec_info::SpecInfo const ir::IRCode const mi::MethodInstance - const world::UInt + world::WorldWithRange curridx::Int const argtypes_refined::Vector{Bool} const sptypes::Vector{VarState} const tpdum::TwoPhaseDefUseMap const ssa_refined::BitSet const lazyreachability::LazyCFGReachability - valid_worlds::WorldRange const tasks::Vector{WorkThunk} const edges::Vector{Any} callstack #::Vector{AbsIntState} @@ -809,8 +821,8 @@ mutable struct IRInterpretationState tasks = WorkThunk[] edges = Any[] callstack = AbsIntState[] - return new(spec_info, ir, mi, world, curridx, argtypes_refined, ir.sptypes, tpdum, - ssa_refined, lazyreachability, valid_worlds, tasks, edges, callstack, 0, 0) + return new(spec_info, ir, mi, WorldWithRange(world, valid_worlds), curridx, argtypes_refined, ir.sptypes, tpdum, + ssa_refined, lazyreachability, tasks, edges, callstack, 0, 0) end end @@ -910,8 +922,8 @@ spec_info(sv::IRInterpretationState) = sv.spec_info propagate_inbounds(sv::AbsIntState) = spec_info(sv).propagate_inbounds method_for_inference_limit_heuristics(sv::AbsIntState) = spec_info(sv).method_for_inference_limit_heuristics -frame_world(sv::InferenceState) = sv.world -frame_world(sv::IRInterpretationState) = sv.world +frame_world(sv::InferenceState) = sv.world.this +frame_world(sv::IRInterpretationState) = sv.world.this function is_effect_overridden(sv::AbsIntState, effect::Symbol) if is_effect_overridden(frame_instance(sv), effect) @@ -933,9 +945,8 @@ has_conditional(::AbstractLattice, ::IRInterpretationState) = false # work towards converging the valid age range for sv function update_valid_age!(sv::AbsIntState, valid_worlds::WorldRange) - valid_worlds = sv.valid_worlds = intersect(valid_worlds, sv.valid_worlds) - @assert sv.world in valid_worlds "invalid age range update" - return valid_worlds + sv.world = intersect(sv.world, valid_worlds) + return sv.world.valid_worlds end """ @@ -1131,6 +1142,7 @@ function Future{T}(f, prev::Future{S}, interp::AbstractInterpreter, sv::AbsIntSt else @assert Core._hasmethod(Tuple{Core.Typeof(f), S, typeof(interp), typeof(sv)}) result = Future{T}() + @assert !isa(sv, InferenceState) || interp === sv.interp push!(sv.tasks, function (interp, sv) result[] = f(later[], interp, sv) # capture just later, instead of all of prev return true diff --git a/base/compiler/optimize.jl b/base/compiler/optimize.jl index aeb3e6849773b..edc374f675c5f 100644 --- a/base/compiler/optimize.jl +++ b/base/compiler/optimize.jl @@ -141,7 +141,7 @@ struct InliningState{Interp<:AbstractInterpreter} interp::Interp end function InliningState(sv::InferenceState, interp::AbstractInterpreter) - return InliningState(sv.edges, sv.world, interp) + return InliningState(sv.edges, frame_world(sv), interp) end function InliningState(interp::AbstractInterpreter) return InliningState(Any[], get_inference_world(interp), interp) @@ -1033,7 +1033,7 @@ function run_passes_ipo_safe( end if is_asserts() @timeit "verify 3" begin - verify_ir(ir, true, false, optimizer_lattice(sv.inlining.interp)) + verify_ir(ir, true, false, optimizer_lattice(sv.inlining.interp), sv.linfo) verify_linetable(ir.debuginfo, length(ir.stmts)) end end diff --git a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl index 1f98758cd6055..887a21ef7e0f6 100644 --- a/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl +++ b/base/compiler/ssair/EscapeAnalysis/EscapeAnalysis.jl @@ -26,7 +26,7 @@ using ._TOP_MOD: # Base definitions using Core.Compiler: # Core.Compiler specific definitions AbstractLattice, Bottom, IRCode, IR_FLAG_NOTHROW, InferenceResult, SimpleInferenceLattice, argextype, fieldcount_noerror, hasintersect, has_flag, intrinsic_nothrow, - is_meta_expr_head, is_mutation_free_argtype, isexpr, println, setfield!_nothrow, + is_meta_expr_head, is_identity_free_argtype, isexpr, println, setfield!_nothrow, singleton_type, try_compute_field, try_compute_fieldidx, widenconst, ⊑ include(x) = _TOP_MOD.include(@__MODULE__, x) @@ -861,7 +861,7 @@ function add_escape_change!(astate::AnalysisState, @nospecialize(x), xinfo::Esca xinfo === ⊥ && return nothing # performance optimization xidx = iridx(x, astate.estate) if xidx !== nothing - if force || !is_mutation_free_argtype(argextype(x, astate.ir)) + if force || !is_identity_free_argtype(argextype(x, astate.ir)) push!(astate.changes, EscapeChange(xidx, xinfo)) end end @@ -871,7 +871,7 @@ end function add_liveness_change!(astate::AnalysisState, @nospecialize(x), livepc::Int) xidx = iridx(x, astate.estate) if xidx !== nothing - if !is_mutation_free_argtype(argextype(x, astate.ir)) + if !is_identity_free_argtype(argextype(x, astate.ir)) push!(astate.changes, LivenessChange(xidx, livepc)) end end @@ -1077,7 +1077,10 @@ function escape_invoke!(astate::AnalysisState, pc::Int, args::Vector{Any}) # to consider the possibility of aliasing between them and the return value. for argidx = first_idx:last_idx arg = args[argidx] - if !is_mutation_free_argtype(argextype(arg, astate.ir)) + if arg isa GlobalRef + continue # :effect_free guarantees that nothings escapes to the global scope + end + if !is_identity_free_argtype(argextype(arg, astate.ir)) add_alias_change!(astate, ret, arg) end end diff --git a/base/compiler/ssair/passes.jl b/base/compiler/ssair/passes.jl index b483c307a2f5e..dad4a09a3e710 100644 --- a/base/compiler/ssair/passes.jl +++ b/base/compiler/ssair/passes.jl @@ -1731,8 +1731,11 @@ function sroa_mutables!(ir::IRCode, defuses::IdDict{Int,Tuple{SPCSet,SSADefUse}} if finalizer_useidx isa Int nargs = length(ir.argtypes) # COMBAK this might need to be `Int(opt.src.nargs)` estate = EscapeAnalysis.analyze_escapes(ir, nargs, 𝕃ₒ, get_escape_cache(inlining.interp)) + # disable finalizer inlining when this allocation is aliased to somewhere, + # mostly likely to edges of `PhiNode` + hasaliases = EscapeAnalysis.getaliases(SSAValue(defidx), estate) !== nothing einfo = estate[SSAValue(defidx)] - if EscapeAnalysis.has_no_escape(einfo) + if !hasaliases && EscapeAnalysis.has_no_escape(einfo) already = BitSet(use.idx for use in defuse.uses) for idx = einfo.Liveness if idx ∉ already diff --git a/base/compiler/ssair/verify.jl b/base/compiler/ssair/verify.jl index 268991282c483..14ca6ef2dbe9a 100644 --- a/base/compiler/ssair/verify.jl +++ b/base/compiler/ssair/verify.jl @@ -7,6 +7,7 @@ function maybe_show_ir(ir::IRCode) else Core.show(ir) end + Core.println(Core.stdout) end if !isdefined(@__MODULE__, Symbol("@verify_error")) @@ -25,7 +26,8 @@ end is_toplevel_expr_head(head::Symbol) = head === :global || head === :method || head === :thunk is_value_pos_expr_head(head::Symbol) = head === :static_parameter -function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int, use_idx::Int, printed_use_idx::Int, print::Bool, isforeigncall::Bool, arg_idx::Int, allow_frontend_forms::Bool) +function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int, use_idx::Int, printed_use_idx::Int, print::Bool, isforeigncall::Bool, arg_idx::Int, + allow_frontend_forms::Bool, @nospecialize(raise_error)) if isa(op, SSAValue) op.id > 0 || @verify_error "Def ($(op.id)) is invalid in final IR" if op.id > length(ir.stmts) @@ -39,14 +41,14 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int, else if op.id >= use_idx @verify_error "Def ($(op.id)) does not dominate use ($(use_idx)) in same BB" - error("") + raise_error() end end else if !dominates(domtree, def_bb, use_bb) && !(bb_unreachable(domtree, def_bb) && bb_unreachable(domtree, use_bb)) # At the moment, we allow GC preserve tokens outside the standard domination notion @verify_error "Basic Block $def_bb does not dominate block $use_bb (tried to use value %$(op.id) at %$(printed_use_idx))" - error("") + raise_error() end end @@ -56,12 +58,12 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int, # an earlier block got deleted, but for some reason we didn't figure # out yet that this entire block is dead also. @verify_error "At statement %$use_idx: Invalid use of value statement or terminator %$(op.id)" - error("") + raise_error() end elseif isa(op, GlobalRef) if !isdefined(op.mod, op.name) || !isconst(op.mod, op.name) @verify_error "Unbound GlobalRef not allowed in value position" - error("") + raise_error() end elseif isa(op, Expr) # Only Expr(:boundscheck) is allowed in value position @@ -72,15 +74,15 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int, elseif !is_value_pos_expr_head(op.head) if !allow_frontend_forms || op.head !== :opaque_closure_method @verify_error "Expr not allowed in value position" - error("") + raise_error() end end elseif isa(op, Union{OldSSAValue, NewSSAValue}) @verify_error "At statement %$use_idx: Left over SSA marker ($op)" - error("") + raise_error() elseif isa(op, SlotNumber) @verify_error "Left over slot detected in converted IR" - error("") + raise_error() end end @@ -96,31 +98,49 @@ end function verify_ir(ir::IRCode, print::Bool=true, allow_frontend_forms::Bool=false, - 𝕃ₒ::AbstractLattice = SimpleInferenceLattice.instance) + 𝕃ₒ::AbstractLattice = SimpleInferenceLattice.instance, + mi::Union{Nothing,MethodInstance}=nothing) + function raise_error() + error_args = Any["IR verification failed."] + if isdefined(Core, :Main) && isdefined(Core.Main, :Base) + # ensure we use I/O that does not yield, as this gets called during compilation + firstline = invokelatest(Core.Main.Base.IRShow.debuginfo_firstline, ir.debuginfo) + else + firstline = nothing + end + if firstline !== nothing + file, line = firstline + push!(error_args, "\n", " Code location: ", file, ":", line) + end + if mi !== nothing + push!(error_args, "\n", " Method instance: ", mi) + end + error(error_args...) + end # Verify CFG graph. Must be well formed to construct domtree if !(length(ir.cfg.blocks) - 1 <= length(ir.cfg.index) <= length(ir.cfg.blocks)) @verify_error "CFG index length ($(length(ir.cfg.index))) does not correspond to # of blocks $(length(ir.cfg.blocks))" - error("") + raise_error() end if length(ir.stmts.stmt) != length(ir.stmts) @verify_error "IR stmt length is invalid $(length(ir.stmts.stmt)) / $(length(ir.stmts))" - error("") + raise_error() end if length(ir.stmts.type) != length(ir.stmts) @verify_error "IR type length is invalid $(length(ir.stmts.type)) / $(length(ir.stmts))" - error("") + raise_error() end if length(ir.stmts.info) != length(ir.stmts) @verify_error "IR info length is invalid $(length(ir.stmts.info)) / $(length(ir.stmts))" - error("") + raise_error() end if length(ir.stmts.line) != length(ir.stmts) * 3 @verify_error "IR line length is invalid $(length(ir.stmts.line)) / $(length(ir.stmts) * 3)" - error("") + raise_error() end if length(ir.stmts.flag) != length(ir.stmts) @verify_error "IR flag length is invalid $(length(ir.stmts.flag)) / $(length(ir.stmts))" - error("") + raise_error() end # For now require compact IR # @assert isempty(ir.new_nodes) @@ -132,43 +152,43 @@ function verify_ir(ir::IRCode, print::Bool=true, p == 0 && continue if !(1 <= p <= length(ir.cfg.blocks)) @verify_error "Predecessor $p of block $idx out of bounds for IR" - error("") + raise_error() end c = count_int(idx, ir.cfg.blocks[p].succs) if c == 0 @verify_error "Predecessor $p of block $idx not in successor list" - error("") + raise_error() elseif c == 2 if count_int(p, block.preds) != 2 @verify_error "Double edge from $p to $idx not correctly accounted" - error("") + raise_error() end end end for s in block.succs if !(1 <= s <= length(ir.cfg.blocks)) @verify_error "Successor $s of block $idx out of bounds for IR" - error("") + raise_error() end if !(idx in ir.cfg.blocks[s].preds) #Base.@show ir.cfg #Base.@show ir #Base.@show ir.argtypes @verify_error "Successor $s of block $idx not in predecessor list" - error("") + raise_error() end end if !(1 <= first(block.stmts) <= length(ir.stmts)) @verify_error "First statement of BB $idx ($(first(block.stmts))) out of bounds for IR (length=$(length(ir.stmts)))" - error("") + raise_error() end if !(1 <= last(block.stmts) <= length(ir.stmts)) @verify_error "Last statement of BB $idx ($(last(block.stmts))) out of bounds for IR (length=$(length(ir.stmts)))" - error("") + raise_error() end if idx <= length(ir.cfg.index) && last(block.stmts) + 1 != ir.cfg.index[idx] @verify_error "End of BB $idx ($(last(block.stmts))) is not one less than CFG index ($(ir.cfg.index[idx]))" - error("") + raise_error() end end # Verify statements @@ -177,7 +197,7 @@ function verify_ir(ir::IRCode, print::Bool=true, if first(block.stmts) != last_end + 1 #ranges = [(idx,first(bb.stmts),last(bb.stmts)) for (idx, bb) in pairs(ir.cfg.blocks)] @verify_error "First statement of BB $idx ($(first(block.stmts))) does not match end of previous ($last_end)" - error("") + raise_error() end last_end = last(block.stmts) terminator = ir[SSAValue(last_end)][:stmt] @@ -186,32 +206,32 @@ function verify_ir(ir::IRCode, print::Bool=true, if isa(terminator, ReturnNode) if !isempty(block.succs) @verify_error "Block $idx ends in return or unreachable, but has successors" - error("") + raise_error() end elseif isa(terminator, GotoNode) if length(block.succs) != 1 || block.succs[1] != terminator.label @verify_error "Block $idx successors ($(block.succs)), does not match GotoNode terminator ($(terminator.label))" - error("") + raise_error() end elseif isa(terminator, GotoIfNot) if terminator.dest == idx + 1 @verify_error "Block $idx terminator forms a double edge to block $(idx+1)" - error("") + raise_error() end if length(block.succs) != 2 || (block.succs != [terminator.dest, idx+1] && block.succs != [idx+1, terminator.dest]) @verify_error "Block $idx successors ($(block.succs)), does not match GotoIfNot terminator" - error("") + raise_error() end elseif isa(terminator, EnterNode) @label enter_check if length(block.succs) == 1 if terminator.catch_dest != 0 @verify_error "Block $idx successors ($(block.succs)), does not match :enter terminator" - error("") + raise_error() end elseif (block.succs != Int[terminator.catch_dest, idx+1] && block.succs != Int[idx+1, terminator.catch_dest]) @verify_error "Block $idx successors ($(block.succs)), does not match :enter terminator" - error("") + raise_error() end else if length(block.succs) != 1 || block.succs[1] != idx + 1 @@ -233,14 +253,14 @@ function verify_ir(ir::IRCode, print::Bool=true, # here, but that isn't always possible. else @verify_error "Block $idx successors ($(block.succs)), does not match fall-through terminator %$termidx ($terminator)::$stmttyp" - error("") + raise_error() end end end end if length(ir.stmts) != last(ir.cfg.blocks[end].stmts) @verify_error "End of last BB $(last(ir.cfg.blocks[end].stmts)) does not match last IR statement $(length(ir.stmts))" - error("") + raise_error() end lastbb = 0 is_phinode_block = false @@ -260,7 +280,7 @@ function verify_ir(ir::IRCode, print::Bool=true, if isa(stmt, PhiNode) if !is_phinode_block @verify_error "φ node $idx is not at the beginning of the basic block $bb" - error("") + raise_error() end lastphi = idx @assert length(stmt.edges) == length(stmt.values) @@ -271,20 +291,20 @@ function verify_ir(ir::IRCode, print::Bool=true, if edge == edge′ # TODO: Move `unique` to Core.Compiler. For now we assume the predecessor list is always unique. @verify_error "Edge list φ node $idx in bb $bb not unique (double edge?)" - error("") + raise_error() end end if !(edge == 0 && bb == 1) && !(edge in ir.cfg.blocks[bb].preds) #Base.@show ir.argtypes #Base.@show ir @verify_error "Edge $edge of φ node $idx not in predecessor list" - error("") + raise_error() end edge == 0 && continue if bb_unreachable(domtree, Int(edge)) # TODO: Disallow? #@verify_error "Unreachable edge from #$edge should have been cleaned up at idx $idx" - #error("") + #raise_error() continue end isassigned(stmt.values, i) || continue @@ -297,10 +317,11 @@ function verify_ir(ir::IRCode, print::Bool=true, # PhiNode type was $phiT # Value type was $(ir.stmts[val.id][:type]) #""" - #error("") + #raise_error() end end - check_op(ir, domtree, val, Int(edge), last(ir.cfg.blocks[stmt.edges[i]].stmts)+1, idx, print, false, i, allow_frontend_forms) + check_op(ir, domtree, val, Int(edge), last(ir.cfg.blocks[stmt.edges[i]].stmts)+1, idx, print, false, i, + allow_frontend_forms, raise_error) end continue end @@ -311,7 +332,8 @@ function verify_ir(ir::IRCode, print::Bool=true, for validate_idx in firstidx:(lastphi-1) validate_stmt = ir[SSAValue(validate_idx)][:stmt] isa(validate_stmt, PhiNode) && continue - check_op(ir, domtree, validate_stmt, bb, idx, idx, print, false, 0, allow_frontend_forms) + check_op(ir, domtree, validate_stmt, bb, idx, idx, print, false, 0, + allow_frontend_forms, raise_error) end is_phinode_block = false end @@ -321,21 +343,21 @@ function verify_ir(ir::IRCode, print::Bool=true, val = stmt.values[i] if !isa(val, SSAValue) @verify_error "Operand $i of PhiC node $idx must be an SSA Value." - error("") + raise_error() end if !isa(ir[val][:stmt], UpsilonNode) @verify_error "Operand $i of PhiC node $idx must reference an Upsilon node." - error("") + raise_error() end end elseif isterminator(stmt) if idx != last(ir.cfg.blocks[bb].stmts) @verify_error "Terminator $idx in bb $bb is not the last statement in the block" - error("") + raise_error() end if !isa(stmt, ReturnNode) && ir[SSAValue(idx)][:type] !== Any @verify_error "Explicit terminators (other than ReturnNode) must have `Any` type" - error("") + raise_error() end else isforeigncall = false @@ -343,7 +365,7 @@ function verify_ir(ir::IRCode, print::Bool=true, if stmt.head === :(=) if stmt.args[1] isa SSAValue @verify_error "SSAValue as assignment LHS" - error("") + raise_error() end if stmt.args[2] isa GlobalRef # undefined GlobalRef as assignment RHS is OK @@ -352,7 +374,7 @@ function verify_ir(ir::IRCode, print::Bool=true, elseif stmt.head === :isdefined if length(stmt.args) > 2 || (length(stmt.args) == 2 && !isa(stmt.args[2], Bool)) @verify_error "malformed isdefined" - error("") + raise_error() end if stmt.args[1] isa GlobalRef # undefined GlobalRef is OK in isdefined @@ -380,12 +402,12 @@ function verify_ir(ir::IRCode, print::Bool=true, arg = stmt.args[i] if !isa(arg, Union{Nothing, SSAValue}) @verify_error "Malformed :leave - Expected `Nothing` or SSAValue" - error() + raise_error() elseif isa(arg, SSAValue) enter_stmt = ir[arg::SSAValue][:stmt] if !isa(enter_stmt, Nothing) && !isa(enter_stmt, EnterNode) @verify_error "Malformed :leave - argument ssavalue should point to `nothing` or :enter" - error() + raise_error() end end end @@ -394,7 +416,8 @@ function verify_ir(ir::IRCode, print::Bool=true, n = 1 for op in userefs(stmt) op = op[] - check_op(ir, domtree, op, bb, idx, idx, print, isforeigncall, n, allow_frontend_forms) + check_op(ir, domtree, op, bb, idx, idx, print, isforeigncall, n, + allow_frontend_forms, raise_error) n += 1 end end diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index aaa1354fd5e54..f0212f1082331 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -2497,11 +2497,11 @@ function builtin_effects(𝕃::AbstractLattice, @nospecialize(f::Builtin), argty elseif f === getglobal 2 ≤ length(argtypes) ≤ 3 || return EFFECTS_THROWS # Modeled more precisely in abstract_eval_getglobal - return Effects(EFFECTS_TOTAL; consistent=ALWAYS_FALSE, nothrow=false, inaccessiblememonly=ALWAYS_FALSE) + return generic_getglobal_effects elseif f === Core.get_binding_type length(argtypes) == 2 || return EFFECTS_THROWS # Modeled more precisely in abstract_eval_get_binding_type - return Effects(EFFECTS_TOTAL; effect_free=ALWAYS_FALSE) + return Effects(EFFECTS_TOTAL; nothrow=get_binding_type_nothrow(𝕃, argtypes[1], argtypes[2])) elseif f === compilerbarrier length(argtypes) == 2 || return Effects(EFFECTS_THROWS; consistent=ALWAYS_FALSE) setting = argtypes[1] diff --git a/base/compiler/typeinfer.jl b/base/compiler/typeinfer.jl index 11337d5a4d047..1b3ff144639e4 100644 --- a/base/compiler/typeinfer.jl +++ b/base/compiler/typeinfer.jl @@ -25,7 +25,7 @@ end function _typeinf_identifier(frame::Core.Compiler.InferenceState) mi_info = InferenceFrameInfo( frame.linfo, - frame.world, + frame_world(sv), copy(frame.sptypes), copy(frame.slottypes), length(frame.result.argtypes), @@ -173,7 +173,7 @@ function finish_cycle(::AbstractInterpreter, frames::Vector{AbsIntState}, cyclei # all frames in the cycle should have the same bits of `valid_worlds` and `effects` # that are simply the intersection of each partial computation, without having # dependencies on each other (unlike rt and exct) - cycle_valid_worlds = intersect(cycle_valid_worlds, caller.valid_worlds) + cycle_valid_worlds = intersect(cycle_valid_worlds, caller.world.valid_worlds) cycle_valid_effects = merge_effects(cycle_valid_effects, caller.ipo_effects) end for frameid = cycleid:length(frames) @@ -197,7 +197,7 @@ function finish_cycle(::AbstractInterpreter, frames::Vector{AbsIntState}, cyclei end function adjust_cycle_frame!(sv::InferenceState, cycle_valid_worlds::WorldRange, cycle_valid_effects::Effects) - sv.valid_worlds = cycle_valid_worlds + update_valid_age!(sv, cycle_valid_worlds) sv.ipo_effects = cycle_valid_effects # traverse the callees of this cycle that are tracked within `sv.cycle_backedges` # and adjust their statements so that they are consistent with the new `cycle_valid_effects` @@ -403,13 +403,13 @@ function finishinfer!(me::InferenceState, interp::AbstractInterpreter) end end result = me.result - result.valid_worlds = me.valid_worlds + result.valid_worlds = me.world.valid_worlds result.result = bestguess ipo_effects = result.ipo_effects = me.ipo_effects = adjust_effects(me) result.exc_result = me.exc_bestguess = refine_exception_type(me.exc_bestguess, ipo_effects) me.src.rettype = widenconst(ignorelimited(bestguess)) - me.src.min_world = first(me.valid_worlds) - me.src.max_world = last(me.valid_worlds) + me.src.min_world = first(me.world.valid_worlds) + me.src.max_world = last(me.world.valid_worlds) istoplevel = !(me.linfo.def isa Method) istoplevel || compute_edges!(me) # don't add backedges to toplevel method instance @@ -637,7 +637,7 @@ function merge_call_chain!(::AbstractInterpreter, parent::InferenceState, child: end function add_cycle_backedge!(caller::InferenceState, frame::InferenceState) - update_valid_age!(caller, frame.valid_worlds) + update_valid_age!(caller, frame.world.valid_worlds) backedge = (caller, caller.currpc) contains_is(frame.cycle_backedges, backedge) || push!(frame.cycle_backedges, backedge) return frame @@ -730,7 +730,7 @@ end function codeinst_as_edge(interp::AbstractInterpreter, sv::InferenceState) mi = sv.linfo owner = cache_owner(interp) - min_world, max_world = first(sv.valid_worlds), last(sv.valid_worlds) + min_world, max_world = first(sv.world.valid_worlds), last(sv.world.valid_worlds) if max_world >= get_world_counter() max_world = typemax(UInt) end @@ -816,7 +816,7 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize # while splitting off the rest of the work for this caller into a separate workq thunk let mresult = Future{MethodCallResult}() push!(caller.tasks, function get_infer_result(interp, caller) - update_valid_age!(caller, frame.valid_worlds) + update_valid_age!(caller, frame.world.valid_worlds) local isinferred = is_inferred(frame) local edge = isinferred ? edge_ci : nothing local effects = isinferred ? frame.result.ipo_effects : # effects are adjusted already within `finish` for ipo_effects @@ -842,7 +842,7 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize end # return the current knowledge about this cycle frame = frame::InferenceState - update_valid_age!(caller, frame.valid_worlds) + update_valid_age!(caller, frame.world.valid_worlds) effects = adjust_effects(effects_for_cycle(frame.ipo_effects), method) bestguess = frame.bestguess exc_bestguess = refine_exception_type(frame.exc_bestguess, effects) diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index 7441f5b993bf4..d618330e79874 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -1305,6 +1305,12 @@ kw";" Short-circuiting boolean AND. +This is equivalent to `x ? y : false`: it returns `false` if `x` is `false` and the result of evaluating `y` if `x` is `true`. +Note that if `y` is an expression, it is only evaluated when `x` is `true`, which is called "short-circuiting" behavior. + +Also, `y` does not need to have a boolean value. This means that `(condition) && (statement)` can be used as shorthand for +`if condition; statement; end` for an arbitrary `statement`. + See also [`&`](@ref), the ternary operator `? :`, and the manual section on [control flow](@ref man-conditional-evaluation). # Examples @@ -1316,6 +1322,9 @@ true julia> x < 0 && error("expected positive x") false + +julia> x > 0 && "not a boolean" +"not a boolean" ``` """ kw"&&" @@ -1325,6 +1334,12 @@ kw"&&" Short-circuiting boolean OR. +This is equivalent to `x ? true : y`: it returns `true` if `x` is `true` and the result of evaluating `y` if `x` is `false`. +Note that if `y` is an expression, it is only evaluated when `x` is `false`, which is called "short-circuiting" behavior. + +Also, `y` does not need to have a boolean value. This means that `(condition) || (statement)` can be used as shorthand for +`if !(condition); statement; end` for an arbitrary `statement`. + See also: [`|`](@ref), [`xor`](@ref), [`&&`](@ref). # Examples @@ -1334,6 +1349,9 @@ true julia> false || true || println("neither is true!") true + +julia> pi < 3 || "not a boolean" +"not a boolean" ``` """ kw"||" @@ -1689,7 +1707,7 @@ ErrorException """ FieldError(type::DataType, field::Symbol) -An operation tried to access invalid `field` of `type`. +An operation tried to access invalid `field` on an object of `type`. !!! compat "Julia 1.12" Prior to Julia 1.12, invalid field access threw an [`ErrorException`](@ref) diff --git a/base/essentials.jl b/base/essentials.jl index 750ee0f9c434c..64fbaea95d4e7 100644 --- a/base/essentials.jl +++ b/base/essentials.jl @@ -9,7 +9,8 @@ const Bottom = Union{} # Define minimal array interface here to help code used in macros: length(a::Array{T, 0}) where {T} = 1 length(a::Array{T, 1}) where {T} = getfield(a, :size)[1] -length(a::Array) = getfield(getfield(getfield(a, :ref), :mem), :length) +length(a::Array{T, 2}) where {T} = (sz = getfield(a, :size); sz[1] * sz[2]) +# other sizes are handled by generic prod definition for AbstractArray length(a::GenericMemory) = getfield(a, :length) throw_boundserror(A, I) = (@noinline; throw(BoundsError(A, I))) diff --git a/base/range.jl b/base/range.jl index c4435f2ff3e97..b05dddb025a7c 100644 --- a/base/range.jl +++ b/base/range.jl @@ -711,6 +711,7 @@ julia> step(range(2.5, stop=10.9, length=85)) """ step(r::StepRange) = r.step step(r::AbstractUnitRange{T}) where {T} = oneunit(T) - zero(T) +step(r::AbstractUnitRange{Bool}) = true step(r::StepRangeLen) = r.step step(r::StepRangeLen{T}) where {T<:AbstractFloat} = T(r.step) step(r::LinRange) = (last(r)-first(r))/r.lendiv diff --git a/base/reflection.jl b/base/reflection.jl index f2a554e0f27c5..0b7612e44f744 100644 --- a/base/reflection.jl +++ b/base/reflection.jl @@ -15,7 +15,7 @@ yielded by expanding the generators. The keyword `debuginfo` controls the amount of code metadata present in the output. -Note that an error will be thrown if `types` are not leaf types when `generated` is +Note that an error will be thrown if `types` are not concrete types when `generated` is `true` and any of the corresponding methods are an `@generated` method. """ function code_lowered(@nospecialize(f), @nospecialize(t=Tuple); generated::Bool=true, debuginfo::Symbol=:default) @@ -37,7 +37,7 @@ function code_lowered(@nospecialize(f), @nospecialize(t=Tuple); generated::Bool= else error("Could not expand generator for `@generated` method ", m, ". ", "This can happen if the provided argument types (", t, ") are ", - "not leaf types, but the `generated` argument is `true`.") + "not concrete types, but the `generated` argument is `true`.") end else code = uncompressed_ir(m.def::Method) diff --git a/base/reshapedarray.jl b/base/reshapedarray.jl index 019f1d30a25c2..07f608588837b 100644 --- a/base/reshapedarray.jl +++ b/base/reshapedarray.jl @@ -35,31 +35,33 @@ end length(R::ReshapedArrayIterator) = length(R.iter) eltype(::Type{<:ReshapedArrayIterator{I}}) where {I} = @isdefined(I) ? ReshapedIndex{eltype(I)} : Any -## reshape(::Array, ::Dims) returns an Array, except for isbitsunion eltypes (issue #28611) +@noinline throw_dmrsa(dims, len) = + throw(DimensionMismatch("new dimensions $(dims) must be consistent with array length $len")) + +## reshape(::Array, ::Dims) returns a new Array (to avoid conditionally aliasing the structure, only the data) # reshaping to same # of dimensions @eval function reshape(a::Array{T,M}, dims::NTuple{N,Int}) where {T,N,M} - throw_dmrsa(dims, len) = - throw(DimensionMismatch("new dimensions $(dims) must be consistent with array length $len")) len = Core.checked_dims(dims...) # make sure prod(dims) doesn't overflow (and because of the comparison to length(a)) if len != length(a) throw_dmrsa(dims, length(a)) end - isbitsunion(T) && return ReshapedArray(a, dims, ()) - if N == M && dims == size(a) - return a - end ref = a.ref - if M == 1 && N !== 1 - mem = ref.mem::Memory{T} - if !(ref === memoryref(mem) && len === mem.length) - mem = ccall(:jl_genericmemory_slice, Memory{T}, (Any, Ptr{Cvoid}, Int), mem, ref.ptr_or_offset, len) - ref = memoryref(mem)::typeof(ref) - end - end - # or we could use `a = Array{T,N}(undef, ntuple(0, Val(N))); a.ref = ref; a.size = dims; return a` here + # or we could use `a = Array{T,N}(undef, ntuple(i->0, Val(N))); a.ref = ref; a.size = dims; return a` here to avoid the eval return $(Expr(:new, :(Array{T,N}), :ref, :dims)) end +## reshape!(::Array, ::Dims) returns the original array, but must have the same dimensions and length as the original +# see also resize! for a similar operation that can change the length +function reshape!(a::Array{T,N}, dims::NTuple{N,Int}) where {T,N} + len = Core.checked_dims(dims...) # make sure prod(dims) doesn't overflow (and because of the comparison to length(a)) + if len != length(a) + throw_dmrsa(dims, length(a)) + end + setfield!(a, :dims, dims) + return a +end + + """ reshape(A, dims...) -> AbstractArray diff --git a/base/runtime_internals.jl b/base/runtime_internals.jl index 1da58af38d545..4a04d406550b7 100644 --- a/base/runtime_internals.jl +++ b/base/runtime_internals.jl @@ -839,7 +839,7 @@ end """ isdispatchtuple(T) -Determine whether type `T` is a tuple "leaf type", +Determine whether type `T` is a tuple of concrete types, meaning it could appear as a type signature in dispatch and has no subtypes (or supertypes) which could appear in a call. If `T` is not a type, then return `false`. @@ -894,7 +894,7 @@ isconcretedispatch(@nospecialize t) = isconcretetype(t) && !iskindtype(t) using Core: has_free_typevars # equivalent to isa(v, Type) && isdispatchtuple(Tuple{v}) || v === Union{} -# and is thus perhaps most similar to the old (pre-1.0) `isleaftype` query +# and is thus perhaps most similar to the old (pre-1.0) `isconcretetype` query function isdispatchelem(@nospecialize v) return (v === Bottom) || (v === typeof(Bottom)) || isconcretedispatch(v) || (isType(v) && !has_free_typevars(v)) diff --git a/base/sort.jl b/base/sort.jl index ef0f208209fc8..2251d0b965228 100644 --- a/base/sort.jl +++ b/base/sort.jl @@ -231,8 +231,8 @@ function searchsorted(v::AbstractVector, x, ilo::T, ihi::T, o::Ordering)::UnitRa elseif lt(o, x, v[m]) hi = m else - a = searchsortedfirst(v, x, max(lo,ilo), m, o) - b = searchsortedlast(v, x, m, min(hi,ihi), o) + a = searchsortedfirst(v, x, lo+u, m, o) + b = searchsortedlast(v, x, m, hi-u, o) return a : b end end diff --git a/doc/src/devdocs/ast.md b/doc/src/devdocs/ast.md index 50a64ec5813f7..d8db3ca677082 100644 --- a/doc/src/devdocs/ast.md +++ b/doc/src/devdocs/ast.md @@ -501,9 +501,9 @@ These symbols appear in the `head` field of [`Expr`](@ref)s in lowered form. The number of required arguments for a varargs function definition. - * `args[5]::QuoteNode{Symbol}` : calling convention + * `args[5]::QuoteNode{<:Union{Symbol,Tuple{Symbol,UInt16}}`: calling convention - The calling convention for the call. + The calling convention for the call, optionally with effects. * `args[6:5+length(args[3])]` : arguments diff --git a/doc/src/manual/calling-c-and-fortran-code.md b/doc/src/manual/calling-c-and-fortran-code.md index b8d064f698208..f675ab5eb16e8 100644 --- a/doc/src/manual/calling-c-and-fortran-code.md +++ b/doc/src/manual/calling-c-and-fortran-code.md @@ -276,17 +276,17 @@ it to be freed prematurely. First, let's review some relevant Julia type terminology: -| Syntax / Keyword | Example | Description | -|:----------------------------- |:------------------------------------------- |:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `mutable struct` | `BitSet` | "Leaf Type" :: A group of related data that includes a type-tag, is managed by the Julia GC, and is defined by object-identity. The type parameters of a leaf type must be fully defined (no `TypeVars` are allowed) in order for the instance to be constructed. | -| `abstract type` | `Any`, `AbstractArray{T, N}`, `Complex{T}` | "Super Type" :: A super-type (not a leaf-type) that cannot be instantiated, but can be used to describe a group of types. | -| `T{A}` | `Vector{Int}` | "Type Parameter" :: A specialization of a type (typically used for dispatch or storage optimization). | -| | | "TypeVar" :: The `T` in the type parameter declaration is referred to as a TypeVar (short for type variable). | -| `primitive type` | `Int`, `Float64` | "Primitive Type" :: A type with no fields, but a size. It is stored and defined by-value. | -| `struct` | `Pair{Int, Int}` | "Struct" :: A type with all fields defined to be constant. It is defined by-value, and may be stored with a type-tag. | -| | `ComplexF64` (`isbits`) | "Is-Bits" :: A `primitive type`, or a `struct` type where all fields are other `isbits` types. It is defined by-value, and is stored without a type-tag. | -| `struct ...; end` | `nothing` | "Singleton" :: a Leaf Type or Struct with no fields. | -| `(...)` or `tuple(...)` | `(1, 2, 3)` | "Tuple" :: an immutable data-structure similar to an anonymous struct type, or a constant array. Represented as either an array or a struct. | +| Syntax / Keyword | Example | Description | +|:----------------------------- |:------------------------------------------- |:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `mutable struct` | `BitSet` | "Concrete Type" :: A group of related data that includes a type-tag, is managed by the Julia GC, and is defined by object-identity. The type parameters of a concrete type must be fully defined (no `TypeVars` are allowed) in order for the instance to be constructed. Also see [`isconcretetype`](@ref). | +| `abstract type` | `Any`, `AbstractArray{T, N}`, `Complex{T}` | "Super Type" :: A super-type (not a concrete type) that cannot be instantiated, but can be used to describe a group of types. Also see [`isabstracttype`](@ref). | +| `T{A}` | `Vector{Int}` | "Type Parameter" :: A specialization of a type (typically used for dispatch or storage optimization). | +| | | "TypeVar" :: The `T` in the type parameter declaration is referred to as a TypeVar (short for type variable). | +| `primitive type` | `Int`, `Float64` | "Primitive Type" :: A type with no fields, but a size. It is stored and defined by-value. | +| `struct` | `Pair{Int, Int}` | "Struct" :: A type with all fields defined to be constant. It is defined by-value, and may be stored with a type-tag. | +| | `ComplexF64` (`isbits`) | "Is-Bits" :: A `primitive type`, or a `struct` type where all fields are other `isbits` types. It is defined by-value, and is stored without a type-tag. | +| `struct ...; end` | `nothing` | "Singleton" :: a concrete Type or Struct with no fields. | +| `(...)` or `tuple(...)` | `(1, 2, 3)` | "Tuple" :: an immutable data-structure similar to an anonymous struct type, or a constant array. Represented as either an array or a struct. | ### [Bits Types](@id man-bits-types) @@ -626,7 +626,7 @@ For translating a C argument list to Julia: * argument value will be copied (passed by value) * `struct T` (including typedef to a struct) - * `T`, where `T` is a Julia leaf type + * `T`, where `T` is a concrete Julia type * argument value will be copied (passed by value) * `void*` @@ -679,7 +679,7 @@ For translating a C return type to Julia: * argument value will be copied (returned by-value) * `struct T` (including typedef to a struct) - * `T`, where `T` is a Julia Leaf Type + * `T`, where `T` is a concrete Julia Type * argument value will be copied (returned by-value) * `void*` diff --git a/doc/src/manual/performance-tips.md b/doc/src/manual/performance-tips.md index 3033720b5df8c..038000f55e761 100644 --- a/doc/src/manual/performance-tips.md +++ b/doc/src/manual/performance-tips.md @@ -805,7 +805,7 @@ or `nothing` if it is not found, a clear type instability. In order to make it e type instabilities that are likely to be important, `Union`s containing either `missing` or `nothing` are color highlighted in yellow, instead of red. -The following examples may help you interpret expressions marked as containing non-leaf types: +The following examples may help you interpret expressions marked as containing non-concrete types: * Function body starting with `Body::Union{T1,T2})` * Interpretation: function with unstable return type @@ -821,7 +821,7 @@ The following examples may help you interpret expressions marked as containing n element accesses * `Base.getfield(%%x, :(:data))::Array{Float64,N} where N` - * Interpretation: getting a field that is of non-leaf type. In this case, the type of `x`, say `ArrayContainer`, had a + * Interpretation: getting a field that is of non-concrete type. In this case, the type of `x`, say `ArrayContainer`, had a field `data::Array{T}`. But `Array` needs the dimension `N`, too, to be a concrete type. * Suggestion: use concrete types like `Array{T,3}` or `Array{T,N}`, where `N` is now a parameter of `ArrayContainer` diff --git a/src/codegen.cpp b/src/codegen.cpp index b0d5038024900..e2bc8fe6e43d1 100644 --- a/src/codegen.cpp +++ b/src/codegen.cpp @@ -1435,18 +1435,6 @@ static const auto jl_allocgenericmemory = new JuliaFunction{ - XSTR(jl_array_data_owner), - [](LLVMContext &C) { - auto T_prjlvalue = JuliaType::get_prjlvalue_ty(C); - return FunctionType::get(T_prjlvalue, - {T_prjlvalue}, false); - }, - [](LLVMContext &C) { return AttributeList::get(C, - Attributes(C, {Attribute::ReadOnly, Attribute::NoUnwind}), - Attributes(C, {Attribute::NonNull}), - None); }, -}; #define BOX_FUNC(ct,at,attrs,nbytes) \ static const auto box_##ct##_func = new JuliaFunction<>{ \ XSTR(jl_box_##ct), \ @@ -4341,8 +4329,7 @@ static bool emit_f_opmemory(jl_codectx_t &ctx, jl_cgval_t *ret, jl_value_t *f, } Value *data_owner = NULL; // owner object against which the write barrier must check if (isboxed || layout->first_ptr >= 0) { // if elements are just bits, don't need a write barrier - Value *V = emit_memoryref_FCA(ctx, ref, layout); - data_owner = emit_genericmemoryowner(ctx, CreateSimplifiedExtractValue(ctx, V, 1)); + data_owner = emit_memoryref_mem(ctx, ref, layout); } *ret = typed_store(ctx, ptr, @@ -10166,7 +10153,8 @@ jl_llvm_functions_t jl_emit_codeinst( !(params.imaging_mode || jl_options.incremental)) { // don't delete code when generating a precompile file // Never end up in a situation where the codeinst has no invoke, but also no source, so we never fall // through the cracks of SOURCE_MODE_ABI. - jl_atomic_store_release(&codeinst->invoke, jl_fptr_wait_for_compiled_addr); + jl_callptr_t expected = NULL; + jl_atomic_cmpswap_relaxed(&codeinst->invoke, &expected, jl_fptr_wait_for_compiled_addr); jl_atomic_store_release(&codeinst->inferred, jl_nothing); } } diff --git a/src/genericmemory.c b/src/genericmemory.c index 5c48e3202493e..0bf2cf46edaae 100644 --- a/src/genericmemory.c +++ b/src/genericmemory.c @@ -197,7 +197,7 @@ JL_DLLEXPORT jl_value_t *jl_genericmemory_to_string(jl_genericmemory_t *m, size_ if (how != 0) { jl_value_t *o = jl_genericmemory_data_owner_field(m); jl_genericmemory_data_owner_field(m) = NULL; - if (how == 3 && + if (how == 3 && // implies jl_is_string(o) ((mlength + sizeof(void*) + 1 <= GC_MAX_SZCLASS) == (len + sizeof(void*) + 1 <= GC_MAX_SZCLASS))) { if (jl_string_data(o)[len] != '\0') jl_string_data(o)[len] = '\0'; @@ -221,39 +221,6 @@ JL_DLLEXPORT jl_genericmemory_t *jl_alloc_memory_any(size_t n) return jl_alloc_genericmemory(jl_memory_any_type, n); } -JL_DLLEXPORT jl_genericmemory_t *jl_genericmemory_slice(jl_genericmemory_t *mem, void *data, size_t len) -{ - // Given a GenericMemoryRef represented as `jl_genericmemory_ref ref = {data, mem}`, - // return a new GenericMemory that only accesses the slice from the given GenericMemoryRef to - // the given length if this is possible to return. This allows us to make - // `length(Array)==length(Array.ref.mem)`, for simplification of this. - jl_datatype_t *dt = (jl_datatype_t*)jl_typetagof(mem); - const jl_datatype_layout_t *layout = dt->layout; - // repeated checks here ensure the values cannot overflow, since we know mem->length is a reasonable value - if (len > mem->length) - jl_exceptionf(jl_argumenterror_type, "invalid GenericMemory slice"); // TODO: make a BoundsError - if (layout->flags.arrayelem_isunion) { - if (!((size_t)data == 0 && mem->length == len)) - jl_exceptionf(jl_argumenterror_type, "invalid GenericMemory slice"); // only exact slices are supported - data = mem->ptr; - } - else if (layout->size == 0) { - if ((size_t)data > mem->length || (size_t)data + len > mem->length) - jl_exceptionf(jl_argumenterror_type, "invalid GenericMemory slice"); // TODO: make a BoundsError - data = mem->ptr; - } - else { - if (data < mem->ptr || (char*)data > (char*)mem->ptr + mem->length * layout->size || (char*)data + len * layout->size > (char*)mem->ptr + mem->length * layout->size) - jl_exceptionf(jl_argumenterror_type, "invalid GenericMemory slice"); // TODO: make a BoundsError - } - jl_task_t *ct = jl_current_task; - jl_genericmemory_t *newmem = (jl_genericmemory_t*)jl_gc_alloc(ct->ptls, sizeof(jl_genericmemory_t) + sizeof(void*), dt); - newmem->length = len; - newmem->ptr = data; - jl_genericmemory_data_owner_field(newmem) = jl_genericmemory_owner(mem); - return newmem; -} - JL_DLLEXPORT void jl_genericmemory_copyto(jl_genericmemory_t *dest, char* destdata, jl_genericmemory_t *src, char* srcdata, size_t n) JL_NOTSAFEPOINT diff --git a/src/julia.h b/src/julia.h index 5b9986a5e68ee..301650540a15c 100644 --- a/src/julia.h +++ b/src/julia.h @@ -1233,7 +1233,7 @@ STATIC_INLINE jl_value_t *jl_svecset( 0 = data is inlined 1 = owns the gc-managed data, exclusively (will free it) 2 = malloc-allocated pointer (does not own it) - 3 = has a pointer to the object that owns the data pointer + 3 = has a pointer to the String object that owns the data pointer (m must be isbits) */ STATIC_INLINE int jl_genericmemory_how(jl_genericmemory_t *m) JL_NOTSAFEPOINT { @@ -1249,8 +1249,6 @@ STATIC_INLINE int jl_genericmemory_how(jl_genericmemory_t *m) JL_NOTSAFEPOINT STATIC_INLINE jl_value_t *jl_genericmemory_owner(jl_genericmemory_t *m JL_PROPAGATES_ROOT) JL_NOTSAFEPOINT { - if (jl_genericmemory_how(m) == 3) - return jl_genericmemory_data_owner_field(m); return (jl_value_t*)m; } @@ -1280,8 +1278,6 @@ STATIC_INLINE jl_value_t *jl_genericmemory_ptr_set( assert(i < m_->length); jl_atomic_store_release(((_Atomic(jl_value_t*)*)(m_->ptr)) + i, (jl_value_t*)x); if (x) { - if (jl_genericmemory_how(m_) == 3) - m = (void*)jl_genericmemory_data_owner_field(m_); jl_gc_wb(m, x); } return (jl_value_t*)x; diff --git a/src/julia_internal.h b/src/julia_internal.h index f3959490855c8..c5bac37890042 100644 --- a/src/julia_internal.h +++ b/src/julia_internal.h @@ -1129,7 +1129,7 @@ void jl_safepoint_end_gc(void); // The caller should set it **BEFORE** calling this function. void jl_safepoint_wait_gc(void) JL_NOTSAFEPOINT; void jl_safepoint_wait_thread_resume(void) JL_NOTSAFEPOINT; - +int8_t jl_safepoint_take_sleep_lock(jl_ptls_t ptls) JL_NOTSAFEPOINT_ENTER; // Set pending sigint and enable the mechanisms to deliver the sigint. void jl_safepoint_enable_sigint(void); // If the safepoint is enabled to deliver sigint, disable it diff --git a/src/llvm-gc-interface-passes.h b/src/llvm-gc-interface-passes.h index d33567e887118..278987858eab7 100644 --- a/src/llvm-gc-interface-passes.h +++ b/src/llvm-gc-interface-passes.h @@ -353,10 +353,11 @@ struct LateLowerGCFrame: private JuliaPassContext { State LocalScan(Function &F); void ComputeLiveness(State &S); void ComputeLiveSets(State &S); - SmallVector ColorRoots(const State &S); + std::pair, int> ColorRoots(const State &S); void PlaceGCFrameStore(State &S, unsigned R, unsigned MinColorRoot, ArrayRef Colors, Value *GCFrame, Instruction *InsertBefore); - void PlaceGCFrameStores(State &S, unsigned MinColorRoot, ArrayRef Colors, Value *GCFrame); - void PlaceRootsAndUpdateCalls(SmallVectorImpl &Colors, State &S, std::map>); + void PlaceGCFrameStores(State &S, unsigned MinColorRoot, ArrayRef Colors, int PreAssignedColors, Value *GCFrame); + void PlaceGCFrameReset(State &S, unsigned R, unsigned MinColorRoot, ArrayRef Colors, Value *GCFrame, Instruction *InsertBefore); + void PlaceRootsAndUpdateCalls(ArrayRef Colors, int PreAssignedColors, State &S, std::map>); void CleanupWriteBarriers(Function &F, State *S, const SmallVector &WriteBarriers, bool *CFGModified); bool CleanupIR(Function &F, State *S, bool *CFGModified); void NoteUseChain(State &S, BBState &BBS, User *TheUser); diff --git a/src/llvm-late-gc-lowering.cpp b/src/llvm-late-gc-lowering.cpp index 1d390a5115207..3e372ec9884e7 100644 --- a/src/llvm-late-gc-lowering.cpp +++ b/src/llvm-late-gc-lowering.cpp @@ -1820,7 +1820,7 @@ JL_USED_FUNC static void dumpColorAssignments(const State &S, const ArrayRef LateLowerGCFrame::ColorRoots(const State &S) { +std::pair, int> LateLowerGCFrame::ColorRoots(const State &S) { SmallVector Colors; Colors.resize(S.MaxPtrNumber + 1, -1); PEOIterator Ordering(S.Neighbors); @@ -1862,7 +1862,7 @@ SmallVector LateLowerGCFrame::ColorRoots(const State &S) { NewColor += PreAssignedColors; Colors[ActiveElement] = NewColor; } - return Colors; + return {Colors, PreAssignedColors}; } // Size of T is assumed to be `sizeof(void*)` @@ -2292,8 +2292,21 @@ void LateLowerGCFrame::PlaceGCFrameStore(State &S, unsigned R, unsigned MinColor new StoreInst(Val, slotAddress, InsertBefore); } +void LateLowerGCFrame::PlaceGCFrameReset(State &S, unsigned R, unsigned MinColorRoot, + ArrayRef Colors, Value *GCFrame, + Instruction *InsertBefore) { + // Get the slot address. + auto slotAddress = CallInst::Create( + getOrDeclare(jl_intrinsics::getGCFrameSlot), + {GCFrame, ConstantInt::get(Type::getInt32Ty(InsertBefore->getContext()), Colors[R] + MinColorRoot)}, + "gc_slot_addr_" + StringRef(std::to_string(Colors[R] + MinColorRoot)), InsertBefore); + // Reset the slot to NULL. + Value *Val = ConstantPointerNull::get(T_prjlvalue); + new StoreInst(Val, slotAddress, InsertBefore); +} + void LateLowerGCFrame::PlaceGCFrameStores(State &S, unsigned MinColorRoot, - ArrayRef Colors, Value *GCFrame) + ArrayRef Colors, int PreAssignedColors, Value *GCFrame) { for (auto &BB : *S.F) { const BBState &BBS = S.BBStates[&BB]; @@ -2306,6 +2319,15 @@ void LateLowerGCFrame::PlaceGCFrameStores(State &S, unsigned MinColorRoot, for(auto rit = BBS.Safepoints.rbegin(); rit != BBS.Safepoints.rend(); ++rit ) { const LargeSparseBitVector &NowLive = S.LiveSets[*rit]; + // reset slots which are no longer alive + for (int Idx : *LastLive) { + if (Idx >= PreAssignedColors && !HasBitSet(NowLive, Idx)) { + PlaceGCFrameReset(S, Idx, MinColorRoot, Colors, GCFrame, + S.ReverseSafepointNumbering[*rit]); + } + } + // store values which are alive in this safepoint but + // haven't been stored in the GC frame before for (int Idx : NowLive) { if (!HasBitSet(*LastLive, Idx)) { PlaceGCFrameStore(S, Idx, MinColorRoot, Colors, GCFrame, @@ -2317,7 +2339,7 @@ void LateLowerGCFrame::PlaceGCFrameStores(State &S, unsigned MinColorRoot, } } -void LateLowerGCFrame::PlaceRootsAndUpdateCalls(SmallVectorImpl &Colors, State &S, +void LateLowerGCFrame::PlaceRootsAndUpdateCalls(ArrayRef Colors, int PreAssignedColors, State &S, std::map>) { auto F = S.F; auto T_int32 = Type::getInt32Ty(F->getContext()); @@ -2439,7 +2461,7 @@ void LateLowerGCFrame::PlaceRootsAndUpdateCalls(SmallVectorImpl &Colors, St pushGcframe->setArgOperand(1, NRoots); // Insert GC frame stores - PlaceGCFrameStores(S, AllocaSlot - 2, Colors, gcframe); + PlaceGCFrameStores(S, AllocaSlot - 2, Colors, PreAssignedColors, gcframe); // Insert GCFrame pops for (auto &BB : *F) { if (isa(BB.getTerminator())) { @@ -2464,9 +2486,9 @@ bool LateLowerGCFrame::runOnFunction(Function &F, bool *CFGModified) { State S = LocalScan(F); ComputeLiveness(S); - SmallVector Colors = ColorRoots(S); + auto Colors = ColorRoots(S); std::map> CallFrames; // = OptimizeCallFrames(S, Ordering); - PlaceRootsAndUpdateCalls(Colors, S, CallFrames); + PlaceRootsAndUpdateCalls(Colors.first, Colors.second, S, CallFrames); CleanupIR(F, &S, CFGModified); return true; } diff --git a/src/safepoint.c b/src/safepoint.c index 2e324078897a6..8e24543c6769d 100644 --- a/src/safepoint.c +++ b/src/safepoint.c @@ -276,6 +276,25 @@ void jl_safepoint_wait_thread_resume(void) jl_atomic_store_release(&ct->ptls->gc_state, state); uv_mutex_unlock(&ct->ptls->sleep_lock); } +// This takes the sleep lock and puts the thread in GC_SAFE +int8_t jl_safepoint_take_sleep_lock(jl_ptls_t ptls) +{ + int8_t gc_state = jl_gc_safe_enter(ptls); + uv_mutex_lock(&ptls->sleep_lock); + if (jl_atomic_load_relaxed(&ptls->suspend_count)) { + // This dance with the locks is because we are not allowed to hold both these locks at the same time + // This avoids a situation where jl_safepoint_suspend_thread loads our GC state and sees GC_UNSAFE + // But we are in the process of becoming GC_SAFE, and also trigger the old safepoint, this causes us + // to go sleep in scheduler and the suspender thread to go to sleep in safepoint_cond_begin meaning we hang + // To avoid this we do the broadcast below to force it to observe the new gc_state + uv_mutex_unlock(&ptls->sleep_lock); + uv_mutex_lock(&safepoint_lock); + uv_cond_broadcast(&safepoint_cond_begin); + uv_mutex_unlock(&safepoint_lock); + uv_mutex_lock(&ptls->sleep_lock); + } + return gc_state; +} // n.b. suspended threads may still run in the GC or GC safe regions // but shouldn't be observable, depending on which enum the user picks (only 1 and 2 are typically recommended here) diff --git a/src/scheduler.c b/src/scheduler.c index 7e23f654c2566..fff891d91a813 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -499,8 +499,7 @@ JL_DLLEXPORT jl_task_t *jl_task_get_next(jl_value_t *trypoptask, jl_value_t *q, // the other threads will just wait for an individual wake signal to resume JULIA_DEBUG_SLEEPWAKE( ptls->sleep_enter = cycleclock() ); - int8_t gc_state = jl_gc_safe_enter(ptls); - uv_mutex_lock(&ptls->sleep_lock); + int8_t gc_state = jl_safepoint_take_sleep_lock(ptls); // This puts the thread in GC_SAFE and takes the sleep lock while (may_sleep(ptls)) { if (ptls->tid == 0) { task = wait_empty; diff --git a/src/staticdata.c b/src/staticdata.c index 0d609db03aebc..decc0ce6570aa 100644 --- a/src/staticdata.c +++ b/src/staticdata.c @@ -932,7 +932,7 @@ static void jl_insert_into_serialization_queue(jl_serializer_state *s, jl_value_ jl_genericmemory_t *m = (jl_genericmemory_t*)v; const char *data = (const char*)m->ptr; if (jl_genericmemory_how(m) == 3) { - jl_queue_for_serialization_(s, jl_genericmemory_data_owner_field(v), 1, immediate); + assert(jl_is_string(jl_genericmemory_data_owner_field(m))); } else if (layout->flags.arrayelem_isboxed) { size_t i, l = m->length; @@ -1472,17 +1472,7 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED jl_genericmemory_t *m = (jl_genericmemory_t*)v; const jl_datatype_layout_t *layout = t->layout; size_t len = m->length; - if (jl_genericmemory_how(m) == 3 && jl_is_genericmemory(jl_genericmemory_data_owner_field(m))) { - jl_genericmemory_t *owner = (jl_genericmemory_t*)jl_genericmemory_data_owner_field(m); - size_t data = ((char*)m->ptr - (char*)owner->ptr); // relocation offset (bytes) - write_uint(f, len); - write_uint(f, data); - write_pointerfield(s, (jl_value_t*)owner); - // similar to record_memoryref, but the field is always an (offset) pointer - arraylist_push(&s->memowner_list, (void*)(reloc_offset + offsetof(jl_genericmemory_t, ptr))); // relocation location - arraylist_push(&s->memowner_list, NULL); // relocation target (ignored) - } - // else if (jl_genericmemory_how(m) == 3) { + // if (jl_genericmemory_how(m) == 3) { // jl_value_t *owner = jl_genericmemory_data_owner_field(m); // write_uint(f, len); // write_pointerfield(s, owner); @@ -1491,7 +1481,8 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED // assert(new_mem->ptr == NULL); // new_mem->ptr = (void*)((char*)m->ptr - (char*)owner); // relocation offset // } - else { + // else + { size_t datasize = len * layout->size; size_t tot = datasize; int isbitsunion = layout->flags.arrayelem_isunion; @@ -1538,10 +1529,13 @@ static void jl_write_values(jl_serializer_state *s) JL_GC_DISABLED ios_write(s->const_data, (char*)m->ptr, tot); } } - if (len == 0) // TODO: should we have a zero-page, instead of writing each type's fragment separately? + if (len == 0) { // TODO: should we have a zero-page, instead of writing each type's fragment separately? write_padding(s->const_data, layout->size ? layout->size : isbitsunion); - else if (jl_genericmemory_how(m) == 3 && jl_is_string(jl_genericmemory_data_owner_field(m))) + } + else if (jl_genericmemory_how(m) == 3) { + assert(jl_is_string(jl_genericmemory_data_owner_field(m))); write_padding(s->const_data, 1); + } } else { // Pointer eltypes are encoded in the mutable data section diff --git a/stdlib/Dates/src/rounding.jl b/stdlib/Dates/src/rounding.jl index b5b6e52decba8..08a8218365d2c 100644 --- a/stdlib/Dates/src/rounding.jl +++ b/stdlib/Dates/src/rounding.jl @@ -84,6 +84,12 @@ function Base.floor(dt::DateTime, p::TimePeriod) return epochms2datetime(milliseconds - mod(milliseconds, value(Millisecond(p)))) end +function Base.floor(t::Time, p::TimePeriod) + value(p) < 1 && throw(DomainError(p)) + nanoseconds = value(t) + return Time(Nanosecond(nanoseconds - mod(nanoseconds, value(Nanosecond(p))))) +end + """ floor(x::Period, precision::T) where T <: Union{TimePeriod, Week, Day} -> T diff --git a/stdlib/Dates/test/rounding.jl b/stdlib/Dates/test/rounding.jl index 85c90981423d3..03c57c7a5bce3 100644 --- a/stdlib/Dates/test/rounding.jl +++ b/stdlib/Dates/test/rounding.jl @@ -188,7 +188,27 @@ end @test round(x, Dates.Microsecond) == Dates.Microsecond(2001000) @test round(x, Dates.Nanosecond) == x end - +@testset "Rounding Time" begin + x = Time(9, 25, 45, 25, 650, 500) + @test floor(x, Dates.Hour) == Time(9) + @test floor(x, Dates.Minute) == Time(9, 25) + @test floor(x, Dates.Second) == Time(9, 25, 45) + @test floor(x, Dates.Millisecond) == Time(9, 25, 45, 25) + @test floor(x, Dates.Microsecond) == Time(9, 25, 45, 25, 650) + @test floor(x, Dates.Nanosecond) == x + @test ceil(x, Dates.Hour) == Time(10) + @test ceil(x, Dates.Minute) == Time(9, 26) + @test ceil(x, Dates.Second) == Time(9, 25, 46) + @test ceil(x, Dates.Millisecond) == Time(9, 25, 45, 26) + @test ceil(x, Dates.Microsecond) == Time(9, 25, 45, 25, 651) + @test ceil(x, Dates.Nanosecond) == x + @test round(x, Dates.Hour) == Time(9) + @test round(x, Dates.Minute) == Time(9, 26) + @test round(x, Dates.Second) == Time(9, 25, 45) + @test round(x, Dates.Millisecond) == Time(9, 25, 45, 26) + @test round(x, Dates.Microsecond) == Time(9, 25, 45, 25, 651) + @test round(x, Dates.Nanosecond) == x +end @testset "Rounding DateTime to Date" begin now_ = DateTime(2020, 9, 1, 13) for p in (Year, Month, Day) diff --git a/stdlib/InteractiveUtils/src/codeview.jl b/stdlib/InteractiveUtils/src/codeview.jl index e3ef0a14a6608..92354d2fb9a75 100644 --- a/stdlib/InteractiveUtils/src/codeview.jl +++ b/stdlib/InteractiveUtils/src/codeview.jl @@ -128,10 +128,10 @@ end Prints lowered and type-inferred ASTs for the methods matching the given generic function and type signature to `io` which defaults to `stdout`. The ASTs are annotated in such a way -as to cause "non-leaf" types which may be problematic for performance to be emphasized +as to cause non-concrete types which may be problematic for performance to be emphasized (if color is available, displayed in red). This serves as a warning of potential type instability. -Not all non-leaf types are particularly problematic for performance, and the performance +Not all non-concrete types are particularly problematic for performance, and the performance characteristics of a particular type is an implementation detail of the compiler. `code_warntype` will err on the side of coloring types red if they might be a performance concern, so some types may be colored red even if they do not impact performance. diff --git a/stdlib/LinearAlgebra/src/matmul.jl b/stdlib/LinearAlgebra/src/matmul.jl index 8e90b21a4b7ce..2f1a3fe2ba861 100644 --- a/stdlib/LinearAlgebra/src/matmul.jl +++ b/stdlib/LinearAlgebra/src/matmul.jl @@ -337,7 +337,7 @@ end # this indirection allows is to specialize on the types of the wrappers of A and B to some extent, # even though the wrappers are stripped off in mul! # By default, we ignore the wrapper info and forward the arguments to generic_matmatmul! -Base.@constprop :aggressive function generic_matmatmul_wrapper!(C, tA, tB, A, B, α, β, @nospecialize(val)) +function generic_matmatmul_wrapper!(C, tA, tB, A, B, α, β, @nospecialize(val)) generic_matmatmul!(C, tA, tB, A, B, α, β) end diff --git a/stdlib/LinearAlgebra/src/triangular.jl b/stdlib/LinearAlgebra/src/triangular.jl index 31447f1aff5ae..4fed45b009fff 100644 --- a/stdlib/LinearAlgebra/src/triangular.jl +++ b/stdlib/LinearAlgebra/src/triangular.jl @@ -160,7 +160,7 @@ function imag(A::UnitLowerTriangular) L = LowerTriangular(A.data) Lim = similar(L) # must be mutable to set diagonals to zero Lim .= imag.(L) - for i in 1:size(Lim,1) + for i in axes(Lim,1) Lim[i,i] = zero(Lim[i,i]) end return Lim @@ -169,7 +169,7 @@ function imag(A::UnitUpperTriangular) U = UpperTriangular(A.data) Uim = similar(U) # must be mutable to set diagonals to zero Uim .= imag.(U) - for i in 1:size(Uim,1) + for i in axes(Uim,1) Uim[i,i] = zero(Uim[i,i]) end return Uim @@ -200,7 +200,7 @@ end function full!(A::UnitLowerTriangular) B = A.data tril!(B) - for i = 1:size(A,1) + for i in axes(A,1) B[i,i] = oneunit(eltype(B)) end B @@ -213,7 +213,7 @@ end function full!(A::UnitUpperTriangular) B = A.data triu!(B) - for i = 1:size(A,1) + for i in axes(A,1) B[i,i] = oneunit(eltype(B)) end B @@ -350,9 +350,8 @@ Base.@constprop :aggressive function istril(A::LowerTriangular, k::Integer=0) end @inline function _istril(A::LowerTriangular, k) P = parent(A) - m = size(A, 1) - for j in max(1, k + 2):m - all(iszero, view(P, j:min(j - k - 1, m), j)) || return false + for j in max(firstindex(P,2), k + 2):lastindex(P,2) + all(iszero, @view(P[j:min(j - k - 1, end), j])) || return false end return true end @@ -363,8 +362,8 @@ end @inline function _istriu(A::UpperTriangular, k) P = parent(A) m = size(A, 1) - for j in 1:min(m, m + k - 1) - all(iszero, view(P, max(1, j - k + 1):j, j)) || return false + for j in firstindex(P,2):min(m + k - 1, lastindex(P,2)) + all(iszero, @view(P[max(begin, j - k + 1):j, j])) || return false end return true end @@ -374,12 +373,11 @@ istriu(A::Adjoint, k::Integer=0) = istril(A.parent, -k) istriu(A::Transpose, k::Integer=0) = istril(A.parent, -k) function tril!(A::UpperTriangular{T}, k::Integer=0) where {T} - n = size(A,1) if k < 0 fill!(A.data, zero(T)) return A elseif k == 0 - for j in 1:n, i in 1:j-1 + for j in axes(A.data,2), i in intersect(axes(A.data,1), 1:j-1) A.data[i,j] = zero(T) end return A @@ -388,9 +386,8 @@ function tril!(A::UpperTriangular{T}, k::Integer=0) where {T} end end function triu!(A::UpperTriangular, k::Integer=0) - n = size(A,1) if k > 0 - for j in 1:n, i in max(1,j-k+1):j + for j in axes(A.data,2), i in intersect(axes(A.data,1), range(stop=j, length=k)) A.data[i,j] = zero(eltype(A)) end end @@ -398,7 +395,6 @@ function triu!(A::UpperTriangular, k::Integer=0) end function tril!(A::UnitUpperTriangular{T}, k::Integer=0) where {T} - n = size(A,1) if k < 0 fill!(A.data, zero(T)) return UpperTriangular(A.data) @@ -417,19 +413,18 @@ function tril!(A::UnitUpperTriangular{T}, k::Integer=0) where {T} end function triu!(A::UnitUpperTriangular, k::Integer=0) - for i in diagind(A) + for i in diagind(A.data) A.data[i] = oneunit(eltype(A)) end return triu!(UpperTriangular(A.data), k) end function triu!(A::LowerTriangular{T}, k::Integer=0) where {T} - n = size(A,1) if k > 0 fill!(A.data, zero(T)) return A elseif k == 0 - for j in 1:n, i in j+1:n + for j in axes(A.data,2), i in j+1:lastindex(A.data,1) A.data[i,j] = zero(T) end return A @@ -439,9 +434,8 @@ function triu!(A::LowerTriangular{T}, k::Integer=0) where {T} end function tril!(A::LowerTriangular, k::Integer=0) - n = size(A,1) if k < 0 - for j in 1:n, i in j:min(j-k-1,n) + for j in axes(A.data,2), i in intersect(range(j, length=-k), axes(A.data,1)) A.data[i, j] = zero(eltype(A)) end end @@ -449,7 +443,6 @@ function tril!(A::LowerTriangular, k::Integer=0) end function triu!(A::UnitLowerTriangular{T}, k::Integer=0) where T - n = size(A,1) if k > 0 fill!(A.data, zero(T)) return LowerTriangular(A.data) @@ -468,7 +461,7 @@ function triu!(A::UnitLowerTriangular{T}, k::Integer=0) where T end function tril!(A::UnitLowerTriangular, k::Integer=0) - for i in diagind(A) + for i in diagind(A.data) A.data[i] = oneunit(eltype(A)) end return tril!(LowerTriangular(A.data), k) @@ -502,7 +495,7 @@ function -(A::UnitLowerTriangular) Adata = A.data Anew = similar(Adata) # must be mutable, even if Adata is not @. Anew = -Adata - for i = 1:size(A, 1) + for i in axes(A, 1) Anew[i, i] = -A[i, i] end LowerTriangular(Anew) @@ -511,7 +504,7 @@ function -(A::UnitUpperTriangular) Adata = A.data Anew = similar(Adata) # must be mutable, even if Adata is not @. Anew = -Adata - for i = 1:size(A, 1) + for i in axes(A, 1) Anew[i, i] = -A[i, i] end UpperTriangular(Anew) @@ -555,28 +548,30 @@ for (T, UT) in ((:UpperTriangular, :UnitUpperTriangular), (:LowerTriangular, :Un end @inline function _copyto!(A::UpperOrUnitUpperTriangular, B::UnitUpperTriangular) @boundscheck checkbounds(A, axes(B)...) - n = size(B,1) B2 = Base.unalias(A, B) - for j = 1:n - for i = 1:j-1 - @inbounds parent(A)[i,j] = parent(B2)[i,j] + Ap = parent(A) + B2p = parent(B2) + for j in axes(B2,2) + for i in firstindex(Ap,1):j-1 + @inbounds Ap[i,j] = B2p[i,j] end if A isa UpperTriangular # copy diagonal - @inbounds parent(A)[j,j] = B2[j,j] + @inbounds Ap[j,j] = B2[j,j] end end return A end @inline function _copyto!(A::LowerOrUnitLowerTriangular, B::UnitLowerTriangular) @boundscheck checkbounds(A, axes(B)...) - n = size(B,1) B2 = Base.unalias(A, B) - for j = 1:n + Ap = parent(A) + B2p = parent(B2) + for j in axes(B2,2) if A isa LowerTriangular # copy diagonal - @inbounds parent(A)[j,j] = B2[j,j] + @inbounds Ap[j,j] = B2[j,j] end - for i = j+1:n - @inbounds parent(A)[i,j] = parent(B2)[i,j] + for i in j+1:lastindex(Ap,1) + @inbounds Ap[i,j] = B2p[i,j] end end return A @@ -611,10 +606,10 @@ end @boundscheck checkbounds(dest, axes(U)...) isunit = U isa UnitUpperTriangular for col in axes(dest,2) - for row in 1:col-isunit + for row in firstindex(dest,1):col-isunit @inbounds dest[row,col] = U.data[row,col] end - for row in col+!isunit:size(U,1) + for row in col+!isunit:lastindex(dest,1) @inbounds dest[row,col] = U[row,col] end end @@ -624,10 +619,10 @@ end @boundscheck checkbounds(dest, axes(L)...) isunit = L isa UnitLowerTriangular for col in axes(dest,2) - for row in 1:col-!isunit + for row in firstindex(dest,1):col-!isunit @inbounds dest[row,col] = L[row,col] end - for row in col+isunit:size(L,1) + for row in col+isunit:lastindex(dest,1) @inbounds dest[row,col] = L.data[row,col] end end @@ -646,84 +641,84 @@ function checksize1(A, B) end function _triscale!(A::UpperTriangular, B::UpperTriangular, c::Number, _add) - n = checksize1(A, B) + checksize1(A, B) iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) - for j = 1:n - for i = 1:j + for j in axes(B.data,2) + for i in firstindex(B.data,1):j @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j)) end end return A end function _triscale!(A::UpperTriangular, c::Number, B::UpperTriangular, _add) - n = checksize1(A, B) + checksize1(A, B) iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) - for j = 1:n - for i = 1:j + for j in axes(B.data,2) + for i in firstindex(B.data,1):j @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j)) end end return A end function _triscale!(A::UpperOrUnitUpperTriangular, B::UnitUpperTriangular, c::Number, _add) - n = checksize1(A, B) + checksize1(A, B) iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) - for j = 1:n + for j in axes(B.data,2) @inbounds _modify!(_add, c, A, (j,j)) - for i = 1:(j - 1) + for i in firstindex(B.data,1):(j - 1) @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j)) end end return A end function _triscale!(A::UpperOrUnitUpperTriangular, c::Number, B::UnitUpperTriangular, _add) - n = checksize1(A, B) + checksize1(A, B) iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) - for j = 1:n + for j in axes(B.data,2) @inbounds _modify!(_add, c, A, (j,j)) - for i = 1:(j - 1) + for i in firstindex(B.data,1):(j - 1) @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j)) end end return A end function _triscale!(A::LowerTriangular, B::LowerTriangular, c::Number, _add) - n = checksize1(A, B) + checksize1(A, B) iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) - for j = 1:n - for i = j:n + for j in axes(B.data,2) + for i in j:lastindex(B.data,1) @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j)) end end return A end function _triscale!(A::LowerTriangular, c::Number, B::LowerTriangular, _add) - n = checksize1(A, B) + checksize1(A, B) iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) - for j = 1:n - for i = j:n + for j in axes(B.data,2) + for i in j:lastindex(B.data,1) @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j)) end end return A end function _triscale!(A::LowerOrUnitLowerTriangular, B::UnitLowerTriangular, c::Number, _add) - n = checksize1(A, B) + checksize1(A, B) iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) - for j = 1:n + for j in axes(B.data,2) @inbounds _modify!(_add, c, A, (j,j)) - for i = (j + 1):n + for i in (j + 1):lastindex(B.data,1) @inbounds _modify!(_add, B.data[i,j] * c, A.data, (i,j)) end end return A end function _triscale!(A::LowerOrUnitLowerTriangular, c::Number, B::UnitLowerTriangular, _add) - n = checksize1(A, B) + checksize1(A, B) iszero(_add.alpha) && return _rmul_or_fill!(A, _add.beta) - for j = 1:n + for j in axes(B.data,2) @inbounds _modify!(_add, c, A, (j,j)) - for i = (j + 1):n + for i in (j + 1):lastindex(B.data,1) @inbounds _modify!(_add, c * B.data[i,j], A.data, (i,j)) end end @@ -731,36 +726,36 @@ function _triscale!(A::LowerOrUnitLowerTriangular, c::Number, B::UnitLowerTriang end function _trirdiv!(A::UpperTriangular, B::UpperOrUnitUpperTriangular, c::Number) - n = checksize1(A, B) - for j in 1:n - for i in 1:j + checksize1(A, B) + for j in axes(B,2) + for i in firstindex(B,1):j @inbounds A[i, j] = B[i, j] / c end end return A end function _trirdiv!(A::LowerTriangular, B::LowerOrUnitLowerTriangular, c::Number) - n = checksize1(A, B) - for j in 1:n - for i in j:n + checksize1(A, B) + for j in axes(B,2) + for i in j:lastindex(B,1) @inbounds A[i, j] = B[i, j] / c end end return A end function _trildiv!(A::UpperTriangular, c::Number, B::UpperOrUnitUpperTriangular) - n = checksize1(A, B) - for j in 1:n - for i in 1:j + checksize1(A, B) + for j in axes(B,2) + for i in firstindex(B,1):j @inbounds A[i, j] = c \ B[i, j] end end return A end function _trildiv!(A::LowerTriangular, c::Number, B::LowerOrUnitLowerTriangular) - n = checksize1(A, B) - for j in 1:n - for i in j:n + checksize1(A, B) + for j in axes(B,2) + for i in j:lastindex(B,1) @inbounds A[i, j] = c \ B[i, j] end end @@ -779,7 +774,7 @@ function dot(x::AbstractVector, A::UpperTriangular, y::AbstractVector) end x₁ = x[1] r = dot(x₁, A[1,1], y[1]) - @inbounds for j in 2:m + @inbounds for j in axes(A, 2)[2:end] yj = y[j] if !iszero(yj) temp = adjoint(A[1,j]) * x₁ @@ -800,7 +795,7 @@ function dot(x::AbstractVector, A::UnitUpperTriangular, y::AbstractVector) end x₁ = first(x) r = dot(x₁, y[1]) - @inbounds for j in 2:m + @inbounds for j in axes(A, 2)[2:end] yj = y[j] if !iszero(yj) temp = adjoint(A[1,j]) * x₁ @@ -821,11 +816,11 @@ function dot(x::AbstractVector, A::LowerTriangular, y::AbstractVector) return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) end r = zero(typeof(dot(first(x), first(A), first(y)))) - @inbounds for j in 1:m + @inbounds for j in axes(A, 2) yj = y[j] if !iszero(yj) temp = adjoint(A[j,j]) * x[j] - @simd for i in j+1:m + @simd for i in j+1:lastindex(A,1) temp += adjoint(A[i,j]) * x[i] end r += dot(temp, yj) @@ -841,11 +836,11 @@ function dot(x::AbstractVector, A::UnitLowerTriangular, y::AbstractVector) return dot(zero(eltype(x)), zero(eltype(A)), zero(eltype(y))) end r = zero(typeof(dot(first(x), first(y)))) - @inbounds for j in 1:m + @inbounds for j in axes(A, 2) yj = y[j] if !iszero(yj) temp = x[j] - @simd for i in j+1:m + @simd for i in j+1:lastindex(A,1) temp += adjoint(A[i,j]) * x[i] end r += dot(temp, yj) @@ -952,25 +947,24 @@ function kron!(C::LowerTriangular{<:Any,<:StridedMaybeAdjOrTransMat}, A::LowerTr end function _triukron!(C, A, B) - n_A = size(A, 1) n_B = size(B, 1) - @inbounds for j = 1:n_A + @inbounds for j in axes(A,2) jnB = (j - 1) * n_B - for i = 1:(j-1) + for i in firstindex(A,1):(j-1) Aij = A[i, j] inB = (i - 1) * n_B - for l = 1:n_B - for k = 1:l + for l in axes(B,2) + for k in firstindex(B,1):l C[inB+k, jnB+l] = Aij * B[k, l] end - for k = 1:(l-1) + for k in firstindex(B,1):(l-1) C[inB+l, jnB+k] = zero(C[inB+k, jnB+l]) end end end Ajj = A[j, j] - for l = 1:n_B - for k = 1:l + for l in axes(B,2) + for k in firstindex(B,1):l C[jnB+k, jnB+l] = Ajj * B[k, l] end end @@ -980,22 +974,22 @@ end function _trilkron!(C, A, B) n_A = size(A, 1) n_B = size(B, 1) - @inbounds for j = 1:n_A + @inbounds for j in axes(A,2) jnB = (j - 1) * n_B Ajj = A[j, j] - for l = 1:n_B - for k = l:n_B + for l in axes(B,2) + for k in l:lastindex(B,1) C[jnB+k, jnB+l] = Ajj * B[k, l] end end - for i = (j+1):n_A + for i in (j+1):n_A Aij = A[i, j] inB = (i - 1) * n_B - for l = 1:n_B - for k = l:n_B + for l in axes(B,2) + for k in l:lastindex(B,1) C[inB+k, jnB+l] = Aij * B[k, l] end - for k = (l+1):n_B + for k in (l+1):lastindex(B,1) C[inB+l, jnB+k] = zero(C[inB+k, jnB+l]) end end @@ -1063,6 +1057,7 @@ end for TC in (:AbstractVector, :AbstractMatrix) @eval @inline function _mul!(C::$TC, A::AbstractTriangular, B::AbstractVector, alpha::Number, beta::Number) + check_A_mul_B!_sizes(size(C), size(A), size(B)) if isone(alpha) && iszero(beta) return _trimul!(C, A, B) else @@ -1075,6 +1070,7 @@ for (TA, TB) in ((:AbstractTriangular, :AbstractMatrix), (:AbstractTriangular, :AbstractTriangular) ) @eval @inline function _mul!(C::AbstractMatrix, A::$TA, B::$TB, alpha::Number, beta::Number) + check_A_mul_B!_sizes(size(C), size(A), size(B)) if isone(alpha) && iszero(beta) return _trimul!(C, A, B) else @@ -1198,7 +1194,7 @@ function eigvecs(A::UpperTriangular{<:BlasFloat,<:StridedMatrix}) LAPACK.trevc!('R', 'A', BlasInt[], triu!(A.data)) end function eigvecs(A::UnitUpperTriangular{<:BlasFloat,<:StridedMatrix}) - for i = 1:size(A, 1) + for i in axes(A, 1) A.data[i,i] = 1 end LAPACK.trevc!('R', 'A', BlasInt[], triu!(A.data)) @@ -1207,7 +1203,7 @@ function eigvecs(A::LowerTriangular{<:BlasFloat,<:StridedMatrix}) LAPACK.trevc!('L', 'A', BlasInt[], copy(tril!(A.data)')) end function eigvecs(A::UnitLowerTriangular{<:BlasFloat,<:StridedMatrix}) - for i = 1:size(A, 1) + for i in axes(A, 1) A.data[i,i] = 1 end LAPACK.trevc!('L', 'A', BlasInt[], copy(tril!(A.data)')) @@ -1230,7 +1226,7 @@ for (t, unitt) in ((UpperTriangular, UnitUpperTriangular), function (*)(A::$unitt, x::Number) B = $t(A.data)*x - for i = 1:size(A, 1) + for i in axes(A, 1) B.data[i,i] = x end return B @@ -1245,7 +1241,7 @@ for (t, unitt) in ((UpperTriangular, UnitUpperTriangular), function (*)(x::Number, A::$unitt) B = x*$t(A.data) - for i = 1:size(A, 1) + for i in axes(A, 1) B.data[i,i] = x end return B @@ -1261,7 +1257,7 @@ for (t, unitt) in ((UpperTriangular, UnitUpperTriangular), function (/)(A::$unitt, x::Number) B = $t(A.data)/x invx = inv(x) - for i = 1:size(A, 1) + for i in axes(A, 1) B.data[i,i] = invx end return B @@ -1277,7 +1273,7 @@ for (t, unitt) in ((UpperTriangular, UnitUpperTriangular), function (\)(x::Number, A::$unitt) B = x\$t(A.data) invx = inv(x) - for i = 1:size(A, 1) + for i in axes(A, 1) B.data[i,i] = invx end return B @@ -1288,33 +1284,25 @@ end ## Generic triangular multiplication function generic_trimatmul!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractVecOrMat) require_one_based_indexing(C, A, B) - m, n = size(B, 1), size(B, 2) - N = size(A, 1) - if m != N - throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $(size(A,1)), has size $m")) - end - mc, nc = size(C, 1), size(C, 2) - if mc != N || nc != n - throw(DimensionMismatch(lazy"output has dimensions ($mc,$nc), should have ($N,$n)")) - end + check_A_mul_B!_sizes(size(C), size(A), size(B)) oA = oneunit(eltype(A)) unit = isunitc == 'U' @inbounds if uploc == 'U' if tfun === identity - for j in 1:n - for i in 1:m + for j in axes(B,2) + for i in axes(B,1) Cij = (unit ? oA : A[i,i]) * B[i,j] - for k in i + 1:m + for k in i + 1:lastindex(B,1) Cij += A[i,k] * B[k,j] end C[i,j] = Cij end end else # tfun in (transpose, adjoint) - for j in 1:n - for i in m:-1:1 + for j in axes(B,2) + for i in reverse(axes(B,1)) Cij = (unit ? oA : tfun(A[i,i])) * B[i,j] - for k in 1:i - 1 + for k in firstindex(B,1):i - 1 Cij += tfun(A[k,i]) * B[k,j] end C[i,j] = Cij @@ -1323,20 +1311,20 @@ function generic_trimatmul!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, end else # uploc == 'L' if tfun === identity - for j in 1:n - for i in m:-1:1 + for j in axes(B,2) + for i in reverse(axes(B,1)) Cij = (unit ? oA : A[i,i]) * B[i,j] - for k in 1:i - 1 + for k in firstindex(B,1):i - 1 Cij += A[i,k] * B[k,j] end C[i,j] = Cij end end else # tfun in (transpose, adjoint) - for j in 1:n - for i in 1:m + for j in axes(B,2) + for i in axes(B,1) Cij = (unit ? oA : tfun(A[i,i])) * B[i,j] - for k in i + 1:m + for k in i + 1:lastindex(B,1) Cij += tfun(A[k,i]) * B[k,j] end C[i,j] = Cij @@ -1348,34 +1336,26 @@ function generic_trimatmul!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, end # conjugate cases function generic_trimatmul!(C::AbstractVecOrMat, uploc, isunitc, ::Function, xA::AdjOrTrans, B::AbstractVecOrMat) + require_one_based_indexing(C, xA, B) + check_A_mul_B!_sizes(size(C), size(xA), size(B)) A = parent(xA) - require_one_based_indexing(C, A, B) - m, n = size(B, 1), size(B, 2) - N = size(A, 1) - if m != N - throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $(size(A,1)), has size $m")) - end - mc, nc = size(C, 1), size(C, 2) - if mc != N || nc != n - throw(DimensionMismatch(lazy"output has dimensions ($mc,$nc), should have ($N,$n)")) - end oA = oneunit(eltype(A)) unit = isunitc == 'U' @inbounds if uploc == 'U' - for j in 1:n - for i in 1:m + for j in axes(B,2) + for i in axes(B,1) Cij = (unit ? oA : conj(A[i,i])) * B[i,j] - for k in i + 1:m + for k in i + 1:lastindex(B,1) Cij += conj(A[i,k]) * B[k,j] end C[i,j] = Cij end end else # uploc == 'L' - for j in 1:n - for i in m:-1:1 + for j in axes(B,2) + for i in reverse(axes(B,1)) Cij = (unit ? oA : conj(A[i,i])) * B[i,j] - for k in 1:i - 1 + for k in firstindex(B,1):i - 1 Cij += conj(A[i,k]) * B[k,j] end C[i,j] = Cij @@ -1387,33 +1367,25 @@ end function generic_mattrimul!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractMatrix) require_one_based_indexing(C, A, B) - m, n = size(A, 1), size(A, 2) - N = size(B, 1) - if n != N - throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $n, has size $N")) - end - mc, nc = size(C, 1), size(C, 2) - if mc != m || nc != N - throw(DimensionMismatch(lazy"output has dimensions ($mc,$nc), should have ($m,$N)")) - end + check_A_mul_B!_sizes(size(C), size(A), size(B)) oB = oneunit(eltype(B)) unit = isunitc == 'U' @inbounds if uploc == 'U' if tfun === identity - for i in 1:m - for j in n:-1:1 + for i in axes(A,1) + for j in reverse(axes(A,2)) Cij = A[i,j] * (unit ? oB : B[j,j]) - for k in 1:j - 1 + for k in firstindex(A,2):j - 1 Cij += A[i,k] * B[k,j] end C[i,j] = Cij end end else # tfun in (transpose, adjoint) - for i in 1:m - for j in 1:n + for i in axes(A,1) + for j in axes(A,2) Cij = A[i,j] * (unit ? oB : tfun(B[j,j])) - for k in j + 1:n + for k in j + 1:lastindex(A,2) Cij += A[i,k] * tfun(B[j,k]) end C[i,j] = Cij @@ -1422,20 +1394,20 @@ function generic_mattrimul!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A end else # uploc == 'L' if tfun === identity - for i in 1:m - for j in 1:n + for i in axes(A,1) + for j in axes(A,2) Cij = A[i,j] * (unit ? oB : B[j,j]) - for k in j + 1:n + for k in j + 1:lastindex(A,2) Cij += A[i,k] * B[k,j] end C[i,j] = Cij end end else # tfun in (transpose, adjoint) - for i in 1:m - for j in n:-1:1 + for i in axes(A,1) + for j in reverse(axes(A,2)) Cij = A[i,j] * (unit ? oB : tfun(B[j,j])) - for k in 1:j - 1 + for k in firstindex(A,2):j - 1 Cij += A[i,k] * tfun(B[j,k]) end C[i,j] = Cij @@ -1447,34 +1419,26 @@ function generic_mattrimul!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A end # conjugate cases function generic_mattrimul!(C::AbstractMatrix, uploc, isunitc, ::Function, A::AbstractMatrix, xB::AdjOrTrans) + require_one_based_indexing(C, A, xB) + check_A_mul_B!_sizes(size(C), size(A), size(xB)) B = parent(xB) - require_one_based_indexing(C, A, B) - m, n = size(A, 1), size(A, 2) - N = size(B, 1) - if n != N - throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $n, has size $N")) - end - mc, nc = size(C, 1), size(C, 2) - if mc != m || nc != N - throw(DimensionMismatch(lazy"output has dimensions ($mc,$nc), should have ($m,$N)")) - end oB = oneunit(eltype(B)) unit = isunitc == 'U' @inbounds if uploc == 'U' - for i in 1:m - for j in n:-1:1 + for i in axes(A,1) + for j in reverse(axes(A,2)) Cij = A[i,j] * (unit ? oB : conj(B[j,j])) - for k in 1:j - 1 + for k in firstindex(A,2):j - 1 Cij += A[i,k] * conj(B[k,j]) end C[i,j] = Cij end end else # uploc == 'L' - for i in 1:m - for j in 1:n + for i in axes(A,1) + for j in axes(A,2) Cij = A[i,j] * (unit ? oB : conj(B[j,j])) - for k in j + 1:n + for k in j + 1:lastindex(A,2) Cij += A[i,k] * conj(B[k,j]) end C[i,j] = Cij @@ -1498,7 +1462,7 @@ end function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractVecOrMat) require_one_based_indexing(C, A, B) mA, nA = size(A) - m, n = size(B, 1), size(B,2) + m = size(B, 1) if nA != m throw(DimensionMismatch(lazy"second dimension of left hand side A, $nA, and first dimension of right hand side B, $m, must be equal")) end @@ -1510,30 +1474,30 @@ function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, @inbounds if uploc == 'U' if isunitc == 'N' if tfun === identity - for k in 1:n + for k in axes(B,2) amm = A[m,m] iszero(amm) && throw(SingularException(m)) Cm = C[m,k] = amm \ B[m,k] # fill C-column - for i in m-1:-1:1 + for i in reverse(axes(B,1))[2:end] C[i,k] = oA \ B[i,k] - _ustrip(A[i,m]) * Cm end - for j in m-1:-1:1 + for j in reverse(axes(B,1))[2:end] ajj = A[j,j] iszero(ajj) && throw(SingularException(j)) Cj = C[j,k] = _ustrip(ajj) \ C[j,k] - for i in j-1:-1:1 + for i in j-1:-1:firstindex(B,1) C[i,k] -= _ustrip(A[i,j]) * Cj end end end else # tfun in (adjoint, transpose) - for k in 1:n - for j in 1:m + for k in axes(B,2) + for j in axes(B,1) ajj = A[j,j] iszero(ajj) && throw(SingularException(j)) Bj = B[j,k] - for i in 1:j-1 + for i in firstindex(A,1):j-1 Bj -= tfun(A[i,j]) * C[i,k] end C[j,k] = tfun(ajj) \ Bj @@ -1542,24 +1506,24 @@ function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, end else # isunitc == 'U' if tfun === identity - for k in 1:n + for k in axes(B,2) Cm = C[m,k] = oA \ B[m,k] # fill C-column - for i in m-1:-1:1 + for i in reverse(axes(B,1))[2:end] C[i,k] = oA \ B[i,k] - _ustrip(A[i,m]) * Cm end - for j in m-1:-1:1 + for j in reverse(axes(B,1))[2:end] Cj = C[j,k] - for i in 1:j-1 + for i in firstindex(A,1):j-1 C[i,k] -= _ustrip(A[i,j]) * Cj end end end else # tfun in (adjoint, transpose) - for k in 1:n - for j in 1:m + for k in axes(B,2) + for j in axes(B,1) Bj = B[j,k] - for i in 1:j-1 + for i in firstindex(A,1):j-1 Bj -= tfun(A[i,j]) * C[i,k] end C[j,k] = oA \ Bj @@ -1570,30 +1534,30 @@ function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, else # uploc == 'L' if isunitc == 'N' if tfun === identity - for k in 1:n + for k in axes(B,2) a11 = A[1,1] iszero(a11) && throw(SingularException(1)) C1 = C[1,k] = a11 \ B[1,k] # fill C-column - for i in 2:m + for i in axes(B,1)[2:end] C[i,k] = oA \ B[i,k] - _ustrip(A[i,1]) * C1 end - for j in 2:m + for j in axes(B,1)[2:end] ajj = A[j,j] iszero(ajj) && throw(SingularException(j)) Cj = C[j,k] = _ustrip(ajj) \ C[j,k] - for i in j+1:m + for i in j+1:lastindex(A,1) C[i,k] -= _ustrip(A[i,j]) * Cj end end end else # tfun in (adjoint, transpose) - for k in 1:n - for j in m:-1:1 + for k in axes(B,2) + for j in reverse(axes(B,1)) ajj = A[j,j] iszero(ajj) && throw(SingularException(j)) Bj = B[j,k] - for i in j+1:m + for i in j+1:lastindex(A,1) Bj -= tfun(A[i,j]) * C[i,k] end C[j,k] = tfun(ajj) \ Bj @@ -1602,24 +1566,24 @@ function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, tfun::Function, end else # isunitc == 'U' if tfun === identity - for k in 1:n + for k in axes(B,2) C1 = C[1,k] = oA \ B[1,k] # fill C-column - for i in 2:m + for i in axes(B,1)[2:end] C[i,k] = oA \ B[i,k] - _ustrip(A[i,1]) * C1 end - for j in 2:m + for j in axes(B,1)[2:end] Cj = C[j,k] - for i in j+1:m + for i in j+1:lastindex(A,1) C[i,k] -= _ustrip(A[i,j]) * Cj end end end else # tfun in (adjoint, transpose) - for k in 1:n - for j in m:-1:1 + for k in axes(B,2) + for j in reverse(axes(B,1)) Bj = B[j,k] - for i in j+1:m + for i in j+1:lastindex(A,1) Bj -= tfun(A[i,j]) * C[i,k] end C[j,k] = oA \ Bj @@ -1635,7 +1599,7 @@ function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, ::Function, xA: A = parent(xA) require_one_based_indexing(C, A, B) mA, nA = size(A) - m, n = size(B, 1), size(B,2) + m = size(B, 1) if nA != m throw(DimensionMismatch(lazy"second dimension of left hand side A, $nA, and first dimension of right hand side B, $m, must be equal")) end @@ -1646,33 +1610,33 @@ function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, ::Function, xA: oA = oneunit(eltype(A)) @inbounds if uploc == 'U' if isunitc == 'N' - for k in 1:n + for k in axes(B,2) amm = conj(A[m,m]) iszero(amm) && throw(SingularException(m)) Cm = C[m,k] = amm \ B[m,k] # fill C-column - for i in m-1:-1:1 + for i in reverse(axes(B,1))[2:end] C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,m])) * Cm end - for j in m-1:-1:1 + for j in reverse(axes(B,1))[2:end] ajj = conj(A[j,j]) iszero(ajj) && throw(SingularException(j)) Cj = C[j,k] = _ustrip(ajj) \ C[j,k] - for i in j-1:-1:1 + for i in j-1:-1:firstindex(A,1) C[i,k] -= _ustrip(conj(A[i,j])) * Cj end end end else # isunitc == 'U' - for k in 1:n + for k in axes(B,2) Cm = C[m,k] = oA \ B[m,k] # fill C-column - for i in m-1:-1:1 + for i in reverse(axes(B,1))[2:end] C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,m])) * Cm end - for j in m-1:-1:1 + for j in reverse(axes(B,1))[2:end] Cj = C[j,k] - for i in 1:j-1 + for i in firstindex(A,1):j-1 C[i,k] -= _ustrip(conj(A[i,j])) * Cj end end @@ -1680,33 +1644,33 @@ function generic_trimatdiv!(C::AbstractVecOrMat, uploc, isunitc, ::Function, xA: end else # uploc == 'L' if isunitc == 'N' - for k in 1:n + for k in axes(B,2) a11 = conj(A[1,1]) iszero(a11) && throw(SingularException(1)) C1 = C[1,k] = a11 \ B[1,k] # fill C-column - for i in 2:m + for i in axes(B,1)[2:end] C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,1])) * C1 end - for j in 2:m + for j in axes(A,2)[2:end] ajj = conj(A[j,j]) iszero(ajj) && throw(SingularException(j)) Cj = C[j,k] = _ustrip(ajj) \ C[j,k] - for i in j+1:m + for i in j+1:lastindex(A,1) C[i,k] -= _ustrip(conj(A[i,j])) * Cj end end end else # isunitc == 'U' - for k in 1:n + for k in axes(B,2) C1 = C[1,k] = oA \ B[1,k] # fill C-column - for i in 2:m + for i in axes(B,1)[2:end] C[i,k] = oA \ B[i,k] - _ustrip(conj(A[i,1])) * C1 end - for j in 1:m + for j in axes(A,2) Cj = C[j,k] - for i in j+1:m + for i in j+1:lastindex(A,1) C[i,k] -= _ustrip(conj(A[i,j])) * Cj end end @@ -1718,7 +1682,7 @@ end function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A::AbstractMatrix, B::AbstractMatrix) require_one_based_indexing(C, A, B) - m, n = size(A) + n = size(A,2) if size(B, 1) != n throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $n, has size $(size(B,1))")) end @@ -1729,10 +1693,10 @@ function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A unit = isunitc == 'U' @inbounds if uploc == 'U' if tfun === identity - for i in 1:m - for j in 1:n + for i in axes(A,1) + for j in axes(A,2) Aij = A[i,j] - for k in 1:j - 1 + for k in firstindex(B,1):j - 1 Aij -= C[i,k]*B[k,j] end unit || (iszero(B[j,j]) && throw(SingularException(j))) @@ -1740,10 +1704,10 @@ function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A end end else # tfun in (adjoint, transpose) - for i in 1:m - for j in n:-1:1 + for i in axes(A,1) + for j in reverse(axes(A,2)) Aij = A[i,j] - for k in j + 1:n + for k in j + 1:lastindex(B,2) Aij -= C[i,k]*tfun(B[j,k]) end unit || (iszero(B[j,j]) && throw(SingularException(j))) @@ -1753,10 +1717,10 @@ function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A end else # uploc == 'L' if tfun === identity - for i in 1:m - for j in n:-1:1 + for i in axes(A,1) + for j in reverse(axes(A,2)) Aij = A[i,j] - for k in j + 1:n + for k in j + 1:lastindex(B,1) Aij -= C[i,k]*B[k,j] end unit || (iszero(B[j,j]) && throw(SingularException(j))) @@ -1764,10 +1728,10 @@ function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, tfun::Function, A end end else # tfun in (adjoint, transpose) - for i in 1:m - for j in 1:n + for i in axes(A,1) + for j in axes(A,2) Aij = A[i,j] - for k in 1:j - 1 + for k in firstindex(B,2):j - 1 Aij -= C[i,k]*tfun(B[j,k]) end unit || (iszero(B[j,j]) && throw(SingularException(j))) @@ -1781,7 +1745,7 @@ end function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, ::Function, A::AbstractMatrix, xB::AdjOrTrans) B = parent(xB) require_one_based_indexing(C, A, B) - m, n = size(A) + n = size(A,2) if size(B, 1) != n throw(DimensionMismatch(lazy"right hand side B needs first dimension of size $n, has size $(size(B,1))")) end @@ -1791,10 +1755,10 @@ function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, ::Function, A::Ab oB = oneunit(eltype(B)) unit = isunitc == 'U' if uploc == 'U' - @inbounds for i in 1:m - for j in 1:n + @inbounds for i in axes(A,1) + for j in axes(A,2) Aij = A[i,j] - for k in 1:j - 1 + for k in firstindex(B,1):j - 1 Aij -= C[i,k]*conj(B[k,j]) end unit || (iszero(B[j,j]) && throw(SingularException(j))) @@ -1802,10 +1766,10 @@ function generic_mattridiv!(C::AbstractMatrix, uploc, isunitc, ::Function, A::Ab end end else # uploc == 'L' - @inbounds for i in 1:m - for j in n:-1:1 + @inbounds for i in axes(A,1) + for j in reverse(axes(A,2)) Aij = A[i,j] - for k in j + 1:n + for k in j + 1:lastindex(B,1) Aij -= C[i,k]*conj(B[k,j]) end unit || (iszero(B[j,j]) && throw(SingularException(j))) @@ -1915,14 +1879,14 @@ function powm!(A0::UpperTriangular, p::Real) rmul!(A0, 1/normA0) theta = [1.53e-5, 2.25e-3, 1.92e-2, 6.08e-2, 1.25e-1, 2.03e-1, 2.84e-1] - n = checksquare(A0) + checksquare(A0) A, m, s = invsquaring(A0, theta) A = I - A # Compute accurate diagonal of I - T sqrt_diag!(A0, A, s) - for i = 1:n + for i in axes(A,1) A[i, i] = -A[i, i] end # Compute the Padé approximant @@ -1930,10 +1894,10 @@ function powm!(A0::UpperTriangular, p::Real) triu!(A) S = c * A Stmp = similar(S) - for j = m-1:-1:1 + for j in m-1:-1:1 j4 = 4 * j c = (-p - j) / (j4 + 2) - for i = 1:n + for i in axes(S,1) @inbounds S[i, i] = S[i, i] + 1 end copyto!(Stmp, S) @@ -1941,20 +1905,20 @@ function powm!(A0::UpperTriangular, p::Real) ldiv!(Stmp, S) c = (p - j) / (j4 - 2) - for i = 1:n + for i in axes(S,1) @inbounds S[i, i] = S[i, i] + 1 end copyto!(Stmp, S) mul!(S, A, c) ldiv!(Stmp, S) end - for i = 1:n + for i in axes(S,1) S[i, i] = S[i, i] + 1 end copyto!(Stmp, S) mul!(S, A, -p) ldiv!(Stmp, S) - for i = 1:n + for i in axes(S,1) @inbounds S[i, i] = S[i, i] + 1 end @@ -1986,7 +1950,7 @@ log(A::UnitLowerTriangular) = copy(transpose(log(copy(transpose(A))))) function log_quasitriu(A0::AbstractMatrix{T}) where T<:BlasFloat # allocate real A if log(A) will be real and complex A otherwise - n = checksquare(A0) + checksquare(A0) if isreal(A0) && (!istriu(A0) || !any(x -> real(x) < zero(real(T)), diag(A0))) A = T <: Complex ? real(A0) : copy(A0) else @@ -1994,7 +1958,7 @@ function log_quasitriu(A0::AbstractMatrix{T}) where T<:BlasFloat end if A0 isa UnitUpperTriangular A = UpperTriangular(parent(A)) - @inbounds for i in 1:n + @inbounds for i in axes(A,1) A[i,i] = 1 end end @@ -2023,13 +1987,13 @@ function _log_quasitriu!(A0, A) # Get the Gauss-Legendre quadrature points and weights R = zeros(Float64, m, m) - for i = 1:m - 1 + for i in 1:m - 1 R[i,i+1] = i / sqrt((2 * i)^2 - 1) R[i+1,i] = R[i,i+1] end x,V = eigen(R) w = Vector{Float64}(undef, m) - for i = 1:m + for i in 1:m x[i] = (x[i] + 1) / 2 w[i] = V[1,i]^2 end @@ -2039,9 +2003,9 @@ function _log_quasitriu!(A0, A) n = size(A, 1) Y = zeros(t, n, n) B = similar(A) - for k = 1:m + for k in 1:m B .= t(x[k]) .* A - @inbounds for i in 1:n + @inbounds for i in axes(B,1) B[i,i] += 1 end Y .+= t(w[k]) .* rdiv_quasitriu!(A, B) @@ -2072,7 +2036,6 @@ function _find_params_log_quasitriu!(A) 2.060962623452836e-001, 2.879093714241194e-001] tmax = size(theta, 1) - n = size(A, 1) p = 0 m = 0 @@ -2089,7 +2052,7 @@ function _find_params_log_quasitriu!(A) s0 = s # Compute repeated roots - for k = 1:min(s, maxsqrt) + for k in 1:min(s, maxsqrt) _sqrt_quasitriu!(A isa UpperTriangular ? parent(A) : A, A) end @@ -2150,7 +2113,7 @@ function _find_params_log_quasitriu!(A) end _sqrt_quasitriu!(A isa UpperTriangular ? parent(A) : A, A) copyto!(AmI, A) - for i in 1:n + for i in axes(AmI,1) @inbounds AmI[i,i] -= 1 end mul!(AmI2, AmI, AmI) @@ -2163,9 +2126,8 @@ end # Compute accurate diagonal of A = A0^s - I function sqrt_diag!(A0::UpperTriangular, A::UpperTriangular, s) - n = checksquare(A0) - T = eltype(A) - @inbounds for i = 1:n + checksquare(A0) + @inbounds for i in axes(A0,1) a = complex(A0[i,i]) A[i,i] = _sqrt_pow(a, s) end @@ -2206,7 +2168,7 @@ function _sqrt_pow(a::Number, s) z0 = a - 1 a = sqrt(a) r = 1 + a - for j = 1:s0-1 + for j in 1:s0-1 a = sqrt(a) r = r * (1 + a) end @@ -2375,7 +2337,7 @@ function invsquaring(A0::UpperTriangular, theta) # assumes theta is in ascending order maxsqrt = 100 tmax = size(theta, 1) - n = checksquare(A0) + checksquare(A0) A = complex(copy(A0)) p = 0 m = 0 @@ -2390,7 +2352,7 @@ function invsquaring(A0::UpperTriangular, theta) s = s + 1 end s0 = s - for k = 1:min(s, maxsqrt) + for k in 1:min(s, maxsqrt) A = sqrt(A) end @@ -2464,8 +2426,8 @@ end # Compute accurate diagonal and superdiagonal of A = A0^p function blockpower!(A::UpperTriangular, A0::UpperTriangular, p) - n = checksquare(A0) - @inbounds for k = 1:n-1 + checksquare(A0) + @inbounds for k in axes(A0,1)[1:end-1] Ak = complex(A0[k,k]) Akp1 = complex(A0[k+1,k+1]) @@ -2500,10 +2462,10 @@ unw(x::Number) = ceil((imag(x) - pi) / (2 * pi)) # compute A / B for upper quasi-triangular B, possibly overwriting B function rdiv_quasitriu!(A, B) - n = checksquare(A) + checksquare(A) AG = copy(A) # use Givens rotations to annihilate 2x2 blocks - @inbounds for k in 1:(n-1) + @inbounds for k in axes(B,2)[1:end-1] s = B[k+1,k] iszero(s) && continue # 1x1 block G = first(givens(B[k+1,k+1], s, k, k+1)) @@ -2518,15 +2480,14 @@ end sqrt(A::UpperTriangular) = sqrt_quasitriu(A) function sqrt(A::UnitUpperTriangular{T}) where T B = A.data - n = checksquare(B) t = typeof(sqrt(zero(T))) - R = Matrix{t}(I, n, n) + R = Matrix{t}(I, size(A)) tt = typeof(oneunit(t)*oneunit(t)) half = inv(R[1,1]+R[1,1]) # for general, algebraic cases. PR#20214 - @inbounds for j = 1:n - for i = j-1:-1:1 + @inbounds for j in axes(B,2) + for i in j-1:-1:firstindex(B) r::tt = B[i,j] - @simd for k = i+1:j-1 + @simd for k in i+1:j-1 r -= R[i,k]*R[k,j] end iszero(r) || (R[i,j] = half*r) @@ -2548,7 +2509,7 @@ function sqrt_quasitriu(A0; blockwidth = eltype(A0) <: Complex ? 512 : 256) if isreal(A0) is_sqrt_real = true if istriu(A0) - for i in 1:n + for i in axes(A0,1) Aii = real(A0[i,i]) if Aii < zero(Aii) is_sqrt_real = false @@ -2557,15 +2518,15 @@ function sqrt_quasitriu(A0; blockwidth = eltype(A0) <: Complex ? 512 : 256) end end if is_sqrt_real - R = zeros(Tr, n, n) + R = zeros(Tr, size(A0)) A = real(A0) else - R = zeros(Tc, n, n) + R = zeros(Tc, size(A0)) A = A0 end else A = A0 - R = zeros(Tc, n, n) + R = zeros(Tc, size(A0)) end _sqrt_quasitriu!(R, A; blockwidth=blockwidth, n=n) Rc = eltype(A0) <: Real ? R : complex(R) @@ -2865,7 +2826,7 @@ det(A::LowerTriangular) = prod(diag(A.data)) function logabsdet(A::Union{UpperTriangular{T},LowerTriangular{T}}) where T sgn = one(T) abs_det = zero(real(T)) - @inbounds for i in 1:size(A,1) + @inbounds for i in axes(A.data,1) diag_i = A.data[i,i] sgn *= sign(diag_i) abs_det += log(abs(diag_i)) @@ -2955,8 +2916,8 @@ end M_L₁ = zeros(T,4,4) M_Bᵢⱼ⁽⁰⁾ = zeros(T,2,2) M_Bᵢⱼ⁽¹⁾ = zeros(T,2,2) - for k = 1:n-1 - for i = 1:n-k + for k in axes(A,2)[1:end-1] + for i in axes(A,2)[1:end-k] if sizes[i] == 0 || sizes[i+k] == 0 continue end k₁, k₂ = i+1+(sizes[i+1]==0), i+k-1 i₁, i₂, j₁, j₂, s₁, s₂ = i, i+sizes[i]-1, i+k, i+k+sizes[i+k]-1, sizes[i], sizes[i+k] @@ -2982,7 +2943,11 @@ end end end # Make quasi triangular - for j=1:n for i=j+1+(sizes[j]==2):n A[i,j] = 0 end end + for j in axes(A,2) + for i=j+1+(sizes[j]==2):lastindex(A,1) + A[i,j] = 0 + end + end return A end diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index bea8f288937d0..694d1292b02ab 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -9,7 +9,7 @@ Profiling support. - `@profile foo()` to profile a specific call. - `Profile.print()` to print the report. Paths are clickable links in supported terminals and specialized for JULIA_EDITOR etc. - `Profile.clear()` to clear the buffer. -- Send a $(Sys.isbsd() ? "SIGINFO (ctrl-t)" : "SIGUSR1") signal to the process to automatically trigger a profile and print. +- Send a SIGUSR1 (on linux) or SIGINFO (on macOS/BSD) signal to the process to automatically trigger a profile and print. i.e. `kill -s SIGUSR1/SIGINFO 1234`, where 1234 is the pid of the julia process. On macOS & BSD platforms `ctrl-t` can be used directly. ## Memory profiling - `Profile.Allocs.@profile [sample_rate=0.1] foo()` to sample allocations within a specific call. A sample rate of 1.0 will record everything; 0.0 will record nothing. diff --git a/stdlib/REPL/src/docview.jl b/stdlib/REPL/src/docview.jl index 3c5e102bb657e..566046f325499 100644 --- a/stdlib/REPL/src/docview.jl +++ b/stdlib/REPL/src/docview.jl @@ -473,7 +473,12 @@ function repl_corrections(io::IO, s, mod::Module) quot = any(isspace, s) ? "'" : "" print(io, quot) printstyled(io, s, color=:cyan) - print(io, quot, '\n') + print(io, quot) + if Base.identify_package(s) === nothing + print(io, '\n') + else + print(io, ", but a loadable package with that name exists. If you are looking for the package docs load the package first.\n") + end print_correction(io, s, mod) end repl_corrections(s) = repl_corrections(stdout, s) diff --git a/stdlib/REPL/test/docview.jl b/stdlib/REPL/test/docview.jl index 6b374ed7f0149..02f1dc8238f04 100644 --- a/stdlib/REPL/test/docview.jl +++ b/stdlib/REPL/test/docview.jl @@ -28,6 +28,11 @@ end @test occursin("Couldn't find 'mutable s'", str) end +@testset "non-loaded packages in doc search" begin + str = get_help_io("Profile") + @test occursin("Couldn't find Profile, but a loadable package with that name exists.", str) +end + @testset "Check @var_str also completes to var\"\" in REPL.doc_completions()" begin checks = ["var", "raw", "r"] symbols = "@" .* checks .* "_str" diff --git a/test/cartesian.jl b/test/cartesian.jl index 9643da72642ec..7064b54ebbb8d 100644 --- a/test/cartesian.jl +++ b/test/cartesian.jl @@ -1,12 +1,20 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -@test Base.Cartesian.exprresolve(:(1 + 3)) == 4 + ex = Base.Cartesian.exprresolve(:(if 5 > 4; :x; else :y; end)) @test ex.args[2] == QuoteNode(:x) @test Base.Cartesian.lreplace!("val_col", Base.Cartesian.LReplace{String}(:col, "col", 1)) == "val_1" @test Base.setindex(CartesianIndex(1,5,4),3,2) == CartesianIndex(1, 3, 4) - +@testset "Expression Resolve" begin + @test Base.Cartesian.exprresolve(:(1 + 3)) == 4 + ex1 = Expr(:ref, [1, 2, 3], 2) + result1 = Base.Cartesian.exprresolve(ex1) + @test result1 == 2 + ex2 = Expr(:ref, [1, 2, 3], "non-real-index") + result2 = Base.Cartesian.exprresolve(ex2) + @test result2 == ex2 +end @testset "CartesianIndices constructions" begin @testset "AbstractUnitRange" begin for oinds in [ diff --git a/test/compiler/EscapeAnalysis/EAUtils.jl b/test/compiler/EscapeAnalysis/EAUtils.jl index c71b821fd25f3..65fa9f75fe03f 100644 --- a/test/compiler/EscapeAnalysis/EAUtils.jl +++ b/test/compiler/EscapeAnalysis/EAUtils.jl @@ -18,7 +18,7 @@ using Core: CodeInstance, MethodInstance, CodeInfo using .CC: InferenceResult, InferenceState, OptimizationState, IRCode -using .EA: analyze_escapes, ArgEscapeCache, EscapeInfo, EscapeState +using .EA: analyze_escapes, ArgEscapeCache, ArgEscapeInfo, EscapeInfo, EscapeState struct EAToken end @@ -167,6 +167,42 @@ function Base.show(io::IO, x::EscapeInfo) end end +function get_sym_color(x::ArgEscapeInfo) + escape_bits = x.escape_bits + if escape_bits == EA.ARG_ALL_ESCAPE + color, sym = :red, "X" + elseif escape_bits == 0x00 + color, sym = :green, "✓" + else + color, sym = :bold, "*" + if !iszero(escape_bits & EA.ARG_RETURN_ESCAPE) + color, sym = :blue, "↑" + end + if !iszero(escape_bits & EA.ARG_THROWN_ESCAPE) + color = :yellow + end + end + return sym, color +end + +function Base.show(io::IO, x::ArgEscapeInfo) + escape_bits = x.escape_bits + if escape_bits == EA.ARG_ALL_ESCAPE + color, sym = :red, "X" + elseif escape_bits == 0x00 + color, sym = :green, "✓" + else + color, sym = :bold, "*" + if !iszero(escape_bits & EA.ARG_RETURN_ESCAPE) + color, sym = :blue, "↑" + end + if !iszero(escape_bits & EA.ARG_THROWN_ESCAPE) + color = :yellow + end + end + printstyled(io, "ArgEscapeInfo(", sym, ")"; color) +end + struct EscapeResult ir::IRCode state::EscapeState diff --git a/test/compiler/codegen.jl b/test/compiler/codegen.jl index ae04250964554..fcb3beb87b5a5 100644 --- a/test/compiler/codegen.jl +++ b/test/compiler/codegen.jl @@ -1003,3 +1003,25 @@ end @test f55768(Vector) @test f55768(Vector{T} where T) @test !f55768(Vector{S} where S) + +# test that values get rooted correctly over throw +for a in ((@noinline Ref{Int}(2)), + (@noinline Ref{Int}(3)), + 5, + (@noinline Ref{Int}(4)), + 6) + @test a[] != 0 + try + b = (@noinline Ref{Int}(5), + @noinline Ref{Int}(6), + @noinline Ref{Int}(7), + @noinline Ref{Int}(8), + @noinline Ref{Int}(9), + @noinline Ref{Int}(10), + @noinline Ref{Int}(11)) + GC.gc(true) + GC.@preserve b throw(a) + catch ex + @test ex === a + end +end diff --git a/test/compiler/effects.jl b/test/compiler/effects.jl index cdc26cddc440d..4174aa3d01030 100644 --- a/test/compiler/effects.jl +++ b/test/compiler/effects.jl @@ -357,18 +357,21 @@ end @test !Core.Compiler.builtin_nothrow(Core.Compiler.fallback_lattice, Core.get_binding_type, Any[Rational{Int}, Core.Const(:foo)], Any) -# Nothrow for assignment to globals +# effects modeling for assignment to globals global glob_assign_int::Int = 0 -f_glob_assign_int() = global glob_assign_int += 1 -let effects = Base.infer_effects(f_glob_assign_int, ()) +f_glob_assign_int() = global glob_assign_int = 1 +let effects = Base.infer_effects(f_glob_assign_int, (); optimize=false) + @test Core.Compiler.is_consistent(effects) @test !Core.Compiler.is_effect_free(effects) @test Core.Compiler.is_nothrow(effects) end -# Nothrow for setglobal! +# effects modeling for for setglobal! global SETGLOBAL!_NOTHROW::Int = 0 -let effects = Base.infer_effects() do +let effects = Base.infer_effects(; optimize=false) do setglobal!(@__MODULE__, :SETGLOBAL!_NOTHROW, 42) end + @test Core.Compiler.is_consistent(effects) + @test !Core.Compiler.is_effect_free(effects) @test Core.Compiler.is_nothrow(effects) end diff --git a/test/compiler/inline.jl b/test/compiler/inline.jl index 416f3873c5422..9895471ab1b27 100644 --- a/test/compiler/inline.jl +++ b/test/compiler/inline.jl @@ -2275,6 +2275,9 @@ function f_EA_finalizer(N::Int) Base.@assume_effects :nothrow @noinline println(devnull, "ptr = ", ptr) end end +let src = code_typed1(foreign_alloc, (Type{Float64},Int,)) + @test count(iscall((src, Core.finalizer)), src.code) == 1 +end let src = code_typed1(f_EA_finalizer, (Int,)) @test count(iscall((src, Core.finalizer)), src.code) == 0 end @@ -2283,6 +2286,23 @@ let;Base.Experimental.@force_compile @test foreign_buffer_checker.finalized end +# JuliaLang/julia#56422: +# EA-based finalizer inlining should not result in an invalid IR in the existence of `PhiNode`s +function issue56422(cnd::Bool, N::Int) + if cnd + workspace = foreign_alloc(Float64, N) + else + workspace = foreign_alloc(Float64, N+1) + end + GC.@preserve workspace begin + (;ptr) = workspace + Base.@assume_effects :nothrow @noinline println(devnull, "ptr = ", ptr) + end +end +let src = code_typed1(issue56422, (Bool,Int,)) + @test_broken count(iscall((src, Core.finalizer)), src.code) == 0 +end + # Test that inlining doesn't unnecessarily move things to statement position @noinline f_noinline_invoke(x::Union{Symbol,Nothing}=nothing) = Core.donotdelete(x) g_noinline_invoke(x) = f_noinline_invoke(x) diff --git a/test/compiler/ssair.jl b/test/compiler/ssair.jl index b7d75d0be5567..39ec60a429677 100644 --- a/test/compiler/ssair.jl +++ b/test/compiler/ssair.jl @@ -172,7 +172,7 @@ let code = Any[ ] ir = make_ircode(code; verify=false) ir = Core.Compiler.compact!(ir, true) - @test_throws ErrorException Core.Compiler.verify_ir(ir, false) + @test_throws ["IR verification failed.", "Code location: "] Core.Compiler.verify_ir(ir, false) end # Issue #29107 diff --git a/test/llvmpasses/returnstwicegc.ll b/test/llvmpasses/returnstwicegc.ll index 511cbb505519b..eb1c6444129c3 100644 --- a/test/llvmpasses/returnstwicegc.ll +++ b/test/llvmpasses/returnstwicegc.ll @@ -1,6 +1,6 @@ ; This file is a part of Julia. License is MIT: https://julialang.org/license -; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LateLowerGCFrame,FinalLowerGC)' -S %s | FileCheck %s --check-prefixes=OPAQUE +; RUN: opt --load-pass-plugin=libjulia-codegen%shlibext -passes='function(LateLowerGCFrame,FinalLowerGC)' -S %s | FileCheck %s declare void @boxed_simple({} addrspace(10)*, {} addrspace(10)*) @@ -13,7 +13,12 @@ declare void @one_arg_boxed({} addrspace(10)*) define void @try_catch(i64 %a, i64 %b) { ; Because of the returns_twice function, we need to keep aboxed live everywhere -; OPAQUE: %gcframe = alloca ptr addrspace(10), i32 4 +; CHECK: %gcframe = alloca ptr addrspace(10), i32 4 +; CHECK: store ptr addrspace(10) %aboxed, ptr [[slot_0:%.*]], +; CHECK-NOT: store {{.*}} ptr [[slot_0]] +; CHECK: store ptr addrspace(10) %bboxed, ptr {{%.*}} +; CHECK-NOT: store {{.*}} ptr [[slot_0]] + top: %sigframe = alloca [208 x i8], align 16 %sigframe.sub = getelementptr inbounds [208 x i8], [208 x i8]* %sigframe, i64 0, i64 0 diff --git a/test/ranges.jl b/test/ranges.jl index 629c2966b2fa6..73595e3056081 100644 --- a/test/ranges.jl +++ b/test/ranges.jl @@ -65,6 +65,10 @@ using .Main.OffsetArrays unitrangeerrstr = "promotion of types Char and Char failed to change any arguments" @test_throws unitrangeerrstr UnitRange('a', 'b') + + @test step(false:true) === true # PR 56405 + @test eltype((false:true) + (Int8(0):Int8(1))) === Int8 + @test eltype((false:true:true) + (Int8(0):Int8(1))) === Int8 end using Dates, Random diff --git a/test/sorting.jl b/test/sorting.jl index 2714197f58823..8cbdb94f02b16 100644 --- a/test/sorting.jl +++ b/test/sorting.jl @@ -581,6 +581,10 @@ end @test searchsortedfirst(o, 1.5) == 0 @test searchsortedlast(o, 0) == firstindex(o) - 1 @test searchsortedlast(o, 1.5) == -1 + + # Issue #56457 + o2 = OffsetArray([2,2,3], typemax(Int)-3); + @test searchsorted(o2, 2) == firstindex(o2):firstindex(o2)+1 end function adaptive_sort_test(v; trusted=InsertionSort, kw...) diff --git a/test/testhelpers/OffsetArrays.jl b/test/testhelpers/OffsetArrays.jl index 3463d5a94393d..06e65f8928036 100644 --- a/test/testhelpers/OffsetArrays.jl +++ b/test/testhelpers/OffsetArrays.jl @@ -821,17 +821,6 @@ centered(A::AbstractArray, cp::Dims=center(A)) = OffsetArray(A, .-cp) centered(A::AbstractArray, i::CartesianIndex) = centered(A, Tuple(i)) -# we may pass the searchsorted* functions to the parent, and wrap the offset -for f in [:searchsortedfirst, :searchsortedlast, :searchsorted] - _safe_f = Symbol("_safe_" * String(f)) - @eval function $_safe_f(v::OffsetArray, x, ilo, ihi, o::Base.Ordering) - offset = firstindex(v) - firstindex(parent(v)) - $f(parent(v), x, ilo - offset, ihi - offset, o) .+ offset - end - @eval Base.$f(v::OffsetVector, x, ilo::T, ihi::T, o::Base.Ordering) where T<:Integer = - $_safe_f(v, x, ilo, ihi, o) -end - ## # Deprecations ##