diff --git a/NEWS.md b/NEWS.md index 0f7da3162e200..5e4940762d331 100644 --- a/NEWS.md +++ b/NEWS.md @@ -770,6 +770,12 @@ Deprecated or removed * `Associative` has been deprecated in favor of `AbstractDict` ([#25012]). + * `Nullable{T}` has been deprecated and moved to the Nullables package ([#23642]). + Use `Union{T, Void}` instead, or `Union{Some{T}, Void}` if `nothing` is a possible value + (i.e. `Void <: T`). `isnull(x)` can be replaced with `x === nothing` + and `unsafe_get`/`get` can be dropped or replaced with `coalesce`. + `NullException` has been removed. + Command-line option changes --------------------------- diff --git a/base/broadcast.jl b/base/broadcast.jl index 185bb1c6fae6c..a3d6d57547d4c 100644 --- a/base/broadcast.jl +++ b/base/broadcast.jl @@ -5,7 +5,7 @@ module Broadcast using Base.Cartesian using Base: Indices, OneTo, linearindices, tail, to_shape, _msk_end, unsafe_bitgetindex, bitcache_chunks, bitcache_size, dumpbitcache, - nullable_returntype, null_safe_op, hasvalue, isoperator + isoperator import Base: broadcast, broadcast! export BroadcastStyle, broadcast_indices, broadcast_similar, broadcast_getindex, broadcast_setindex!, dotview, @__dot__ @@ -45,7 +45,6 @@ Naturally you can specialize this for your particular `C` (e.g., `MyContainer`). struct Style{T} <: BroadcastStyle end BroadcastStyle(::Type{<:Tuple}) = Style{Tuple}() -BroadcastStyle(::Type{<:Nullable}) = Style{Nullable}() struct Unknown <: BroadcastStyle end BroadcastStyle(::Type{Union{}}) = Unknown() # ambiguity resolution @@ -142,7 +141,6 @@ BroadcastStyle(::BroadcastStyle, ::BroadcastStyle) = Unknown() BroadcastStyle(::Unknown, ::Unknown) = Unknown() BroadcastStyle(::S, ::Unknown) where S<:BroadcastStyle = S() # Precedence rules -BroadcastStyle(::Style{Nullable}, ::Scalar) = Style{Nullable}() BroadcastStyle(::Style{Tuple}, ::Scalar) = Style{Tuple}() BroadcastStyle(a::AbstractArrayStyle{0}, ::Style{Tuple}) = typeof(a)(Val(1)) BroadcastStyle(a::AbstractArrayStyle, ::Style{Tuple}) = a @@ -223,7 +221,6 @@ broadcast_indices() = () broadcast_indices(::Type{T}) where T = () broadcast_indices(A) = broadcast_indices(combine_styles(A), A) broadcast_indices(::Scalar, A) = () -broadcast_indices(::Style{Nullable}, A) = () broadcast_indices(::Style{Tuple}, A) = (OneTo(length(A)),) broadcast_indices(::DefaultArrayStyle{0}, A::Ref) = () broadcast_indices(::AbstractArrayStyle, A) = Base.axes(A) @@ -375,7 +372,7 @@ end Base.@propagate_inbounds _broadcast_getindex(::Type{T}, I) where T = T Base.@propagate_inbounds _broadcast_getindex(A, I) = _broadcast_getindex(combine_styles(A), A, I) Base.@propagate_inbounds _broadcast_getindex(::DefaultArrayStyle{0}, A::Ref, I) = A[] -Base.@propagate_inbounds _broadcast_getindex(::Union{Unknown,Scalar,Style{Nullable}}, A, I) = A +Base.@propagate_inbounds _broadcast_getindex(::Union{Unknown,Scalar}, A, I) = A Base.@propagate_inbounds _broadcast_getindex(::Any, A, I) = A[I] Base.@propagate_inbounds _broadcast_getindex(::Style{Tuple}, A::Tuple{Any}, I) = A[1] @@ -510,28 +507,20 @@ maptoTuple(f, a, b...) = Tuple{f(a), maptoTuple(f, b...).types...} # )::_broadcast_getindex_eltype(A) _broadcast_getindex_eltype(A) = _broadcast_getindex_eltype(combine_styles(A), A) _broadcast_getindex_eltype(::Scalar, ::Type{T}) where T = Type{T} -_broadcast_getindex_eltype(::Union{Unknown,Scalar,Style{Nullable}}, A) = typeof(A) +_broadcast_getindex_eltype(::Union{Unknown,Scalar}, A) = typeof(A) _broadcast_getindex_eltype(::BroadcastStyle, A) = eltype(A) # Tuple, Array, etc. -# An element type satisfying for all A: -# unsafe_get(A)::unsafe_get_eltype(A) -_unsafe_get_eltype(x::Nullable) = eltype(x) -_unsafe_get_eltype(::Type{T}) where T = Type{T} -_unsafe_get_eltype(x) = typeof(x) - # Inferred eltype of result of broadcast(f, xs...) combine_eltypes(f, A, As...) = Base._return_type(f, maptoTuple(_broadcast_getindex_eltype, A, As...)) -_nullable_eltype(f, A, As...) = - Base._return_type(f, maptoTuple(_unsafe_get_eltype, A, As...)) """ broadcast(f, As...) -Broadcasts the arrays, tuples, `Ref`s, nullables, and/or scalars `As` to a +Broadcasts the arrays, tuples, `Ref`s and/or scalars `As` to a container of the appropriate type and dimensions. In this context, anything -that is not a subtype of `AbstractArray`, `Ref` (except for `Ptr`s), `Tuple`, -or `Nullable` is considered a scalar. The resulting container is established by +that is not a subtype of `AbstractArray`, `Ref` (except for `Ptr`s) or `Tuple` +is considered a scalar. The resulting container is established by the following rules: - If all the arguments are scalars, it returns a scalar. @@ -540,10 +529,6 @@ the following rules: (expanding singleton dimensions), and treats `Ref`s as 0-dimensional arrays, and tuples as 1-dimensional arrays. -The following additional rule applies to `Nullable` arguments: If there is at -least one `Nullable`, and all the arguments are scalars or `Nullable`, it -returns a `Nullable` treating `Nullable`s as "containers". - A special syntax exists for broadcasting: `f.(args...)` is equivalent to `broadcast(f, args...)`, and nested `f.(g.(args...))` calls are fused into a single broadcast loop. @@ -602,14 +587,6 @@ julia> string.(("one","two","three","four"), ": ", 1:4) "three: 3" "four: 4" -julia> Nullable("X") .* "Y" -Nullable{String}("XY") - -julia> broadcast(/, 1.0, Nullable(2.0)) -Nullable{Float64}(0.5) - -julia> (1 + im) ./ Nullable{Int}() -Nullable{Complex{Float64}}() ``` """ @inline broadcast(f, A, Bs...) = @@ -656,21 +633,6 @@ function broadcast_nonleaf(f, s::NonleafHandlingTypes, ::Type{ElType}, shape::In return _broadcast!(f, dest, keeps, Idefaults, As, Val(nargs), iter, st, 1) end -@inline function broadcast(f, ::Style{Nullable}, ::Void, ::Void, a...) - nonnull = all(hasvalue, a) - S = _nullable_eltype(f, a...) - if Base._isleaftype(S) && null_safe_op(f, maptoTuple(_unsafe_get_eltype, - a...).types...) - Nullable{S}(f(map(unsafe_get, a)...), nonnull) - else - if nonnull - Nullable(f(map(unsafe_get, a)...)) - else - Nullable{nullable_returntype(S)}() - end - end -end - broadcast(f, ::Union{Scalar,Unknown}, ::Void, ::Void, a...) = f(a...) @inline broadcast(f, ::Style{Tuple}, ::Void, ::Void, A, Bs...) = diff --git a/base/channels.jl b/base/channels.jl index b48a8d0cf91be..4e40177cf1c82 100644 --- a/base/channels.jl +++ b/base/channels.jl @@ -18,13 +18,13 @@ Other constructors: * `Channel(sz)`: equivalent to `Channel{Any}(sz)` """ mutable struct Channel{T} <: AbstractChannel - cond_take::Condition # waiting for data to become available - cond_put::Condition # waiting for a writeable slot + cond_take::Condition # waiting for data to become available + cond_put::Condition # waiting for a writeable slot state::Symbol - excp::Nullable{Exception} # Exception to be thrown when state != :open + excp::Union{Exception, Void} # exception to be thrown when state != :open data::Vector{T} - sz_max::Int # maximum size of channel + sz_max::Int # maximum size of channel # Used when sz_max == 0, i.e., an unbuffered channel. waiters::Int @@ -42,7 +42,7 @@ mutable struct Channel{T} <: AbstractChannel if sz < 0 throw(ArgumentError("Channel size must be either 0, a positive integer or Inf")) end - ch = new(Condition(), Condition(), :open, Nullable{Exception}(), Vector{T}(), sz, 0) + ch = new(Condition(), Condition(), :open, nothing, Vector{T}(), sz, 0) if sz == 0 ch.takers = Vector{Task}() ch.putters = Vector{Task}() @@ -129,7 +129,7 @@ isbuffered(c::Channel) = c.sz_max==0 ? false : true function check_channel_state(c::Channel) if !isopen(c) - !isnull(c.excp) && throw(get(c.excp)) + c.excp !== nothing && throw(c.excp) throw(closed_exception()) end end @@ -143,7 +143,7 @@ Close a channel. An exception is thrown by: """ function close(c::Channel) c.state = :closed - c.excp = Nullable{}(closed_exception()) + c.excp = closed_exception() notify_error(c) nothing end @@ -237,7 +237,7 @@ function close_chnl_on_taskdone(t::Task, ref::WeakRef) !isopen(c) && return if istaskfailed(t) c.state = :closed - c.excp = Nullable{Exception}(task_result(t)) + c.excp = task_result(t) notify_error(c) else close(c) @@ -387,7 +387,7 @@ function notify_error(c::Channel, err) foreach(t->schedule(t, err; error=true), waiters) end end -notify_error(c::Channel) = notify_error(c, get(c.excp)) +notify_error(c::Channel) = notify_error(c, c.excp) eltype(::Type{Channel{T}}) where {T} = T diff --git a/base/client.jl b/base/client.jl index 26014d026a668..c4f29777c5149 100644 --- a/base/client.jl +++ b/base/client.jl @@ -76,7 +76,7 @@ color_normal = text_colors[:normal] function repl_color(key, default) env_str = get(ENV, key, "") c = tryparse(Int, env_str) - c_conv = isnull(c) ? Symbol(env_str) : get(c) + c_conv = coalesce(c, Symbol(env_str)) haskey(text_colors, c_conv) ? c_conv : default end diff --git a/base/deprecated.jl b/base/deprecated.jl index 860f839acce0a..00ae37bf5cda6 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -1512,8 +1512,8 @@ end function prompt(msg::AbstractString; default::AbstractString="", password::Bool=false) Base.depwarn(string( "`LibGit2.prompt(msg::AbstractString; default::AbstractString=\"\", password::Bool=false)` is deprecated, use ", - "`get(Base.prompt(msg, default=default, password=password), \"\")` instead."), :prompt) - Base.get(Base.prompt(msg, default=default, password=password), "") + "`result = Base.prompt(msg, default=default, password=password); result === nothing ? \"\" : result` instead."), :prompt) + coalesce(Base.prompt(msg, default=default, password=password), "") end end @@ -1658,20 +1658,20 @@ import .Iterators.enumerate # PR #23640 # when this deprecation is deleted, remove all calls to it, and replace all keywords of: -# `payload::Union{CredentialPayload,Nullable{<:Union{AbstractCredential, CachedCredentials}}}` +# `payload::Union{CredentialPayload, AbstractCredential, CachedCredentials, Void}` # with `payload::CredentialPayload` from base/libgit2/libgit2.jl @eval LibGit2 function deprecate_nullable_creds(f, sig, payload) - if isa(payload, Nullable{<:Union{AbstractCredential, CachedCredentials}}) + if isa(payload, Union{AbstractCredential, CachedCredentials, Void}) # Note: Be careful not to show the contents of the credentials as it could reveal a # password. - if isnull(payload) - msg = "LibGit2.$f($sig; payload=Nullable()) is deprecated, use " + if payload === nothing + msg = "LibGit2.$f($sig; payload=nothing) is deprecated, use " msg *= "LibGit2.$f($sig; payload=LibGit2.CredentialPayload()) instead." p = CredentialPayload() else - cred = unsafe_get(payload) + cred = payload C = typeof(cred) - msg = "LibGit2.$f($sig; payload=Nullable($C(...))) is deprecated, use " + msg = "LibGit2.$f($sig; payload=$C(...)) is deprecated, use " msg *= "LibGit2.$f($sig; payload=LibGit2.CredentialPayload($C(...))) instead." p = CredentialPayload(cred) end @@ -3244,6 +3244,11 @@ end @deprecate indices(a) axes(a) @deprecate indices(a, d) axes(a, d) +@deprecate_moved Nullable "Nullables" +@deprecate_moved NullException "Nullables" +@deprecate_moved isnull "Nullables" +@deprecate_moved unsafe_get "Nullables" + # END 0.7 deprecations # BEGIN 1.0 deprecations diff --git a/base/docs/Docs.jl b/base/docs/Docs.jl index 5e5aa4c33056a..835fb858704cc 100644 --- a/base/docs/Docs.jl +++ b/base/docs/Docs.jl @@ -142,7 +142,7 @@ linenumber, source code, and fielddocs. """ mutable struct DocStr text :: Core.SimpleVector - object :: Nullable + object :: Any data :: Dict{Symbol, Any} end @@ -160,9 +160,9 @@ function docstr(binding::Binding, @nospecialize typesig = Union{}) end docstr(object, data = Dict()) = _docstr(object, data) -_docstr(vec::Core.SimpleVector, data) = DocStr(vec, Nullable(), data) -_docstr(str::AbstractString, data) = DocStr(Core.svec(str), Nullable(), data) -_docstr(object, data) = DocStr(Core.svec(), Nullable(object), data) +_docstr(vec::Core.SimpleVector, data) = DocStr(vec, nothing, data) +_docstr(str::AbstractString, data) = DocStr(Core.svec(str), nothing, data) +_docstr(object, data) = DocStr(Core.svec(), object, data) _docstr(doc::DocStr, data) = (doc.data = merge(data, doc.data); doc) @@ -184,13 +184,13 @@ end @noinline formatdoc(buffer, d, part) = print(buffer, part) function parsedoc(d::DocStr) - if isnull(d.object) + if d.object === nothing md = formatdoc(d) md.meta[:module] = d.data[:module] md.meta[:path] = d.data[:path] - d.object = Nullable(md) + d.object = md end - get(d.object) + d.object end """ diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index cfc1bd47da829..7838db7b975ac 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -814,7 +814,7 @@ Void nothing The singleton instance of type `Void`, used by convention when there is no value to return -(as in a C `void` function). Can be converted to an empty [`Nullable`](@ref) value. +(as in a C `void` function) or when a variable or field holds no value. """ nothing diff --git a/base/exports.jl b/base/exports.jl index 3d2c355994b1d..0681197fa74dd 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -77,7 +77,6 @@ export MergeSort, Missing, NTuple, - Nullable, ObjectIdDict, OrdinalRange, Pair, @@ -100,6 +99,7 @@ export AbstractSerializer, SerializationState, Set, + Some, StepRange, StepRangeLen, StridedArray, @@ -150,7 +150,6 @@ export InvalidStateException, KeyError, MissingException, - NullException, ParseError, SystemError, StringIndexError, @@ -863,6 +862,7 @@ export fetch, # missing values + coalesce, ismissing, missing, skipmissing, @@ -1145,10 +1145,6 @@ export unsafe_store!, unsafe_write, -# nullable types - isnull, - unsafe_get, - # Macros # parser internal @__FILE__, diff --git a/base/gmp.jl b/base/gmp.jl index 5cb20f3189e77..ffbc4731fb517 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -234,19 +234,17 @@ convert(::Type{Signed}, x::BigInt) = x hastypemax(::Type{BigInt}) = false function tryparse_internal(::Type{BigInt}, s::AbstractString, startpos::Int, endpos::Int, base_::Integer, raise::Bool) - _n = Nullable{BigInt}() - # don't make a copy in the common case where we are parsing a whole String bstr = startpos == start(s) && endpos == endof(s) ? String(s) : String(SubString(s,startpos,endpos)) sgn, base, i = Base.parseint_preamble(true,Int(base_),bstr,start(bstr),endof(bstr)) if !(2 <= base <= 62) raise && throw(ArgumentError("invalid base: base must be 2 ≤ base ≤ 62, got $base")) - return _n + return nothing end if i == 0 raise && throw(ArgumentError("premature end of integer: $(repr(bstr))")) - return _n + return nothing end z = BigInt() if Base.containsnul(bstr) @@ -256,9 +254,9 @@ function tryparse_internal(::Type{BigInt}, s::AbstractString, startpos::Int, end end if err != 0 raise && throw(ArgumentError("invalid BigInt: $(repr(bstr))")) - return _n + return nothing end - Nullable(flipsign!(z, sgn)) + flipsign!(z, sgn) end convert(::Type{BigInt}, x::Union{Clong,Int32}) = MPZ.set_si(x) diff --git a/base/int.jl b/base/int.jl index b5ff8dca27d79..4620784e3c3a8 100644 --- a/base/int.jl +++ b/base/int.jl @@ -603,12 +603,12 @@ macro big_str(s) print(bf, s[end]) seekstart(bf) n = tryparse(BigInt, String(take!(bf))) - !isnull(n) && return get(n) + n === nothing || return n else n = tryparse(BigInt, s) - !isnull(n) && return get(n) + n === nothing || return n n = tryparse(BigFloat, s) - !isnull(n) && return get(n) + n === nothing || return n end message = "invalid number format $s for BigInt or BigFloat" return :(throw(ArgumentError($message))) diff --git a/base/iterators.jl b/base/iterators.jl index 232fd5a766da7..ce2517bb29c0f 100644 --- a/base/iterators.jl +++ b/base/iterators.jl @@ -5,7 +5,7 @@ Methods for working with Iterators. """ module Iterators -import Base: start, done, next, isempty, length, size, eltype, iteratorsize, iteratoreltype, axes, ndims, pairs, last, first +import Base: start, done, next, isempty, length, size, eltype, iteratorsize, iteratoreltype, axes, ndims, pairs, last, first, get using Base: tail, tuple_type_head, tuple_type_tail, tuple_type_cons, SizeUnknown, HasLength, HasShape, IsInfinite, EltypeUnknown, HasEltype, OneTo, @propagate_inbounds, Generator, AbstractRange @@ -735,7 +735,7 @@ function next(P::ProductIterator, state) iter1 = first(iterators) value1, state1 = next(iter1, states[1]) tailstates = tail(states) - values = (value1, map(unsafe_get, nvalues)...) # safe if not done(P, state) + values = (value1, map(get, nvalues)...) # safe if not done(P, state) if done(iter1, state1) d, tailstates, nvalues = _prod_next(tail(iterators), tailstates, nvalues) if !d # only restart iter1 if not completely done @@ -746,6 +746,14 @@ function next(P::ProductIterator, state) end done(P::ProductIterator, state) = state[1] +struct MaybeValue{T} + x::T + MaybeValue{T}() where {T} = new{T}() + MaybeValue{T}(x::T) where {T} = new{T}(x) +end + +get(v::MaybeValue) = v.x + _prod_start(iterators::Tuple{}) = false, (), () function _prod_start(iterators) iter1 = first(iterators) @@ -753,10 +761,10 @@ function _prod_start(iterators) d, tailstates, tailnvalues = _prod_start(tail(iterators)) if done(iter1, state1) d = true - nvalue1 = Nullable{eltype(iter1)}() + nvalue1 = MaybeValue{eltype(iter1)}() else value1, state1 = next(iter1, state1) - nvalue1 = Nullable{eltype(iter1)}(value1) + nvalue1 = MaybeValue{eltype(iter1)}(value1) end return (d, (state1, tailstates...), (nvalue1, tailnvalues...)) end @@ -767,15 +775,15 @@ function _prod_next(iterators, states, nvalues) state1 = first(states) if !done(iter1, state1) value1, state1 = next(iter1, state1) - nvalue1 = Nullable{eltype(iter1)}(value1) + nvalue1 = MaybeValue{eltype(iter1)}(value1) return false, (state1, tail(states)...), (nvalue1, tail(nvalues)...) else d, tailstates, tailnvalues = _prod_next(tail(iterators), tail(states), tail(nvalues)) if d # all iterators are done - nvalue1 = Nullable{eltype(iter1)}() + nvalue1 = MaybeValue{eltype(iter1)}() else value1, state1 = next(iter1, start(iter1)) # iter cannot be done immediately - nvalue1 = Nullable{eltype(iter1)}(value1) + nvalue1 = MaybeValue{eltype(iter1)}(value1) end return d, (state1, tailstates...), (nvalue1, tailnvalues...) end diff --git a/base/libgit2/callbacks.jl b/base/libgit2/callbacks.jl index 5d5041695a755..2be0e07d9aab2 100644 --- a/base/libgit2/callbacks.jl +++ b/base/libgit2/callbacks.jl @@ -62,7 +62,7 @@ function exhausted_abort() end function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, username_ptr) - cred = Base.get(p.credential)::SSHCredential + cred = p.credential::SSHCredential revised = false # Use a filled credential as-is on the first pass. Reset password on sucessive calls. @@ -114,8 +114,8 @@ function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, if isempty(cred.user) || username_ptr == Cstring(C_NULL) url = git_url(scheme=p.scheme, host=p.host) response = Base.prompt("Username for '$url'", default=cred.user) - isnull(response) && return user_abort() - cred.user = unsafe_get(response) + response === nothing && return user_abort() + cred.user = response end url = git_url(scheme=p.scheme, host=p.host, username=cred.user) @@ -124,8 +124,8 @@ function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, last_private_key = cred.prvkey if !isfile(cred.prvkey) || !revised || !haskey(ENV, "SSH_KEY_PATH") response = Base.prompt("Private key location for '$url'", default=cred.prvkey) - isnull(response) && return user_abort() - cred.prvkey = expanduser(unsafe_get(response)) + response === nothing && return user_abort() + cred.prvkey = expanduser(response) # Only update the public key if the private key changed if cred.prvkey != last_private_key @@ -138,8 +138,8 @@ function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, stale = !p.first_pass && cred.prvkey == last_private_key && cred.pubkey != cred.prvkey * ".pub" if isfile(cred.prvkey) && (stale || !isfile(cred.pubkey)) response = Base.prompt("Public key location for '$url'", default=cred.pubkey) - isnull(response) && return user_abort() - cred.pubkey = expanduser(unsafe_get(response)) + response === nothing && return user_abort() + cred.pubkey = expanduser(response) end # Ask for a passphrase when the private key exists and requires a passphrase @@ -148,12 +148,12 @@ function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, response = Base.winprompt( "Your SSH Key requires a password, please enter it now:", "Passphrase required", cred.prvkey; prompt_username=false) - isnull(response) && return user_abort() - cred.pass = unsafe_get(response)[2] + response === nothing && return user_abort() + cred.pass = response[2] else response = Base.prompt("Passphrase for $(cred.prvkey)", password=true) - isnull(response) && return user_abort() - cred.pass = unsafe_get(response) + response === nothing && return user_abort() + cred.pass = response isempty(cred.pass) && return user_abort() # Ambiguous if EOF or newline end end @@ -174,7 +174,7 @@ function authenticate_ssh(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload, end function authenticate_userpass(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayload) - cred = Base.get(p.credential)::UserPasswordCredential + cred = p.credential::UserPasswordCredential revised = false # Use a filled credential as-is on the first pass. Reset password on sucessive calls. @@ -188,8 +188,8 @@ function authenticate_userpass(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayl git_cred = GitCredential(p.config, p.url) # Use `deepcopy` to ensure zeroing the `git_cred` doesn't also zero the `cred`s copy - cred.user = deepcopy(Base.get(git_cred.username, "")) - cred.pass = deepcopy(Base.get(git_cred.password, "")) + cred.user = deepcopy(coalesce(git_cred.username, "")) + cred.pass = deepcopy(coalesce(git_cred.password, "")) securezero!(git_cred) revised = true @@ -203,17 +203,17 @@ function authenticate_userpass(libgit2credptr::Ptr{Ptr{Void}}, p::CredentialPayl response = Base.winprompt( "Please enter your credentials for '$url'", "Credentials required", username; prompt_username=true) - isnull(response) && return user_abort() - cred.user, cred.pass = unsafe_get(response) + response === nothing && return user_abort() + cred.user, cred.pass = response else response = Base.prompt("Username for '$url'", default=username) - isnull(response) && return user_abort() - cred.user = unsafe_get(response) + response === nothing && return user_abort() + cred.user = response url = git_url(scheme=p.scheme, host=p.host, username=cred.user) response = Base.prompt("Password for '$url'", password=true) - isnull(response) && return user_abort() - cred.pass = unsafe_get(response) + response === nothing && return user_abort() + cred.pass = response isempty(cred.pass) && return user_abort() # Ambiguous if EOF or newline end @@ -274,19 +274,19 @@ function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring, p.url = unsafe_string(url_ptr) m = match(URL_REGEX, p.url) - p.scheme = m[:scheme] === nothing ? "" : m[:scheme] - p.username = m[:user] === nothing ? "" : m[:user] + p.scheme = coalesce(m[:scheme], "") + p.username = coalesce(m[:user], "") p.host = m[:host] # When an explicit credential is supplied we will make sure to use the given # credential during the first callback by modifying the allowed types. The # modification only is in effect for the first callback since `allowed_types` cannot # be mutated. - if !isnull(p.explicit) - cred = unsafe_get(p.explicit) + if p.explicit !== nothing + cred = p.explicit # Copy explicit credentials to avoid mutating approved credentials. - p.credential = Nullable(deepcopy(cred)) + p.credential = deepcopy(cred) if isa(cred, SSHCredential) allowed_types &= Cuint(Consts.CREDTYPE_SSH_KEY) @@ -295,13 +295,12 @@ function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring, else allowed_types &= Cuint(0) # Unhandled credential type end - elseif !isnull(p.cache) - cache = unsafe_get(p.cache) + elseif p.cache !== nothing cred_id = credential_identifier(p.scheme, p.host) # Perform a deepcopy as we do not want to mutate approved cached credentials - if haskey(cache, cred_id) - p.credential = Nullable(deepcopy(cache[cred_id])) + if haskey(p.cache, cred_id) + p.credential = deepcopy(p.cache[cred_id]) end end @@ -312,16 +311,16 @@ function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring, # use ssh key or ssh-agent if isset(allowed_types, Cuint(Consts.CREDTYPE_SSH_KEY)) - if isnull(p.credential) || !isa(unsafe_get(p.credential), SSHCredential) - p.credential = Nullable(SSHCredential(p.username)) + if p.credential === nothing || !isa(p.credential, SSHCredential) + p.credential = SSHCredential(p.username) end err = authenticate_ssh(libgit2credptr, p, username_ptr) err == 0 && return err end if isset(allowed_types, Cuint(Consts.CREDTYPE_USERPASS_PLAINTEXT)) - if isnull(p.credential) || !isa(unsafe_get(p.credential), UserPasswordCredential) - p.credential = Nullable(UserPasswordCredential(p.username)) + if p.credential === nothing || !isa(p.credential, UserPasswordCredential) + p.credential = UserPasswordCredential(p.username) end err = authenticate_userpass(libgit2credptr, p) err == 0 && return err @@ -331,7 +330,7 @@ function credentials_callback(libgit2credptr::Ptr{Ptr{Void}}, url_ptr::Cstring, # that explicit credentials were passed in, but said credentials are incompatible # with the requested authentication method. if err == 0 - if !isnull(p.explicit) + if p.explicit !== nothing ccall((:giterr_set_str, :libgit2), Void, (Cint, Cstring), Cint(Error.Callback), "The explicitly provided credential is incompatible with the requested " * "authentication methods.") diff --git a/base/libgit2/config.jl b/base/libgit2/config.jl index 9f5a7549b9b48..02c86fcdb8bcf 100644 --- a/base/libgit2/config.jl +++ b/base/libgit2/config.jl @@ -197,26 +197,26 @@ function Base.start(ci::GitConfigIter) err = ccall((:git_config_next, :libgit2), Cint, (Ptr{Ptr{ConfigEntry}}, Ptr{Void}), entry_ptr_ptr, ci.ptr) if err == Cint(Error.GIT_OK) - state = Nullable{ConfigEntry}(unsafe_load(entry_ptr_ptr[])) + state = unsafe_load(entry_ptr_ptr[]) elseif err == Cint(Error.ITEROVER) - state = Nullable{ConfigEntry}() + state = nothing else throw(GitError(err)) end return state end -Base.done(ci::GitConfigIter, state) = isnull(state) +Base.done(ci::GitConfigIter, state) = state === nothing function Base.next(ci::GitConfigIter, state) - entry = Base.get(state) + entry = notnothing(state) entry_ptr_ptr = Ref{Ptr{ConfigEntry}}(C_NULL) err = ccall((:git_config_next, :libgit2), Cint, (Ptr{Ptr{ConfigEntry}}, Ptr{Void}), entry_ptr_ptr, ci.ptr) if err == Cint(Error.GIT_OK) - state = Nullable{ConfigEntry}(unsafe_load(entry_ptr_ptr[])) + state = unsafe_load(entry_ptr_ptr[]) elseif err == Cint(Error.ITEROVER) - state = Nullable{ConfigEntry}() + state = nothing else throw(GitError(err)) end diff --git a/base/libgit2/gitcredential.jl b/base/libgit2/gitcredential.jl index 09806ff6dedaf..2eb1b69471371 100644 --- a/base/libgit2/gitcredential.jl +++ b/base/libgit2/gitcredential.jl @@ -5,56 +5,42 @@ Git credential information used in communication with git credential helpers. Th named using the [input/output key specification](https://git-scm.com/docs/git-credential#IOFMT). """ mutable struct GitCredential - protocol::Nullable{String} - host::Nullable{String} - path::Nullable{String} - username::Nullable{String} - password::Nullable{String} + protocol::Union{String, Void} + host::Union{String, Void} + path::Union{String, Void} + username::Union{String, Void} + password::Union{String, Void} use_http_path::Bool function GitCredential( - protocol::Nullable{<:AbstractString}, - host::Nullable{<:AbstractString}, - path::Nullable{<:AbstractString}, - username::Nullable{<:AbstractString}, - password::Nullable{<:AbstractString}) + protocol::Union{AbstractString, Void}=nothing, + host::Union{AbstractString, Void}=nothing, + path::Union{AbstractString, Void}=nothing, + username::Union{AbstractString, Void}=nothing, + password::Union{AbstractString, Void}=nothing) c = new(protocol, host, path, username, password, true) finalizer(securezero!, c) return c end end -function GitCredential( - protocol::Union{AbstractString,Void}=nothing, - host::Union{AbstractString,Void}=nothing, - path::Union{AbstractString,Void}=nothing, - username::Union{AbstractString,Void}=nothing, - password::Union{AbstractString,Void}=nothing) - GitCredential( - Nullable{String}(protocol), - Nullable{String}(host), - Nullable{String}(path), - Nullable{String}(username), - Nullable{String}(password)) -end - function GitCredential(cfg::GitConfig, url::AbstractString) fill!(cfg, parse(GitCredential, url)) end function GitCredential(cred::UserPasswordCredential, url::AbstractString) git_cred = parse(GitCredential, url) - git_cred.username = Nullable{String}(deepcopy(cred.user)) - git_cred.password = Nullable{String}(deepcopy(cred.pass)) + git_cred.username = deepcopy(cred.user) + git_cred.password = deepcopy(cred.pass) return git_cred end function securezero!(cred::GitCredential) - !isnull(cred.protocol) && securezero!(unsafe_get(cred.protocol)) - !isnull(cred.host) && securezero!(unsafe_get(cred.host)) - !isnull(cred.path) && securezero!(unsafe_get(cred.path)) - !isnull(cred.username) && securezero!(unsafe_get(cred.username)) - !isnull(cred.password) && securezero!(unsafe_get(cred.password)) + cred.protocol !== nothing && securezero!(cred.protocol) + cred.host !== nothing && securezero!(cred.host) + cred.path !== nothing && securezero!(cred.path) + cred.username !== nothing && securezero!(cred.username) + cred.password !== nothing && securezero!(cred.password) return cred end @@ -79,14 +65,14 @@ function ismatch(url::AbstractString, git_cred::GitCredential) m === nothing && error("Unable to parse URL") # Note: missing URL groups match anything - (m[:scheme] === nothing ? true : isequal(Nullable(m[:scheme]), git_cred.protocol)) && - (m[:host] === nothing ? true : isequal(Nullable(m[:host]), git_cred.host)) && - (m[:path] === nothing ? true : isequal(Nullable(m[:path]), git_cred.path)) && - (m[:user] === nothing ? true : isequal(Nullable(m[:user]), git_cred.username)) + (m[:scheme] === nothing ? true : m[:scheme] == git_cred.protocol) && + (m[:host] === nothing ? true : m[:host] == git_cred.host) && + (m[:path] === nothing ? true : m[:path] == git_cred.path) && + (m[:user] === nothing ? true : m[:user] == git_cred.username) end function isfilled(cred::GitCredential) - !isnull(cred.username) && !isnull(cred.password) + cred.username !== nothing && cred.password !== nothing end function Base.parse(::Type{GitCredential}, url::AbstractString) @@ -112,11 +98,11 @@ function Base.copy!(a::GitCredential, b::GitCredential) end function Base.write(io::IO, cred::GitCredential) - !isnull(cred.protocol) && println(io, "protocol=", unsafe_get(cred.protocol)) - !isnull(cred.host) && println(io, "host=", unsafe_get(cred.host)) - !isnull(cred.path) && cred.use_http_path && println(io, "path=", unsafe_get(cred.path)) - !isnull(cred.username) && println(io, "username=", unsafe_get(cred.username)) - !isnull(cred.password) && println(io, "password=", unsafe_get(cred.password)) + cred.protocol !== nothing && println(io, "protocol=", cred.protocol) + cred.host !== nothing && println(io, "host=", cred.host) + cred.path !== nothing && cred.use_http_path && println(io, "path=", cred.path) + cred.username !== nothing && println(io, "username=", cred.username) + cred.password !== nothing && println(io, "password=", cred.password) nothing end @@ -130,7 +116,7 @@ function Base.read!(io::IO, cred::GitCredential) # https://git-scm.com/docs/git-credential#git-credential-codeurlcode copy!(cred, parse(GitCredential, value)) else - setfield!(cred, Symbol(key), Nullable(String(value))) + setfield!(cred, Symbol(key), String(value)) end end @@ -141,7 +127,7 @@ function fill!(cfg::GitConfig, cred::GitCredential) cred.use_http_path = use_http_path(cfg, cred) # When the username is missing default to using the username set in the configuration - if isnull(cred.username) + if cred.username === nothing cred.username = default_username(cfg, cred) end @@ -249,7 +235,7 @@ function credential_helpers(cfg::GitConfig, cred::GitCredential) end """ - default_username(config, git_cred) -> Nullable{String} + default_username(config, git_cred) -> Union{String, Void} Return the default username, if any, provided by the `config` which is valid for the specified `git_cred`. @@ -262,10 +248,10 @@ function default_username(cfg::GitConfig, cred::GitCredential) # Only use configuration settings where the URL applies to the git credential ismatch(url, cred) || continue - return Nullable{String}(value) + return value end - return Nullable{String}() + return nothing end function use_http_path(cfg::GitConfig, cred::GitCredential) diff --git a/base/libgit2/index.jl b/base/libgit2/index.jl index 7cea1a6f821e9..378e8ceae3897 100644 --- a/base/libgit2/index.jl +++ b/base/libgit2/index.jl @@ -53,10 +53,10 @@ function write_tree!(idx::GitIndex) end function repository(idx::GitIndex) - if isnull(idx.owner) + if idx.owner === nothing throw(GitError(Error.Index, Error.ENOTFOUND, "Index does not have an owning repository.")) else - return Base.get(idx.owner) + return idx.owner end end @@ -182,8 +182,8 @@ function Base.find(path::String, idx::GitIndex) pos_ref = Ref{Csize_t}(0) ret = ccall((:git_index_find, :libgit2), Cint, (Ref{Csize_t}, Ptr{Void}, Cstring), pos_ref, idx.ptr, path) - ret == Cint(Error.ENOTFOUND) && return Nullable{Csize_t}() - return Nullable(pos_ref[]+1) + ret == Cint(Error.ENOTFOUND) && return nothing + return pos_ref[]+1 end """ diff --git a/base/libgit2/libgit2.jl b/base/libgit2/libgit2.jl index 91ecc806b7e96..2eaefcdb88c38 100644 --- a/base/libgit2/libgit2.jl +++ b/base/libgit2/libgit2.jl @@ -6,6 +6,7 @@ Interface to [libgit2](https://libgit2.github.com/). module LibGit2 import Base: == +using Base: coalesce, notnothing export with, GitRepo, GitConfig @@ -262,7 +263,7 @@ Equivalent to `git fetch [|] []`. function fetch(repo::GitRepo; remote::AbstractString="origin", remoteurl::AbstractString="", refspecs::Vector{<:AbstractString}=AbstractString[], - payload::Union{CredentialPayload,Nullable{<:Union{AbstractCredential, CachedCredentials}}}=CredentialPayload()) + payload::Union{CredentialPayload, AbstractCredential, CachedCredentials, Void}=CredentialPayload()) p = reset!(deprecate_nullable_creds(:fetch, "repo", payload), GitConfig(repo)) rmt = if isempty(remoteurl) get(GitRemote, repo, remote) @@ -304,7 +305,7 @@ function push(repo::GitRepo; remote::AbstractString="origin", remoteurl::AbstractString="", refspecs::Vector{<:AbstractString}=AbstractString[], force::Bool=false, - payload::Union{CredentialPayload,Nullable{<:Union{AbstractCredential, CachedCredentials}}}=CredentialPayload()) + payload::Union{CredentialPayload, AbstractCredential, CachedCredentials, Void}=CredentialPayload()) p = reset!(deprecate_nullable_creds(:push, "repo", payload), GitConfig(repo)) rmt = if isempty(remoteurl) get(GitRemote, repo, remote) @@ -372,22 +373,22 @@ function branch!(repo::GitRepo, branch_name::AbstractString, force::Bool=false, # force branch creation set_head::Bool=true) # set as head reference on exit # try to lookup branch first - branch_ref = force ? Nullable{GitReference}() : lookup_branch(repo, branch_name) - if isnull(branch_ref) - branch_rmt_ref = isempty(track) ? Nullable{GitReference}() : lookup_branch(repo, "$track/$branch_name", true) + branch_ref = force ? nothing : lookup_branch(repo, branch_name) + if branch_ref === nothing + branch_rmt_ref = isempty(track) ? nothing : lookup_branch(repo, "$track/$branch_name", true) # if commit is empty get head commit oid commit_id = if isempty(commit) - if isnull(branch_rmt_ref) + if branch_rmt_ref === nothing with(head(repo)) do head_ref with(peel(GitCommit, head_ref)) do hrc GitHash(hrc) end end else - tmpcmt = with(peel(GitCommit, Base.get(branch_rmt_ref))) do hrc + tmpcmt = with(peel(GitCommit, branch_rmt_ref)) do hrc GitHash(hrc) end - close(Base.get(branch_rmt_ref)) + close(branch_rmt_ref) tmpcmt end else @@ -397,10 +398,10 @@ function branch!(repo::GitRepo, branch_name::AbstractString, cmt = GitCommit(repo, commit_id) new_branch_ref = nothing try - new_branch_ref = Nullable(create_branch(repo, branch_name, cmt, force=force)) + new_branch_ref = create_branch(repo, branch_name, cmt, force=force) finally close(cmt) - isnull(new_branch_ref) && throw(GitError(Error.Object, Error.ERROR, "cannot create branch `$branch_name` with `$commit_id`")) + new_branch_ref === nothing && throw(GitError(Error.Object, Error.ERROR, "cannot create branch `$branch_name` with `$commit_id`")) branch_ref = new_branch_ref end end @@ -410,7 +411,7 @@ function branch!(repo::GitRepo, branch_name::AbstractString, try with(GitConfig, repo) do cfg set!(cfg, "branch.$branch_name.remote", Consts.REMOTE_ORIGIN) - set!(cfg, "branch.$branch_name.merge", name(Base.get(branch_ref))) + set!(cfg, "branch.$branch_name.merge", name(branch_ref)) end catch @warn "Please provide remote tracking for branch '$branch_name' in '$(path(repo))'" @@ -419,15 +420,15 @@ function branch!(repo::GitRepo, branch_name::AbstractString, if set_head # checkout selected branch - with(peel(GitTree, Base.get(branch_ref))) do btree + with(peel(GitTree, branch_ref)) do btree checkout_tree(repo, btree) end # switch head to the branch - head!(repo, Base.get(branch_ref)) + head!(repo, branch_ref) end finally - close(Base.get(branch_ref)) + close(branch_ref) end return end @@ -520,7 +521,7 @@ function clone(repo_url::AbstractString, repo_path::AbstractString; branch::AbstractString="", isbare::Bool = false, remote_cb::Ptr{Void} = C_NULL, - payload::Union{CredentialPayload,Nullable{<:Union{AbstractCredential, CachedCredentials}}}=CredentialPayload()) + payload::Union{CredentialPayload, AbstractCredential, CachedCredentials, Void}=CredentialPayload()) # setup clone options lbranch = Base.cconvert(Cstring, branch) @Base.gc_preserve lbranch begin @@ -549,7 +550,7 @@ end function reset!(repo::GitRepo, committish::AbstractString, pathspecs::AbstractString...) obj = GitObject(repo, isempty(committish) ? Consts.HEAD_FILE : committish) # do not remove entries in the index matching the provided pathspecs with empty target commit tree - reset!(repo, Nullable(obj), pathspecs...) + reset!(repo, obj, pathspecs...) end """ @@ -721,14 +722,14 @@ function merge!(repo::GitRepo; else with(head(repo)) do head_ref tr_brn_ref = upstream(head_ref) - if isnull(tr_brn_ref) + if tr_brn_ref === nothing throw(GitError(Error.Merge, Error.ERROR, "There is no tracking information for the current branch.")) end try - [GitAnnotated(repo, Base.get(tr_brn_ref))] + [GitAnnotated(repo, tr_brn_ref)] finally - close(Base.get(tr_brn_ref)) + close(tr_brn_ref) end end end @@ -765,19 +766,19 @@ function rebase!(repo::GitRepo, upstream::AbstractString="", newbase::AbstractSt head_ann = GitAnnotated(repo, head_ref) upst_ann = if isempty(upstream) brn_ref = LibGit2.upstream(head_ref) - if isnull(brn_ref) + if brn_ref === nothing throw(GitError(Error.Rebase, Error.ERROR, "There is no tracking information for the current branch.")) end try - GitAnnotated(repo, Base.get(brn_ref)) + GitAnnotated(repo, brn_ref) finally close(brn_ref) end else GitAnnotated(repo, upstream) end - onto_ann = Nullable{GitAnnotated}(isempty(newbase) ? nothing : GitAnnotated(repo, newbase)) + onto_ann = isempty(newbase) ? nothing : GitAnnotated(repo, newbase) try sig = default_signature(repo) try @@ -794,12 +795,12 @@ function rebase!(repo::GitRepo, upstream::AbstractString="", newbase::AbstractSt close(rbs) end finally - #!isnull(onto_ann) && close(get(onto_ann)) + #onto_ann !== nothing && close(onto_ann) close(sig) end finally if !isempty(newbase) - close(Base.get(onto_ann)) + close(onto_ann) end close(upst_ann) close(head_ann) @@ -885,7 +886,7 @@ function restore(s::State, repo::GitRepo) opts = CheckoutOptions( checkout_strategy = Consts.CHECKOUT_FORCE | # check the index out to work Consts.CHECKOUT_REMOVE_UNTRACKED) # remove everything else - checkout_index(repo, Nullable(idx), options = opts) + checkout_index(repo, idx, options = opts) read_tree!(idx, s.index) # restore index end diff --git a/base/libgit2/rebase.jl b/base/libgit2/rebase.jl index 579d17fba97ae..030db04fef6d0 100644 --- a/base/libgit2/rebase.jl +++ b/base/libgit2/rebase.jl @@ -1,14 +1,14 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license function GitRebase(repo::GitRepo, branch::GitAnnotated, upstream::GitAnnotated; - onto::Nullable{GitAnnotated}=Nullable{GitAnnotated}(), + onto::Union{GitAnnotated, Void}=nothing, opts::RebaseOptions = RebaseOptions()) rebase_ptr_ptr = Ref{Ptr{Void}}(C_NULL) @check ccall((:git_rebase_init, :libgit2), Cint, (Ptr{Ptr{Void}}, Ptr{Void}, Ptr{Void}, Ptr{Void}, Ptr{Void}, Ptr{RebaseOptions}), rebase_ptr_ptr, repo.ptr, branch.ptr, upstream.ptr, - isnull(onto) ? C_NULL : Base.get(onto).ptr, Ref(opts)) + onto === nothing ? C_NULL : onto.ptr, Ref(opts)) return GitRebase(repo, rebase_ptr_ptr[]) end diff --git a/base/libgit2/reference.jl b/base/libgit2/reference.jl index e703d215d3643..af59f722fdaee 100644 --- a/base/libgit2/reference.jl +++ b/base/libgit2/reference.jl @@ -247,15 +247,14 @@ function head!(repo::GitRepo, ref::GitReference) end """ - lookup_branch(repo::GitRepo, branch_name::AbstractString, remote::Bool=false) -> Nullable{GitReference} + lookup_branch(repo::GitRepo, branch_name::AbstractString, remote::Bool=false) -> Union{GitReference, Void} Determine if the branch specified by `branch_name` exists in the repository `repo`. If `remote` is `true`, `repo` is assumed to be a remote git repository. Otherwise, it is part of the local filesystem. -Return a [`Nullable`](@ref), which will be null if the requested branch does -not exist yet. If the branch does exist, the `Nullable` contains a `GitReference` to -the branch. +Return either a `GitReference` to the requested branch +if it exists, or [`nothing`](@ref) if not. """ function lookup_branch(repo::GitRepo, branch_name::AbstractString, @@ -267,24 +266,23 @@ function lookup_branch(repo::GitRepo, ref_ptr_ptr, repo.ptr, branch_name, branch_type) if err != Int(Error.GIT_OK) if err == Int(Error.ENOTFOUND) - return Nullable{GitReference}() + return nothing end if ref_ptr_ptr[] != C_NULL close(GitReference(repo, ref_ptr_ptr[])) end throw(Error.GitError(err)) end - return Nullable{GitReference}(GitReference(repo, ref_ptr_ptr[])) + return GitReference(repo, ref_ptr_ptr[]) end """ - upstream(ref::GitReference) -> Nullable{GitReference} + upstream(ref::GitReference) -> Union{GitReference, Void} Determine if the branch containing `ref` has a specified upstream branch. -Return a [`Nullable`](@ref), which will be null if the requested branch does -not have an upstream counterpart. If the upstream branch does exist, the `Nullable` -contains a `GitReference` to the upstream branch. +Return either a `GitReference` to the upstream branch if it exists, +or [`nothing`](@ref) if the requested branch does not have an upstream counterpart. """ function upstream(ref::GitReference) isempty(ref) && return nothing @@ -293,14 +291,14 @@ function upstream(ref::GitReference) (Ref{Ptr{Void}}, Ptr{Void},), ref_ptr_ptr, ref.ptr) if err != Int(Error.GIT_OK) if err == Int(Error.ENOTFOUND) - return Nullable{GitReference}() + return nothing end if ref_ptr_ptr[] != C_NULL close(GitReference(ref.owner, ref_ptr_ptr[])) end throw(Error.GitError(err)) end - return Nullable{GitReference}(GitReference(ref.owner, ref_ptr_ptr[])) + return GitReference(ref.owner, ref_ptr_ptr[]) end repository(ref::GitReference) = ref.owner diff --git a/base/libgit2/remote.jl b/base/libgit2/remote.jl index 67d328aa59171..a03a5a0eb1799 100644 --- a/base/libgit2/remote.jl +++ b/base/libgit2/remote.jl @@ -61,17 +61,17 @@ function GitRemoteAnon(repo::GitRepo, url::AbstractString) end """ - lookup_remote(repo::GitRepo, remote_name::AbstractString) -> Nullable{GitRemote} + lookup_remote(repo::GitRepo, remote_name::AbstractString) -> Union{GitRemote, Void} -Determine if the `remote_name` specified exists within the `repo`. Return a -[`Nullable`](@ref), which will be null if the requested remote does not exist. If the remote -does exist, the `Nullable` contains a [`GitRemote`](@ref) to the remote name. +Determine if the `remote_name` specified exists within the `repo`. Return +either a [`GitRemote`](@ref) to the remote name if it exists, or [`nothing`](@ref) +if not. # Examples ```julia repo = LibGit2.GitRepo(path) remote_name = "test" -isnull(LibGit2.lookup_remote(repo, remote_name)) # will return true +LibGit2.lookup_remote(repo, remote_name) # will return nothing ``` """ function lookup_remote(repo::GitRepo, remote_name::AbstractString) @@ -80,9 +80,9 @@ function lookup_remote(repo::GitRepo, remote_name::AbstractString) (Ptr{Ptr{Void}}, Ptr{Void}, Cstring), rmt_ptr_ptr, repo.ptr, remote_name) if err == Int(Error.GIT_OK) - return Nullable{GitRemote}(GitRemote(repo, rmt_ptr_ptr[])) + return GitRemote(repo, rmt_ptr_ptr[]) elseif err == Int(Error.ENOTFOUND) - return Nullable{GitRemote}() + return nothing else throw(Error.GitError(err)) end diff --git a/base/libgit2/repository.jl b/base/libgit2/repository.jl index f73aea52af434..6a00beb80ec43 100644 --- a/base/libgit2/repository.jl +++ b/base/libgit2/repository.jl @@ -336,18 +336,18 @@ function checkout_tree(repo::GitRepo, obj::GitObject; end """ - checkout_index(repo::GitRepo, idx::Nullable{GitIndex} = Nullable{GitIndex}(); options::CheckoutOptions = CheckoutOptions()) + checkout_index(repo::GitRepo, idx::Union{GitIndex, Void} = nothing; options::CheckoutOptions = CheckoutOptions()) -Update the working tree of `repo` to match the index `idx`. If `idx` is null, the +Update the working tree of `repo` to match the index `idx`. If `idx` is `nothing`, the index of `repo` will be used. `options` controls how the checkout will be performed. See [`CheckoutOptions`](@ref) for more information. """ -function checkout_index(repo::GitRepo, idx::Nullable{GitIndex} = Nullable{GitIndex}(); +function checkout_index(repo::GitRepo, idx::Union{GitIndex, Void} = nothing; options::CheckoutOptions = CheckoutOptions()) @check ccall((:git_checkout_index, :libgit2), Cint, (Ptr{Void}, Ptr{Void}, Ptr{CheckoutOptions}), repo.ptr, - isnull(idx) ? C_NULL : Base.get(idx).ptr, + idx === nothing ? C_NULL : idx.ptr, Ref(options)) end @@ -385,11 +385,11 @@ function cherrypick(repo::GitRepo, commit::GitCommit; options::CherrypickOptions end """Updates some entries, determined by the `pathspecs`, in the index from the target commit tree.""" -function reset!(repo::GitRepo, obj::Nullable{<:GitObject}, pathspecs::AbstractString...) +function reset!(repo::GitRepo, obj::Union{GitObject, Void}, pathspecs::AbstractString...) @check ccall((:git_reset_default, :libgit2), Cint, (Ptr{Void}, Ptr{Void}, Ptr{StrArrayStruct}), repo.ptr, - isnull(obj) ? C_NULL : Base.get(obj).ptr, + obj === nothing ? C_NULL : obj.ptr, collect(pathspecs)) return head_oid(repo) end diff --git a/base/libgit2/status.jl b/base/libgit2/status.jl index dcb0f2092bd21..f31b643139ca6 100644 --- a/base/libgit2/status.jl +++ b/base/libgit2/status.jl @@ -36,7 +36,7 @@ function Base.getindex(status::GitStatus, i::Integer) end """ - LibGit2.status(repo::GitRepo, path::String) + LibGit2.status(repo::GitRepo, path::String) -> Union{Cuint, Void} Lookup the status of the file at `path` in the git repository `repo`. For instance, this can be used @@ -48,6 +48,6 @@ function status(repo::GitRepo, path::String) ret = ccall((:git_status_file, :libgit2), Cint, (Ref{Cuint}, Ptr{Void}, Cstring), status_ptr, repo.ptr, path) - (ret == Cint(Error.ENOTFOUND) || ret == Cint(Error.EAMBIGUOUS)) && return Nullable{Cuint}() - return Nullable(status_ptr[]) + (ret == Cint(Error.ENOTFOUND) || ret == Cint(Error.EAMBIGUOUS)) && return nothing + return status_ptr[] end diff --git a/base/libgit2/types.jl b/base/libgit2/types.jl index a55676b953cc0..f8b552af32f5e 100644 --- a/base/libgit2/types.jl +++ b/base/libgit2/types.jl @@ -911,27 +911,27 @@ Base.isempty(obj::AbstractGitObject) = (obj.ptr == C_NULL) abstract type GitObject <: AbstractGitObject end for (typ, owntyp, sup, cname) in [ - (:GitRepo, nothing, :AbstractGitObject, :git_repository), - (:GitConfig, :(Nullable{GitRepo}), :AbstractGitObject, :git_config), - (:GitIndex, :(Nullable{GitRepo}), :AbstractGitObject, :git_index), - (:GitRemote, :GitRepo, :AbstractGitObject, :git_remote), - (:GitRevWalker, :GitRepo, :AbstractGitObject, :git_revwalk), - (:GitReference, :GitRepo, :AbstractGitObject, :git_reference), - (:GitDescribeResult, :GitRepo, :AbstractGitObject, :git_describe_result), - (:GitDiff, :GitRepo, :AbstractGitObject, :git_diff), - (:GitDiffStats, :GitRepo, :AbstractGitObject, :git_diff_stats), - (:GitAnnotated, :GitRepo, :AbstractGitObject, :git_annotated_commit), - (:GitRebase, :GitRepo, :AbstractGitObject, :git_rebase), - (:GitBlame, :GitRepo, :AbstractGitObject, :git_blame), - (:GitStatus, :GitRepo, :AbstractGitObject, :git_status_list), - (:GitBranchIter, :GitRepo, :AbstractGitObject, :git_branch_iterator), - (:GitConfigIter, nothing, :AbstractGitObject, :git_config_iterator), - (:GitUnknownObject, :GitRepo, :GitObject, :git_object), - (:GitCommit, :GitRepo, :GitObject, :git_commit), - (:GitBlob, :GitRepo, :GitObject, :git_blob), - (:GitTree, :GitRepo, :GitObject, :git_tree), - (:GitTag, :GitRepo, :GitObject, :git_tag), - (:GitTreeEntry, :GitTree, :AbstractGitObject, :git_tree_entry), + (:GitRepo, nothing, :AbstractGitObject, :git_repository), + (:GitConfig, :(Union{GitRepo, Void}), :AbstractGitObject, :git_config), + (:GitIndex, :(Union{GitRepo, Void}), :AbstractGitObject, :git_index), + (:GitRemote, :GitRepo, :AbstractGitObject, :git_remote), + (:GitRevWalker, :GitRepo, :AbstractGitObject, :git_revwalk), + (:GitReference, :GitRepo, :AbstractGitObject, :git_reference), + (:GitDescribeResult, :GitRepo, :AbstractGitObject, :git_describe_result), + (:GitDiff, :GitRepo, :AbstractGitObject, :git_diff), + (:GitDiffStats, :GitRepo, :AbstractGitObject, :git_diff_stats), + (:GitAnnotated, :GitRepo, :AbstractGitObject, :git_annotated_commit), + (:GitRebase, :GitRepo, :AbstractGitObject, :git_rebase), + (:GitBlame, :GitRepo, :AbstractGitObject, :git_blame), + (:GitStatus, :GitRepo, :AbstractGitObject, :git_status_list), + (:GitBranchIter, :GitRepo, :AbstractGitObject, :git_branch_iterator), + (:GitConfigIter, nothing, :AbstractGitObject, :git_config_iterator), + (:GitUnknownObject, :GitRepo, :GitObject, :git_object), + (:GitCommit, :GitRepo, :GitObject, :git_commit), + (:GitBlob, :GitRepo, :GitObject, :git_blob), + (:GitTree, :GitRepo, :GitObject, :git_tree), + (:GitTag, :GitRepo, :GitObject, :git_tag), + (:GitTreeEntry, :GitTree, :AbstractGitObject, :git_tree_entry), ] if owntyp === nothing @@ -963,11 +963,9 @@ for (typ, owntyp, sup, cname) in [ return obj end end - if isa(owntyp, Expr) && owntyp.args[1] == :Nullable + if isa(owntyp, Expr) && owntyp.args[1] == :Union && owntyp.args[3] == :Void @eval begin - $typ(ptr::Ptr{Void}, fin::Bool=true) = $typ($owntyp(), ptr, fin) - $typ(owner::$(owntyp.args[2]), ptr::Ptr{Void}, fin::Bool=true) = - $typ($owntyp(owner), ptr, fin) + $typ(ptr::Ptr{Void}, fin::Bool=true) = $typ(nothing, ptr, fin) end end end @@ -1251,8 +1249,8 @@ A `CredentialPayload` instance is expected to be `reset!` whenever it will be us different URL. """ mutable struct CredentialPayload <: Payload - explicit::Nullable{AbstractCredential} - cache::Nullable{CachedCredentials} + explicit::Union{AbstractCredential, Void} + cache::Union{CachedCredentials, Void} allow_ssh_agent::Bool # Allow the use of the SSH agent to get credentials allow_git_helpers::Bool # Allow the use of git credential helpers allow_prompt::Bool # Allow prompting the user for credentials @@ -1260,7 +1258,7 @@ mutable struct CredentialPayload <: Payload config::GitConfig # Ephemeral state fields - credential::Nullable{AbstractCredential} + credential::Union{AbstractCredential, Void} first_pass::Bool use_ssh_agent::Bool use_env::Bool @@ -1273,8 +1271,8 @@ mutable struct CredentialPayload <: Payload host::String function CredentialPayload( - credential::Nullable{<:AbstractCredential}=Nullable{AbstractCredential}(), - cache::Nullable{CachedCredentials}=Nullable{CachedCredentials}(), + credential::Union{AbstractCredential, Void}=nothing, + cache::Union{CachedCredentials, Void}=nothing, config::GitConfig=GitConfig(); allow_ssh_agent::Bool=true, allow_git_helpers::Bool=true, @@ -1286,11 +1284,11 @@ mutable struct CredentialPayload <: Payload end function CredentialPayload(credential::AbstractCredential; kwargs...) - CredentialPayload(Nullable(credential), Nullable{CachedCredentials}(); kwargs...) + CredentialPayload(credential, nothing; kwargs...) end function CredentialPayload(cache::CachedCredentials; kwargs...) - CredentialPayload(Nullable{AbstractCredential}(), Nullable(cache); kwargs...) + CredentialPayload(nothing, cache; kwargs...) end """ @@ -1301,7 +1299,7 @@ the credential callback. If a `config` is provided the configuration will also b """ function reset!(p::CredentialPayload, config::GitConfig=p.config) p.config = config - p.credential = Nullable{AbstractCredential}() + p.credential = nothing p.first_pass = true p.use_ssh_agent = p.allow_ssh_agent p.use_env = true @@ -1325,11 +1323,11 @@ The `shred` keyword controls whether sensitive information in the payload creden should be destroyed. Should only be set to `false` during testing. """ function approve(p::CredentialPayload; shred::Bool=true) - isnull(p.credential) && return # No credentials were used - cred = unsafe_get(p.credential) + cred = p.credential + cred === nothing && return # No credentials were used - if !isnull(p.cache) - approve(unsafe_get(p.cache), cred, p.url) + if p.cache !== nothing + approve(p.cache, cred, p.url) shred = false # Avoid wiping `cred` as this would also wipe the cached copy end if p.allow_git_helpers @@ -1350,11 +1348,11 @@ The `shred` keyword controls whether sensitive information in the payload creden should be destroyed. Should only be set to `false` during testing. """ function reject(p::CredentialPayload; shred::Bool=true) - isnull(p.credential) && return # No credentials were used - cred = unsafe_get(p.credential) + cred = p.credential + cred === nothing && return # No credentials were used - if !isnull(p.cache) - reject(unsafe_get(p.cache), cred, p.url) + if p.cache !== nothing + reject(p.cache, cred, p.url) shred = false # Avoid wiping `cred` as this would also wipe the cached copy end if p.allow_git_helpers diff --git a/base/libgit2/utils.jl b/base/libgit2/utils.jl index ed455035b30d3..1eacbd5e7d180 100644 --- a/base/libgit2/utils.jl +++ b/base/libgit2/utils.jl @@ -163,7 +163,7 @@ end function credential_identifier(url::AbstractString) m = match(URL_REGEX, url) - scheme = m[:scheme] === nothing ? "" : m[:scheme] + scheme = coalesce(m[:scheme], "") host = m[:host] credential_identifier(scheme, host) end diff --git a/base/lock.jl b/base/lock.jl index 533795a1cf072..2b427e84a6398 100644 --- a/base/lock.jl +++ b/base/lock.jl @@ -11,7 +11,7 @@ Each `lock` must be matched with an `unlock`. This lock is NOT threadsafe. See `Threads.Mutex` for a threadsafe lock. """ mutable struct ReentrantLock - locked_by::Nullable{Task} + locked_by::Union{Task, Void} cond_wait::Condition reentrancy_cnt::Int @@ -44,7 +44,7 @@ function trylock(rl::ReentrantLock) rl.locked_by = t rl.reentrancy_cnt = 1 return true - elseif t == get(rl.locked_by) + elseif t == notnothing(rl.locked_by) rl.reentrancy_cnt += 1 return true end @@ -67,7 +67,7 @@ function lock(rl::ReentrantLock) rl.locked_by = t rl.reentrancy_cnt = 1 return - elseif t == get(rl.locked_by) + elseif t == notnothing(rl.locked_by) rl.reentrancy_cnt += 1 return end diff --git a/base/methodshow.jl b/base/methodshow.jl index c10805e5e5684..35bae1f1671a0 100644 --- a/base/methodshow.jl +++ b/base/methodshow.jl @@ -102,7 +102,7 @@ function show_method_params(io::IO, tv) end end -function show(io::IO, m::Method; kwtype::Nullable{DataType}=Nullable{DataType}()) +function show(io::IO, m::Method; kwtype::Union{DataType, Void}=nothing) tv, decls, file, line = arg_decl_parts(m) sig = unwrap_unionall(m.sig) ft0 = sig.parameters[1] @@ -129,8 +129,8 @@ function show(io::IO, m::Method; kwtype::Nullable{DataType}=Nullable{DataType}() print(io, "(") join(io, [isempty(d[2]) ? d[1] : d[1]*"::"*d[2] for d in decls[2:end]], ", ", ", ") - if !isnull(kwtype) - kwargs = kwarg_decl(m, get(kwtype)) + if kwtype !== nothing + kwargs = kwarg_decl(m, kwtype) if !isempty(kwargs) print(io, "; ") join(io, kwargs, ", ", ", ") @@ -157,7 +157,7 @@ function show_method_table(io::IO, ms::MethodList, max::Int=-1, header::Bool=tru what = startswith(ns, '@') ? "macro" : "generic function" print(io, "# $n $m for ", what, " \"", ns, "\":") end - kwtype = isdefined(mt, :kwsorter) ? Nullable{DataType}(typeof(mt.kwsorter)) : Nullable{DataType}() + kwtype = isdefined(mt, :kwsorter) ? typeof(mt.kwsorter) : nothing n = rest = 0 local last @@ -233,7 +233,7 @@ function url(m::Method) end end -function show(io::IO, ::MIME"text/html", m::Method; kwtype::Nullable{DataType}=Nullable{DataType}()) +function show(io::IO, ::MIME"text/html", m::Method; kwtype::Union{DataType, Void}=nothing) tv, decls, file, line = arg_decl_parts(m) sig = unwrap_unionall(m.sig) ft0 = sig.parameters[1] @@ -261,8 +261,8 @@ function show(io::IO, ::MIME"text/html", m::Method; kwtype::Nullable{DataType}=N print(io, "(") join(io, [isempty(d[2]) ? d[1] : d[1]*"::"*d[2]*"" for d in decls[2:end]], ", ", ", ") - if !isnull(kwtype) - kwargs = kwarg_decl(m, get(kwtype)) + if kwtype !== nothing + kwargs = kwarg_decl(m, kwtype) if !isempty(kwargs) print(io, "; ") join(io, kwargs, ", ", ", ") @@ -290,7 +290,7 @@ function show(io::IO, mime::MIME"text/html", ms::MethodList) ns = string(name) what = startswith(ns, '@') ? "macro" : "generic function" print(io, "$n $meths for ", what, " $ns:
    ") - kwtype = isdefined(mt, :kwsorter) ? Nullable{DataType}(typeof(mt.kwsorter)) : Nullable{DataType}() + kwtype = isdefined(mt, :kwsorter) ? typeof(mt.kwsorter) : nothing for meth in ms print(io, "
  • ") show(io, mime, meth; kwtype=kwtype) diff --git a/base/missing.jl b/base/missing.jl index f721edf51ef5a..f7e04f096d489 100644 --- a/base/missing.jl +++ b/base/missing.jl @@ -31,9 +31,13 @@ promote_rule(::Type{Missing}, ::Type{Any}) = Any promote_rule(::Type{Missing}, ::Type{Missing}) = Missing convert(::Type{Union{T, Missing}}, x) where {T} = convert(T, x) +# To fix ambiguities +convert(::Type{Missing}, ::Missing) = missing +convert(::Type{Union{Void, Missing}}, x::Union{Void, Missing}) = x +convert(::Type{Union{Void, Missing}}, x) = + throw(MethodError(convert, (Union{Void, Missing}, x))) # To print more appropriate message than "T not defined" convert(::Type{Missing}, x) = throw(MethodError(convert, (Missing, x))) -convert(::Type{Missing}, ::Missing) = missing # Comparison operators ==(::Missing, ::Missing) = missing diff --git a/base/mpfr.jl b/base/mpfr.jl index 227335de7f2e9..c3208e53c5d34 100644 --- a/base/mpfr.jl +++ b/base/mpfr.jl @@ -125,7 +125,7 @@ function tryparse(::Type{BigFloat}, s::AbstractString, base::Int=0) !isempty(s) && Base.Unicode.isspace(s[end]) && return tryparse(BigFloat, rstrip(s), base) z = BigFloat() err = ccall((:mpfr_set_str, :libmpfr), Int32, (Ref{BigFloat}, Cstring, Int32, Int32), z, s, base, ROUNDING_MODE[]) - err == 0 ? Nullable(z) : Nullable{BigFloat}() + err == 0 ? z : nothing end convert(::Type{Rational}, x::BigFloat) = convert(Rational{BigInt}, x) diff --git a/base/nullable.jl b/base/nullable.jl deleted file mode 100644 index bb3075576eb1e..0000000000000 --- a/base/nullable.jl +++ /dev/null @@ -1,419 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -""" - NullException() - -An attempted access to a [`Nullable`](@ref) with no defined value. - -# Examples -```jldoctest -julia> a = Nullable{Int}() -Nullable{Int64}() - -julia> get(a) -ERROR: NullException() -Stacktrace: -[...] -``` -""" -struct NullException <: Exception -end - -""" - Nullable(x, hasvalue::Bool=true) - -Wrap value `x` in an object of type `Nullable`, which indicates whether a value is present. -`Nullable(x)` yields a non-empty wrapper and `Nullable{T}()` yields an empty instance of a -wrapper that might contain a value of type `T`. - -`Nullable(x, false)` yields `Nullable{typeof(x)}()` with `x` stored in the result's `value` -field. - -# Examples -```jldoctest -julia> Nullable(1) -Nullable{Int64}(1) - -julia> Nullable{Int64}() -Nullable{Int64}() - -julia> Nullable(1, false) -Nullable{Int64}() - -julia> dump(Nullable(1, false)) -Nullable{Int64} - hasvalue: Bool false - value: Int64 1 -``` -""" -Nullable(value::T, hasvalue::Bool=true) where {T} = Nullable{T}(value, hasvalue) -Nullable() = Nullable{Union{}}() - -eltype(::Type{Nullable{T}}) where {T} = T - -convert(::Type{Nullable{T}}, x::Nullable{T}) where {T} = x -convert(::Type{Nullable }, x::Nullable ) = x - -convert(t::Type{Nullable{T}}, x::Any) where {T} = convert(t, convert(T, x)) - -function convert(::Type{Nullable{T}}, x::Nullable) where T - return isnull(x) ? Nullable{T}() : Nullable{T}(convert(T, get(x))) -end - -convert(::Type{Nullable{T}}, x::T) where {T<:Nullable} = Nullable{T}(x) -convert(::Type{Nullable{T}}, x::T) where {T} = Nullable{T}(x) -convert(::Type{Nullable }, x::T) where {T} = Nullable{T}(x) - -convert(::Type{Nullable{T}}, ::Void) where {T} = Nullable{T}() -convert(::Type{Nullable }, ::Void) = Nullable{Union{}}() - -promote_rule(::Type{Nullable{S}}, ::Type{T}) where {S,T} = Nullable{promote_type(S, T)} -promote_rule(::Type{Nullable{S}}, ::Type{Nullable{T}}) where {S,T} = Nullable{promote_type(S, T)} -promote_op(op::Any, ::Type{Nullable{S}}, ::Type{Nullable{T}}) where {S,T} = Nullable{promote_op(op, S, T)} -promote_op(op::Type, ::Type{Nullable{S}}, ::Type{Nullable{T}}) where {S,T} = Nullable{promote_op(op, S, T)} - -function show(io::IO, x::Nullable) - if get(io, :compact, false) - if isnull(x) - print(io, "#NULL") - else - show(io, x.value) - end - else - print(io, "Nullable{") - showcompact(io, eltype(x)) - print(io, "}(") - if !isnull(x) - showcompact(io, x.value) - end - print(io, ')') - end -end - -""" - get(x::Nullable[, y]) - -Attempt to access the value of `x`. Returns the value if it is present; -otherwise, returns `y` if provided, or throws a `NullException` if not. - -# Examples -```jldoctest -julia> get(Nullable(5)) -5 - -julia> get(Nullable()) -ERROR: NullException() -Stacktrace: -[...] -``` -""" -@inline function get(x::Nullable{T}, y) where T - if isbits(T) - ifelse(isnull(x), y, x.value) - else - isnull(x) ? y : x.value - end -end - -get(x::Nullable) = isnull(x) ? throw(NullException()) : x.value - -""" - unsafe_get(x) - -Return the value of `x` for [`Nullable`](@ref) `x`; return `x` for -all other `x`. - -This method does not check whether or not `x` is null before attempting to -access the value of `x` for `x::Nullable` (hence "unsafe"). - -# Examples -```jldoctest -julia> x = Nullable(1) -Nullable{Int64}(1) - -julia> unsafe_get(x) -1 - -julia> x = Nullable{String}() -Nullable{String}() - -julia> unsafe_get(x) -ERROR: UndefRefError: access to undefined reference -Stacktrace: -[...] - -julia> x = 1 -1 - -julia> unsafe_get(x) -1 -``` -""" -unsafe_get(x::Nullable) = x.value -unsafe_get(x) = x - -""" - isnull(x) - -Return whether or not `x` is null for [`Nullable`](@ref) `x`; return -`false` for all other `x`. - -# Examples -```jldoctest -julia> x = Nullable(1, false) -Nullable{Int64}() - -julia> isnull(x) -true - -julia> x = Nullable(1, true) -Nullable{Int64}(1) - -julia> isnull(x) -false - -julia> x = 1 -1 - -julia> isnull(x) -false -``` -""" -isnull(x::Nullable) = !x.hasvalue -isnull(x) = false - -## Operators - -""" - null_safe_op(f::Any, ::Type, ::Type...)::Bool - -Returns whether an operation `f` can safely be applied to any value of the passed type(s). -Returns `false` by default. - -Custom types should implement methods for some or all operations `f` when applicable: -returning `true` means that the operation may be called on any bit pattern without -throwing an error (though returning invalid or nonsensical results is not a problem). -In particular, this means that the operation can be applied on the whole domain of the -type *and on uninitialized objects*. As a general rule, these properties are only true for -safe operations on `isbits` types. - -Types declared as safe can benefit from higher performance for operations on nullable: by -always computing the result even for null values, a branch is avoided, which helps -vectorization. -""" -null_safe_op(f::Any, ::Type, ::Type...) = false - -const NullSafeSignedInts = Union{Type{Int128}, Type{Int16}, Type{Int32}, - Type{Int64}, Type{Int8}} -const NullSafeUnsignedInts = Union{Type{Bool}, Type{UInt128}, Type{UInt16}, - Type{UInt32}, Type{UInt64}, Type{UInt8}} -const NullSafeInts = Union{NullSafeSignedInts, NullSafeUnsignedInts} -const NullSafeFloats = Union{Type{Float16}, Type{Float32}, Type{Float64}} -const NullSafeTypes = Union{NullSafeInts, NullSafeFloats} -const EqualOrLess = Union{typeof(isequal), typeof(isless)} - -null_safe_op(::typeof(identity), ::Type{T}) where {T} = isbits(T) - -null_safe_op(f::EqualOrLess, ::NullSafeTypes, ::NullSafeTypes) = true -null_safe_op(f::EqualOrLess, ::Type{Rational{S}}, ::Type{T}) where {S,T} = - null_safe_op(f, T, S) -# complex numbers can be compared for equality but not in general ordered -null_safe_op(::typeof(isequal), ::Type{Complex{S}}, ::Type{T}) where {S,T} = - null_safe_op(isequal, T, S) - -""" - isequal(x::Nullable, y::Nullable) - -If neither `x` nor `y` is null, compare them according to their values -(i.e. `isequal(get(x), get(y))`). Else, return `true` if both arguments are null, -and `false` if one is null but not the other: nulls are considered equal. - -# Examples -```jldoctest -julia> isequal(Nullable(5), Nullable(5)) -true - -julia> isequal(Nullable(5), Nullable(4)) -false - -julia> isequal(Nullable(5), Nullable()) -false - -julia> isequal(Nullable(), Nullable()) -true -``` -""" -@inline function isequal(x::Nullable{S}, y::Nullable{T}) where {S,T} - if null_safe_op(isequal, S, T) - (isnull(x) & isnull(y)) | (!isnull(x) & !isnull(y) & isequal(x.value, y.value)) - else - (isnull(x) & isnull(y)) || (!isnull(x) & !isnull(y) && isequal(x.value, y.value)) - end -end - -isequal(x::Nullable{Union{}}, y::Nullable{Union{}}) = true -isequal(x::Nullable{Union{}}, y::Nullable) = isnull(y) -isequal(x::Nullable, y::Nullable{Union{}}) = isnull(x) - -""" - isless(x::Nullable, y::Nullable) - -If neither `x` nor `y` is null, compare them according to their values -(i.e. `isless(get(x), get(y))`). Else, return `true` if only `y` is null, and `false` -otherwise: nulls are always considered greater than non-nulls, but not greater than -another null. - -# Examples -```jldoctest -julia> isless(Nullable(6), Nullable(5)) -false - -julia> isless(Nullable(5), Nullable(6)) -true - -julia> isless(Nullable(5), Nullable(4)) -false - -julia> isless(Nullable(5), Nullable()) -true - -julia> isless(Nullable(), Nullable()) -false - -julia> isless(Nullable(), Nullable(5)) -false -``` -""" -@inline function isless(x::Nullable{S}, y::Nullable{T}) where {S,T} - # NULL values are sorted last - if null_safe_op(isless, S, T) - (!isnull(x) & isnull(y)) | (!isnull(x) & !isnull(y) & isless(x.value, y.value)) - else - (!isnull(x) & isnull(y)) || (!isnull(x) & !isnull(y) && isless(x.value, y.value)) - end -end - -isless(x::Nullable{Union{}}, y::Nullable{Union{}}) = false -isless(x::Nullable{Union{}}, y::Nullable) = false -isless(x::Nullable, y::Nullable{Union{}}) = !isnull(x) - -==(x::Nullable, y::Nullable) = throw(NullException()) - -const nullablehash_seed = UInt === UInt64 ? 0x932e0143e51d0171 : 0xe51d0171 - -function hash(x::Nullable, h::UInt) - if isnull(x) - return h + nullablehash_seed - else - return hash(x.value, h + nullablehash_seed) - end -end - -# higher-order functions -""" - filter(p, x::Nullable) - -Return null if either `x` is null or `p(get(x))` is false, and `x` otherwise. - -# Examples -```jldoctest -julia> filter(isodd, Nullable(5)) -Nullable{Int64}(5) - -julia> filter(isodd, Nullable(4)) -Nullable{Int64}() - -julia> filter(isodd, Nullable{Int}()) -Nullable{Int64}() -``` -""" -function filter(p, x::Nullable{T}) where T - if isbits(T) - val = unsafe_get(x) - Nullable{T}(val, !isnull(x) && p(val)) - else - isnull(x) || p(unsafe_get(x)) ? x : Nullable{T}() - end -end - -""" -Return the given type if it is concrete, and `Union{}` otherwise. -""" -nullable_returntype(::Type{T}) where {T} = _isleaftype(T) ? T : Union{} - -""" - map(f, x::Nullable) - -Return `f` applied to the value of `x` if it has one, as a `Nullable`. If `x` -is null, then return a null value of type `Nullable{S}`. `S` is guaranteed to -be either `Union{}` or a concrete type. Whichever of these is chosen is an -implementation detail, but typically the choice that maximizes performance -would be used. If `x` has a value, then the return type is guaranteed to be of -type `Nullable{typeof(f(x))}`. - -# Examples -```jldoctest -julia> map(isodd, Nullable(1)) -Nullable{Bool}(true) - -julia> map(isodd, Nullable(2)) -Nullable{Bool}(false) - -julia> map(isodd, Nullable{Int}()) -Nullable{Bool}() -``` -""" -function map(f, x::Nullable{T}) where T - S = promote_op(f, T) - if _isleaftype(S) && null_safe_op(f, T) - Nullable(f(unsafe_get(x)), !isnull(x)) - else - if isnull(x) - Nullable{nullable_returntype(S)}() - else - Nullable(f(unsafe_get(x))) - end - end -end - -# We need the following function and specializations because LLVM cannot -# optimize !any(isnull, t) without further guidance. -hasvalue(x::Nullable) = x.hasvalue -hasvalue(x) = true -all(f::typeof(hasvalue), t::Tuple) = f(t[1]) & all(f, tail(t)) -all(f::typeof(hasvalue), t::Tuple{}) = true - -# Overloads of null_safe_op -# Unary operators - -# Note this list does not include sqrt since it can raise a DomainError -for op in (+, -, abs, abs2) - global null_safe_op - null_safe_op(::typeof(op), ::NullSafeTypes) = true - null_safe_op(::typeof(op), ::Type{Complex{S}}) where {S} = null_safe_op(op, S) - null_safe_op(::typeof(op), ::Type{Rational{S}}) where {S} = null_safe_op(op, S) -end - -null_safe_op(::typeof(~), ::NullSafeInts) = true -null_safe_op(::typeof(!), ::Type{Bool}) = true - -# Binary operators - -# Note this list does not include ^, ÷ and % -# Operations between signed and unsigned types are not safe: promotion to unsigned -# gives an InexactError for negative numbers -for op in (+, -, *, /, &, |, <<, >>, >>>, - scalarmin, scalarmax) - # to fix ambiguities - global null_safe_op - null_safe_op(::typeof(op), ::NullSafeFloats, ::NullSafeFloats) = true - null_safe_op(::typeof(op), ::NullSafeSignedInts, ::NullSafeSignedInts) = true - null_safe_op(::typeof(op), ::NullSafeUnsignedInts, ::NullSafeUnsignedInts) = true -end -for op in (+, -, *, /) - global null_safe_op - null_safe_op(::typeof(op), ::Type{Complex{S}}, ::Type{T}) where {S,T} = - null_safe_op(op, T, S) - null_safe_op(::typeof(op), ::Type{Rational{S}}, ::Type{T}) where {S,T} = - null_safe_op(op, T, S) -end diff --git a/base/nullabletype.jl b/base/nullabletype.jl deleted file mode 100644 index 8c1c9c3d1d91c..0000000000000 --- a/base/nullabletype.jl +++ /dev/null @@ -1,9 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -struct Nullable{T} - hasvalue::Bool - value::T - - Nullable{T}() where {T} = new(false) - Nullable{T}(value::T, hasvalue::Bool=true) where {T} = new(hasvalue, value) -end diff --git a/base/parse.jl b/base/parse.jl index 7181b3538c457..9ecf359f7452b 100644 --- a/base/parse.jl +++ b/base/parse.jl @@ -86,24 +86,23 @@ function parseint_preamble(signed::Bool, base::Int, s::AbstractString, startpos: end function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int, base_::Integer, raise::Bool) where T<:Integer - _n = Nullable{T}() sgn, base, i = parseint_preamble(T<:Signed, Int(base_), s, startpos, endpos) if sgn == 0 && base == 0 && i == 0 raise && throw(ArgumentError("input string is empty or only contains whitespace")) - return _n + return nothing end if !(2 <= base <= 62) raise && throw(ArgumentError("invalid base: base must be 2 ≤ base ≤ 62, got $base")) - return _n + return nothing end if i == 0 raise && throw(ArgumentError("premature end of integer: $(repr(SubString(s,startpos,endpos)))")) - return _n + return nothing end c, i = parseint_next(s,i,endpos) if i == 0 raise && throw(ArgumentError("premature end of integer: $(repr(SubString(s,startpos,endpos)))")) - return _n + return nothing end base = convert(T,base) @@ -116,13 +115,13 @@ function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos:: 'a' <= c <= 'z' ? c-'a'+a : base if d >= base raise && throw(ArgumentError("invalid base $base digit $(repr(c)) in $(repr(SubString(s,startpos,endpos)))")) - return _n + return nothing end n *= base n += d if i > endpos n *= sgn - return Nullable{T}(n) + return n end c, i = next(s,i) Unicode.isspace(c) && break @@ -134,7 +133,7 @@ function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos:: 'a' <= c <= 'z' ? c-'a'+a : base if d >= base raise && throw(ArgumentError("invalid base $base digit $(repr(c)) in $(repr(SubString(s,startpos,endpos)))")) - return _n + return nothing end (T <: Signed) && (d *= sgn) @@ -142,26 +141,26 @@ function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos:: n, ov_add = add_with_overflow(n, d) if ov_mul | ov_add raise && throw(OverflowError("overflow parsing $(repr(SubString(s,startpos,endpos)))")) - return _n + return nothing end - (i > endpos) && return Nullable{T}(n) + (i > endpos) && return n c, i = next(s,i) end while i <= endpos c, i = next(s,i) if !Unicode.isspace(c) raise && throw(ArgumentError("extra characters after whitespace in $(repr(SubString(s,startpos,endpos)))")) - return _n + return nothing end end - return Nullable{T}(n) + return n end function tryparse_internal(::Type{Bool}, sbuff::Union{String,SubString{String}}, startpos::Int, endpos::Int, base::Integer, raise::Bool) if isempty(sbuff) raise && throw(ArgumentError("input string is empty")) - return Nullable{Bool}() + return nothing end orig_start = startpos @@ -179,9 +178,9 @@ function tryparse_internal(::Type{Bool}, sbuff::Union{String,SubString{String}}, p = pointer(sbuff) + startpos - 1 @gc_preserve sbuff begin (len == 4) && (0 == ccall(:memcmp, Int32, (Ptr{UInt8}, Ptr{UInt8}, UInt), - p, "true", 4)) && (return Nullable(true)) + p, "true", 4)) && (return true) (len == 5) && (0 == ccall(:memcmp, Int32, (Ptr{UInt8}, Ptr{UInt8}, UInt), - p, "false", 5)) && (return Nullable(false)) + p, "false", 5)) && (return false) end if raise @@ -192,7 +191,7 @@ function tryparse_internal(::Type{Bool}, sbuff::Union{String,SubString{String}}, throw(ArgumentError("invalid Bool representation: $(repr(substr))")) end end - return Nullable{Bool}() + return nothing end @inline function check_valid_base(base) @@ -205,8 +204,8 @@ end """ tryparse(type, str, [base]) -Like [`parse`](@ref), but returns a [`Nullable`](@ref) of the requested type. The result -will be null if the string does not contain a valid number. +Like [`parse`](@ref), but returns either a value of the requested type, +or [`nothing`](@ref) if the string does not contain a valid number. """ tryparse(::Type{T}, s::AbstractString, base::Integer) where {T<:Integer} = tryparse_internal(T, s, start(s), endof(s), check_valid_base(base), false) @@ -214,30 +213,60 @@ tryparse(::Type{T}, s::AbstractString) where {T<:Integer} = tryparse_internal(T, s, start(s), endof(s), 0, false) function parse(::Type{T}, s::AbstractString, base::Integer) where T<:Integer - get(tryparse_internal(T, s, start(s), endof(s), check_valid_base(base), true)) + tryparse_internal(T, s, start(s), endof(s), check_valid_base(base), true) end function parse(::Type{T}, s::AbstractString) where T<:Integer - get(tryparse_internal(T, s, start(s), endof(s), 0, true)) # Zero means, "figure it out" + tryparse_internal(T, s, start(s), endof(s), 0, true) # Zero means, "figure it out" end ## string to float functions ## -tryparse(::Type{Float64}, s::String) = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Csize_t), s, 0, sizeof(s)) -tryparse(::Type{Float64}, s::SubString{String}) = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset, s.ncodeunits) -tryparse_internal(::Type{Float64}, s::String, startpos::Int, endpos::Int) = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Csize_t), s, startpos-1, endpos-startpos+1) -tryparse_internal(::Type{Float64}, s::SubString{String}, startpos::Int, endpos::Int) = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset+startpos-1, endpos-startpos+1) - -tryparse(::Type{Float32}, s::String) = ccall(:jl_try_substrtof, Nullable{Float32}, (Ptr{UInt8},Csize_t,Csize_t), s, 0, sizeof(s)) -tryparse(::Type{Float32}, s::SubString{String}) = ccall(:jl_try_substrtof, Nullable{Float32}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset, s.ncodeunits) -tryparse_internal(::Type{Float32}, s::String, startpos::Int, endpos::Int) = ccall(:jl_try_substrtof, Nullable{Float32}, (Ptr{UInt8},Csize_t,Csize_t), s, startpos-1, endpos-startpos+1) -tryparse_internal(::Type{Float32}, s::SubString{String}, startpos::Int, endpos::Int) = ccall(:jl_try_substrtof, Nullable{Float32}, (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset+startpos-1, endpos-startpos+1) - +function tryparse(::Type{Float64}, s::String) + hasvalue, val = ccall(:jl_try_substrtod, Tuple{Bool, Float64}, + (Ptr{UInt8},Csize_t,Csize_t), s, 0, sizeof(s)) + hasvalue ? val : nothing +end +function tryparse(::Type{Float64}, s::SubString{String}) + hasvalue, val = ccall(:jl_try_substrtod, Tuple{Bool, Float64}, + (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset, s.ncodeunits) + hasvalue ? val : nothing +end +function tryparse_internal(::Type{Float64}, s::String, startpos::Int, endpos::Int) + hasvalue, val = ccall(:jl_try_substrtod, Tuple{Bool, Float64}, + (Ptr{UInt8},Csize_t,Csize_t), s, startpos-1, endpos-startpos+1) + hasvalue ? val : nothing +end +function tryparse_internal(::Type{Float64}, s::SubString{String}, startpos::Int, endpos::Int) + hasvalue, val = ccall(:jl_try_substrtod, Tuple{Bool, Float64}, + (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset+startpos-1, endpos-startpos+1) + hasvalue ? val : nothing +end +function tryparse(::Type{Float32}, s::String) + hasvalue, val = ccall(:jl_try_substrtof, Tuple{Bool, Float32}, + (Ptr{UInt8},Csize_t,Csize_t), s, 0, sizeof(s)) + hasvalue ? val : nothing +end +function tryparse(::Type{Float32}, s::SubString{String}) + hasvalue, val = ccall(:jl_try_substrtof, Tuple{Bool, Float32}, + (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset, s.ncodeunits) + hasvalue ? val : nothing +end +function tryparse_internal(::Type{Float32}, s::String, startpos::Int, endpos::Int) + hasvalue, val = ccall(:jl_try_substrtof, Tuple{Bool, Float32}, + (Ptr{UInt8},Csize_t,Csize_t), s, startpos-1, endpos-startpos+1) + hasvalue ? val : nothing +end +function tryparse_internal(::Type{Float32}, s::SubString{String}, startpos::Int, endpos::Int) + hasvalue, val = ccall(:jl_try_substrtof, Tuple{Bool, Float32}, + (Ptr{UInt8},Csize_t,Csize_t), s.string, s.offset+startpos-1, endpos-startpos+1) + hasvalue ? val : nothing +end tryparse(::Type{T}, s::AbstractString) where {T<:Union{Float32,Float64}} = tryparse(T, String(s)) - -tryparse(::Type{Float16}, s::AbstractString) = convert(Nullable{Float16}, tryparse(Float32, s)) +tryparse(::Type{Float16}, s::AbstractString) = + convert(Union{Float16, Void}, tryparse(Float32, s)) tryparse_internal(::Type{Float16}, s::AbstractString, startpos::Int, endpos::Int) = - convert(Nullable{Float16}, tryparse_internal(Float32, s, startpos, endpos)) + convert(Union{Float16, Void}, tryparse_internal(Float32, s, startpos, endpos)) ## string to complex functions ## @@ -248,7 +277,7 @@ function tryparse_internal(::Type{Complex{T}}, s::Union{String,SubString{String} end if i > e raise && throw(ArgumentError("input string is empty or only contains whitespace")) - return Nullable{Complex{T}}() + return nothing end # find index of ± separating real/imaginary parts (if any) @@ -266,35 +295,34 @@ function tryparse_internal(::Type{Complex{T}}, s::Union{String,SubString{String} iᵢ -= 1 if s[iᵢ] != 'i' raise && throw(ArgumentError("expected trailing \"im\", found only \"m\"")) - return Nullable{Complex{T}}() + return nothing end end if i₊ == 0 # purely real or imaginary value if iᵢ > 0 # purely imaginary - x_ = tryparse_internal(T, s, i, iᵢ-1, raise) - isnull(x_) && return Nullable{Complex{T}}() - x = unsafe_get(x_) - return Nullable{Complex{T}}(Complex{T}(zero(x),x)) + x = tryparse_internal(T, s, i, iᵢ-1, raise) + x === nothing && return nothing + return Complex{T}(zero(x),x) else # purely real - return Nullable{Complex{T}}(tryparse_internal(T, s, i, e, raise)) + return Complex{T}(tryparse_internal(T, s, i, e, raise)) end end if iᵢ < i₊ raise && throw(ArgumentError("missing imaginary unit")) - return Nullable{Complex{T}}() # no imaginary part + return nothing # no imaginary part end # parse real part re = tryparse_internal(T, s, i, i₊-1, raise) - isnull(re) && return Nullable{Complex{T}}() + re === nothing && return nothing # parse imaginary part im = tryparse_internal(T, s, i₊+1, iᵢ-1, raise) - isnull(im) && return Nullable{Complex{T}}() + im === nothing && return nothing - return Nullable{Complex{T}}(Complex{T}(unsafe_get(re), s[i₊]=='-' ? -unsafe_get(im) : unsafe_get(im))) + return Complex{T}(re, s[i₊]=='-' ? -im : im) end # the ±1 indexing above for ascii chars is specific to String, so convert: @@ -306,7 +334,7 @@ tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int) wher startpos == start(s) && endpos == endof(s) ? tryparse(T, s) : tryparse(T, SubString(s, startpos, endpos)) function tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int, raise::Bool) where T<:Real result = tryparse_internal(T, s, startpos, endpos) - if raise && isnull(result) + if raise && result === nothing throw(ArgumentError("cannot parse $(repr(s[startpos:endpos])) as $T")) end return result @@ -315,4 +343,4 @@ tryparse_internal(::Type{T}, s::AbstractString, startpos::Int, endpos::Int, rais tryparse_internal(T, s, startpos, endpos, 10, raise) parse(::Type{T}, s::AbstractString) where T<:Union{Real,Complex} = - unsafe_get(tryparse_internal(T, s, start(s), endof(s), true)) + tryparse_internal(T, s, start(s), endof(s), true) diff --git a/base/pkg/entry.jl b/base/pkg/entry.jl index c4dbfc7a616ad..bbaaeaa334ed5 100644 --- a/base/pkg/entry.jl +++ b/base/pkg/entry.jl @@ -312,7 +312,7 @@ function pin(pkg::AbstractString, head::AbstractString) end ref = LibGit2.lookup_branch(repo, branch) try - if !isnull(ref) + if ref !== nothing if LibGit2.revparseid(repo, branch) != id throw(PkgError("Package $pkg: existing branch $branch has " * "been edited and doesn't correspond to its original commit")) @@ -320,17 +320,17 @@ function pin(pkg::AbstractString, head::AbstractString) @info "Package $pkg: checking out existing branch $branch" else @info "Creating $pkg branch $branch" - ref = Nullable(LibGit2.create_branch(repo, branch, commit)) + ref = LibGit2.create_branch(repo, branch, commit) end # checkout selected branch - with(LibGit2.peel(LibGit2.GitTree, get(ref))) do btree + with(LibGit2.peel(LibGit2.GitTree, ref)) do btree LibGit2.checkout_tree(repo, btree) end # switch head to the branch - LibGit2.head!(repo, get(ref)) + LibGit2.head!(repo, ref) finally - close(get(ref)) + close(ref) end finally close(commit) diff --git a/base/pkg/pkg.jl b/base/pkg/pkg.jl index 69634126d3427..cbf1230de71b1 100644 --- a/base/pkg/pkg.jl +++ b/base/pkg/pkg.jl @@ -22,13 +22,13 @@ const META_BRANCH = "metadata-v2" struct PkgError <: Exception msg::AbstractString - ex::Nullable{Exception} + ex::Union{Exception, Void} end -PkgError(msg::AbstractString) = PkgError(msg, Nullable{Exception}()) +PkgError(msg::AbstractString) = PkgError(msg, nothing) function Base.showerror(io::IO, pkgerr::PkgError) print(io, pkgerr.msg) - if !isnull(pkgerr.ex) - pkgex = get(pkgerr.ex) + if pkgerr.ex !== nothing + pkgex = pkgerr.ex if isa(pkgex, CompositeException) for cex in pkgex print(io, "\n=> ") diff --git a/base/pkg/read.jl b/base/pkg/read.jl index 804e1d4c4f346..eda9e6127390c 100644 --- a/base/pkg/read.jl +++ b/base/pkg/read.jl @@ -64,7 +64,7 @@ function isfixed(pkg::AbstractString, prepo::LibGit2.GitRepo, avail::Dict=availa LibGit2.isdirty(prepo) && return true LibGit2.isattached(prepo) && return true LibGit2.need_update(prepo) - if isnull(find("REQUIRE", LibGit2.GitIndex(prepo))) + if find("REQUIRE", LibGit2.GitIndex(prepo)) === nothing isfile(pkg,"REQUIRE") && return true end head = string(LibGit2.head_oid(prepo)) @@ -185,7 +185,7 @@ function requires_path(pkg::AbstractString, avail::Dict=available(pkg)) head = LibGit2.with(LibGit2.GitRepo, pkg) do repo LibGit2.isdirty(repo, "REQUIRE") && return pkgreq LibGit2.need_update(repo) - if isnull(find("REQUIRE", LibGit2.GitIndex(repo))) + if find("REQUIRE", LibGit2.GitIndex(repo)) === nothing isfile(pkgreq) && return pkgreq end string(LibGit2.head_oid(repo)) diff --git a/base/precompile.jl b/base/precompile.jl index 5d5bbea92f4a7..1be887564e958 100644 --- a/base/precompile.jl +++ b/base/precompile.jl @@ -1068,28 +1068,28 @@ precompile(Tuple{typeof(Base._jl_spawn), String, Array{String, 1}, Ptr{Void}, Ba precompile(Tuple{Type{Base.Timer}, Int64, Float64}) precompile(Tuple{typeof(Base.sleep), Int64}) precompile(Tuple{typeof(Base._uv_hook_close), Base.PipeEndpoint}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{IO}}}) -precompile(Tuple{Type{Base.Nullable{IO}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{AbstractString}}}) -precompile(Tuple{Type{Base.Nullable{AbstractString}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{Integer}}}) -precompile(Tuple{Type{Base.Nullable{Integer}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{Union{Int64, Symbol}}}}) -precompile(Tuple{Type{Base.Nullable{Union{Int64, Symbol}}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{Base.Cmd}}}) -precompile(Tuple{Type{Base.Nullable{Base.Cmd}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{Any}}}) -precompile(Tuple{Type{Base.Nullable{Any}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{Bool}}}) -precompile(Tuple{Type{Base.Nullable{Bool}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{Base.Process}}}) -precompile(Tuple{Type{Base.Nullable{Base.Process}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{Base.Dict{K, V} where V where K}}}) -precompile(Tuple{Type{Base.Nullable{Base.Dict{K, V} where V where K}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{Array{T, N} where N where T}}}) -precompile(Tuple{Type{Base.Nullable{Array{T, N} where N where T}}}) -precompile(Tuple{typeof(Base.convert), Type{Base.Nullable{Base.Process}}, Base.Process}) -precompile(Tuple{typeof(Base.convert), Type{Base.Nullable{Bool}}, Bool}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{IO, Void}}}) +precompile(Tuple{Type{Base.Union{IO, Void}}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{AbstractString, Void}}}) +precompile(Tuple{Type{Base.Union{AbstractString, Void}}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{Integer, Void}}}) +precompile(Tuple{Type{Base.Union{Integer, Void}}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{Int64, Symbol, Void}}}) +precompile(Tuple{Type{Base.Union{Int64, Symbol, Void}}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{Base.Cmd, Void}}}) +precompile(Tuple{Type{Base.Union{Base.Cmd, Void}}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{Some{Any}, Void}}}) +precompile(Tuple{Type{Base.Union{Some{Any}, Void}}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{Bool, Void}}}) +precompile(Tuple{Type{Base.Union{Bool, Void}}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{Base.Process, Void}}}) +precompile(Tuple{Type{Base.Union{Base.Process, Void}}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{Base.Dict{K, V}, Void} where V where K}}) +precompile(Tuple{Type{Base.Union{Base.Dict{K, V}, Void} where V where K}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{Array{T, N}, Void} where N where T}}) +precompile(Tuple{Type{Base.Union{Array{T, N}, Void} where N where T}}) +precompile(Tuple{typeof(Base.convert), Type{Base.Union{Base.Process, Void}}, Base.Process}) +precompile(Tuple{typeof(Base.convert), Type{Base.Union{Bool, Void}}, Bool}) precompile(Tuple{typeof(Base.task_done_hook), Task}) precompile(Tuple{typeof(Base.getindex), Tuple{Array{Any, 1}, Tuple{}}, Int64}) precompile(Tuple{Type{Base.TCPSocket}}) @@ -1175,7 +1175,7 @@ precompile(Tuple{typeof(Base._uv_hook_close), Base.Timer}) precompile(Tuple{typeof(Base.notify), Base.Condition, Base.EOFError, Bool, Bool}) precompile(Tuple{typeof(Base.unpreserve_handle), Base.Timer}) precompile(Tuple{typeof(Base.disassociate_julia_struct), Base.Timer}) -precompile(Tuple{typeof(Base.convert), Type{Base.Nullable{AbstractString}}, Base.SubString{String}}) +precompile(Tuple{typeof(Base.convert), Type{Base.Union{AbstractString, Void}}, Base.SubString{String}}) precompile(Tuple{typeof(Base.connect!), Base.TCPSocket, Base.SubString{String}, UInt16}) precompile(Tuple{typeof(Base.notify), Base.Condition, Base.IPv4, Bool, Bool}) precompile(Tuple{typeof(Base.schedule), Task, Base.IPv4}) @@ -1184,7 +1184,7 @@ precompile(Tuple{typeof(Base.uv_status_string), Base.TCPSocket}) precompile(Tuple{typeof(Base._fd), Base.TCPSocket}) precompile(Tuple{typeof(Base.print), Base.GenericIOBuffer{Array{UInt8, 1}}, Base.TCPSocket}) precompile(Tuple{typeof(Base.isopen), Base.TCPSocket}) -precompile(Tuple{typeof(Base.convert), Type{Base.Nullable{AbstractString}}, String}) +precompile(Tuple{typeof(Base.convert), Type{Base.Union{AbstractString, Void}}, String}) precompile(Tuple{typeof(Base.unpreserve_handle), Base.TCPSocket}) precompile(Tuple{typeof(Base.check_open), Base.TCPSocket}) precompile(Tuple{typeof(Base.stream_wait), Base.TCPSocket, Base.Condition}) @@ -1201,7 +1201,7 @@ precompile(Tuple{typeof(Base.unsafe_read), Base.TCPSocket, Ptr{UInt8}, UInt64}) precompile(Tuple{typeof(Base.read!), Base.TCPSocket, Array{Int64, 1}}) precompile(Tuple{typeof(Base.read), Base.TCPSocket, Type{UInt8}}) precompile(Tuple{typeof(Base.convert), Type{IO}, Base.TCPSocket}) -precompile(Tuple{typeof(Base.convert), Type{Base.Nullable{Base.VersionNumber}}, Base.Nullable{Base.VersionNumber}}) +precompile(Tuple{typeof(Base.convert), Type{Base.Union{Base.VersionNumber, Void}}, Base.Union{Base.VersionNumber, Void}}) precompile(Tuple{typeof(Base.join), Base.GenericIOBuffer{Array{UInt8, 1}}, Tuple{Int64}, Char}) precompile(Tuple{typeof(Base.close), Base.TCPSocket}) precompile(Tuple{typeof(Base.convert), Type{Base.AbstractChannel}, Base.Channel{Any}}) @@ -1270,28 +1270,28 @@ precompile(Tuple{typeof(Base.setindex!), Base.Dict{Int64, Void}, Void, Int64}) precompile(Tuple{Type{Array{Union{Tuple{Any, Int64}, Tuple{Tuple{}, Any, Bool}}, 1}}, Tuple{Int64}}) precompile(Tuple{typeof(Base.eachindex), Array{Union{Tuple{Any, Int64}, Tuple{Tuple{}, Any, Bool}}, 1}}) precompile(Tuple{typeof(Base.LinAlg.BLAS.set_num_threads), Int64}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{IO}}}) -precompile(Tuple{Type{Base.Nullable{IO}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{AbstractString}}}) -precompile(Tuple{Type{Base.Nullable{AbstractString}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{Integer}}}) -precompile(Tuple{Type{Base.Nullable{Integer}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{Union{Int64, Symbol}}}}) -precompile(Tuple{Type{Base.Nullable{Union{Int64, Symbol}}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{Base.Cmd}}}) -precompile(Tuple{Type{Base.Nullable{Base.Cmd}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{Any}}}) -precompile(Tuple{Type{Base.Nullable{Any}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{Bool}}}) -precompile(Tuple{Type{Base.Nullable{Bool}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{Base.Process}}}) -precompile(Tuple{Type{Base.Nullable{Base.Process}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{Base.Dict{K, V} where V where K}}}) -precompile(Tuple{Type{Base.Nullable{Base.Dict{K, V} where V where K}}}) -precompile(Tuple{typeof(Base.eltype), Type{Base.Nullable{Array{T, N} where N where T}}}) -precompile(Tuple{Type{Base.Nullable{Array{T, N} where N where T}}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{IO, Void}}}) +precompile(Tuple{Type{Base.Union{IO, Void}}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{AbstractString, Void}}}) +precompile(Tuple{Type{Base.Union{AbstractString, Void}}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{Integer, Void}}}) +precompile(Tuple{Type{Base.Union{Integer, Void}}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{Some{Union{Int64, Symbol}}, Void}}}) +precompile(Tuple{Type{Base.Union{Int64, Symbol, Void}}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{Base.Cmd, Void}}}) +precompile(Tuple{Type{Base.Union{Base.Cmd, Void}}}) +precompile(Tuple{typeof(Base.eltype), Type{Any}}) +precompile(Tuple{Type{Any}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{Bool, Void}}}) +precompile(Tuple{Type{Base.Union{Bool, Void}}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{Base.Process, Void}}}) +precompile(Tuple{Type{Base.Union{Base.Process, Void}}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{Base.Dict{K, V}, Void} where V where K}}) +precompile(Tuple{Type{Base.Union{Base.Dict{K, V}, Void} where V where K}}) +precompile(Tuple{typeof(Base.eltype), Type{Base.Union{Array{T, N}, Void} where N where T}}) +precompile(Tuple{Type{Base.Union{Array{T, N}, Void} where N where T}}) precompile(Tuple{typeof(Base.convert), Type{IO}, Base.TCPSocket}) -precompile(Tuple{typeof(Base.convert), Type{Base.Nullable{Base.VersionNumber}}, Base.VersionNumber}) +precompile(Tuple{typeof(Base.convert), Type{Base.Union{Base.VersionNumber, Void}}, Base.VersionNumber}) precompile(Tuple{typeof(Base.unsafe_write), Base.TCPSocket, Ptr{UInt8}, UInt64}) precompile(Tuple{typeof(Base.uv_write), Base.TCPSocket, Ptr{UInt8}, UInt64}) precompile(Tuple{typeof(Base.flush), Base.TCPSocket}) @@ -1313,12 +1313,12 @@ precompile(Tuple{typeof(Base.ht_keyindex2!), Base.Dict{WeakRef, Void}, WeakRef}) precompile(Tuple{typeof(Base._setindex!), Base.Dict{WeakRef, Void}, Void, WeakRef, Int64}) precompile(Tuple{typeof(Base.setindex!), Base.Dict{WeakRef, Void}, Void, WeakRef}) precompile(Tuple{Type{Base.Channel{Int64}}, Int64}) -precompile(Tuple{typeof(Base.get), Base.Nullable{Base.Dict{K, V} where V where K}, Base.Dict{Any, Any}}) +precompile(Tuple{typeof(Base.get), Base.Union{Base.Dict{K, V}, Void} where V where K, Base.Dict{Any, Any}}) precompile(Tuple{typeof(Base.setindex!), Base.Dict{Any, Any}, Int64, Symbol}) precompile(Tuple{typeof(Base._setindex!), Base.Dict{Any, Any}, Int64, Symbol, Int64}) -precompile(Tuple{typeof(Base.convert), Type{Base.Nullable{Base.Dict{K, V} where V where K}}, Base.Dict{Any, Any}}) -precompile(Tuple{typeof(Base.convert), Type{Base.Nullable{Integer}}, Int64}) -precompile(Tuple{typeof(Base.convert), Type{Base.Nullable{Base.VersionNumber}}, Base.VersionNumber}) +precompile(Tuple{typeof(Base.convert), Type{Base.Union{Base.Dict{K, V}, Void} where V where K}, Base.Dict{Any, Any}}) +precompile(Tuple{typeof(Base.convert), Type{Base.Union{Integer, Void}}, Int64}) +precompile(Tuple{typeof(Base.convert), Type{Base.Union{Base.VersionNumber, Void}}, Base.VersionNumber}) precompile(Tuple{typeof(Base.put!), Base.Channel{Any}, Int64}) precompile(Tuple{typeof(Base.put_buffered), Base.Channel{Any}, Int64}) precompile(Tuple{typeof(Base.put_unbuffered), Base.Channel{Any}, Int64}) diff --git a/base/process.jl b/base/process.jl index acf983d8075cf..2534b6d2e923d 100644 --- a/base/process.jl +++ b/base/process.jl @@ -391,7 +391,7 @@ function _uv_hook_close(proc::Process) notify(proc.closenotify) end -function spawn(redirect::CmdRedirect, stdios::StdIOSet; chain::Nullable{ProcessChain}=Nullable{ProcessChain}()) +function spawn(redirect::CmdRedirect, stdios::StdIOSet; chain::Union{ProcessChain, Void}=nothing) spawn(redirect.cmd, (redirect.stream_no == STDIN_NO ? redirect.handle : stdios[1], redirect.stream_no == STDOUT_NO ? redirect.handle : stdios[2], @@ -399,12 +399,12 @@ function spawn(redirect::CmdRedirect, stdios::StdIOSet; chain::Nullable{ProcessC chain=chain) end -function spawn(cmds::OrCmds, stdios::StdIOSet; chain::Nullable{ProcessChain}=Nullable{ProcessChain}()) +function spawn(cmds::OrCmds, stdios::StdIOSet; chain::Union{ProcessChain, Void}=nothing) out_pipe = Libc.malloc(_sizeof_uv_named_pipe) in_pipe = Libc.malloc(_sizeof_uv_named_pipe) link_pipe(in_pipe, false, out_pipe, false) - if isnull(chain) - chain = Nullable(ProcessChain(stdios)) + if chain === nothing + chain = ProcessChain(stdios) end try spawn(cmds.a, (stdios[1], out_pipe, stdios[3]), chain=chain) @@ -415,15 +415,15 @@ function spawn(cmds::OrCmds, stdios::StdIOSet; chain::Nullable{ProcessChain}=Nul Libc.free(out_pipe) Libc.free(in_pipe) end - get(chain) + chain end -function spawn(cmds::ErrOrCmds, stdios::StdIOSet; chain::Nullable{ProcessChain}=Nullable{ProcessChain}()) +function spawn(cmds::ErrOrCmds, stdios::StdIOSet; chain::Union{ProcessChain, Void}=nothing) out_pipe = Libc.malloc(_sizeof_uv_named_pipe) in_pipe = Libc.malloc(_sizeof_uv_named_pipe) link_pipe(in_pipe, false, out_pipe, false) - if isnull(chain) - chain = Nullable(ProcessChain(stdios)) + if chain === nothing + chain = ProcessChain(stdios) end try spawn(cmds.a, (stdios[1], stdios[2], out_pipe), chain=chain) @@ -434,7 +434,7 @@ function spawn(cmds::ErrOrCmds, stdios::StdIOSet; chain::Nullable{ProcessChain}= Libc.free(out_pipe) Libc.free(in_pipe) end - get(chain) + chain end function setup_stdio(stdio::PipeEndpoint, readable::Bool) @@ -505,7 +505,7 @@ function setup_stdio(anon::Function, stdio::StdIOSet) close_err && close_stdio(err) end -function spawn(cmd::Cmd, stdios::StdIOSet; chain::Nullable{ProcessChain}=Nullable{ProcessChain}()) +function spawn(cmd::Cmd, stdios::StdIOSet; chain::Union{ProcessChain, Void}=nothing) if isempty(cmd.exec) throw(ArgumentError("cannot spawn empty command")) end @@ -515,21 +515,21 @@ function spawn(cmd::Cmd, stdios::StdIOSet; chain::Nullable{ProcessChain}=Nullabl pp.handle = _jl_spawn(cmd.exec[1], cmd.exec, loop, pp, in, out, err) end - if !isnull(chain) - push!(get(chain).processes, pp) + if chain !== nothing + push!(chain.processes, pp) end pp end -function spawn(cmds::AndCmds, stdios::StdIOSet; chain::Nullable{ProcessChain}=Nullable{ProcessChain}()) - if isnull(chain) - chain = Nullable(ProcessChain(stdios)) +function spawn(cmds::AndCmds, stdios::StdIOSet; chain::Union{ProcessChain, Void}=nothing) + if chain === nothing + chain = ProcessChain(stdios) end setup_stdio(stdios) do in, out, err spawn(cmds.a, (in,out,err), chain=chain) spawn(cmds.b, (in,out,err), chain=chain) end - get(chain) + chain end # INTERNAL @@ -555,7 +555,7 @@ spawn_opts_inherit(in::Redirectable=RawFD(0), out::Redirectable=RawFD(1), err::R Run a command object asynchronously, returning the resulting `Process` object. """ -spawn(cmds::AbstractCmd, args...; chain::Nullable{ProcessChain}=Nullable{ProcessChain}()) = +spawn(cmds::AbstractCmd, args...; chain::Union{ProcessChain, Void}=nothing) = spawn(cmds, spawn_opts_swallow(args...)...; chain=chain) function eachline(cmd::AbstractCmd; chomp::Bool=true) diff --git a/base/repl/REPL.jl b/base/repl/REPL.jl index b72ae82ba1f99..a3175eb35d901 100644 --- a/base/repl/REPL.jl +++ b/base/repl/REPL.jl @@ -980,8 +980,7 @@ function setup_interface( linfos = Base.LAST_SHOWN_LINE_INFOS str = String(take!(LineEdit.buffer(s))) n = tryparse(Int, str) - isnull(n) && @goto writeback - n = get(n) + n === nothing && @goto writeback if n <= 0 || n > length(linfos) || startswith(linfos[n][1], "./REPL") @goto writeback end diff --git a/base/repl/REPLCompletions.jl b/base/repl/REPLCompletions.jl index 7eb796261d2ce..345c5a9a8c582 100644 --- a/base/repl/REPLCompletions.jl +++ b/base/repl/REPLCompletions.jl @@ -372,7 +372,7 @@ function complete_methods(ex_org::Expr) t_in = Tuple{Core.Typeof(func), args_ex...} # Input types na = length(args_ex)+1 ml = methods(func) - kwtype = isdefined(ml.mt, :kwsorter) ? Nullable{DataType}(typeof(ml.mt.kwsorter)) : Nullable{DataType}() + kwtype = isdefined(ml.mt, :kwsorter) ? typeof(ml.mt.kwsorter) : nothing io = IOBuffer() for method in ml ms = method.sig diff --git a/base/socket.jl b/base/socket.jl index a0298ec1f05dc..7c1053944a07b 100644 --- a/base/socket.jl +++ b/base/socket.jl @@ -262,7 +262,7 @@ mutable struct TCPSocket <: LibuvStream readnotify::Condition connectnotify::Condition closenotify::Condition - sendbuf::Nullable{IOBuffer} + sendbuf::Union{IOBuffer, Void} lock::ReentrantLock throttle::Int diff --git a/base/some.jl b/base/some.jl new file mode 100644 index 0000000000000..3229283e66e18 --- /dev/null +++ b/base/some.jl @@ -0,0 +1,80 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +""" + Some{T} + +A wrapper type used in `Union{Some{T}, Void}` to distinguish between the absence +of a value ([`nothing`](@ref)) and the presence of a `nothing` value (i.e. `Some(nothing)`). + +Use [`coalesce`](@ref) to access the value wrapped by a `Some` object. +""" +struct Some{T} + value::T +end + +promote_rule(::Type{Some{S}}, ::Type{Some{T}}) where {S,T} = Some{promote_type(S, T)} +promote_rule(::Type{Some{T}}, ::Type{Void}) where {T} = Union{Some{T}, Void} + +convert(::Type{Some{T}}, x::Some) where {T} = Some{T}(convert(T, x.value)) +convert(::Type{Union{Some{T}, Void}}, x::Some) where {T} = convert(Some{T}, x) + +convert(::Type{Union{T, Void}}, x::Any) where {T} = convert(T, x) +convert(::Type{Void}, x::Any) = throw(MethodError(convert, (Void, x))) +convert(::Type{Void}, x::Void) = nothing + +function show(io::IO, x::Some) + if get(io, :typeinfo, Any) == typeof(x) + show(io, x.value) + else + print(io, "Some(") + show(io, x.value) + print(io, ')') + end +end + +""" + coalesce(x, y...) + +Return the first value in the arguments which is not equal to +either [`nothing`](@ref) or [`missing`](@ref), or the last argument. +Unwrap arguments of type [`Some`](@ref). + +# Examples + +```jldoctest +julia> coalesce(nothing, 1) +1 + +julia> coalesce(missing, 1) +1 + +julia> coalesce(1, nothing) +1 + +julia> coalesce(nothing, nothing) +nothing + +julia> coalesce(Some(1)) +1 + +julia> coalesce(nothing, Some(1)) +1 +``` +""" +function coalesce end + +coalesce(x::Any) = x +coalesce(x::Some) = x.value +coalesce(x::Void) = nothing +coalesce(x::Missing) = missing +coalesce(x::Any, y...) = x +coalesce(x::Some, y...) = x.value +coalesce(x::Union{Void, Missing}, y...) = coalesce(y...) + +""" + notnothing(x) + +Throw an error if `x == nothing`, and return `x` if not. +""" +notnothing(x::Any) = x +notnothing(::Void) = throw(ArgumentError("nothing passed to notnothing")) \ No newline at end of file diff --git a/base/statistics.jl b/base/statistics.jl index 91a9660b451e2..593f7c3743053 100644 --- a/base/statistics.jl +++ b/base/statistics.jl @@ -198,7 +198,7 @@ varm(A::AbstractArray{T}, m::AbstractArray, region; corrected::Bool=true) where var(A::AbstractArray{T}; corrected::Bool=true, mean=nothing) where {T} = - real(varm(A, mean === nothing ? Base.mean(A) : mean; corrected=corrected)) + real(varm(A, coalesce(mean, Base.mean(A)); corrected=corrected)) """ var(v[, region]; corrected::Bool=true, mean=nothing) @@ -217,7 +217,7 @@ The mean `mean` over the region may be provided. `DataArrays.jl` package is recommended. """ var(A::AbstractArray, region; corrected::Bool=true, mean=nothing) = - varm(A, mean === nothing ? Base.mean(A, region) : mean, region; corrected=corrected) + varm(A, coalesce(mean, Base.mean(A, region)), region; corrected=corrected) varm(iterable, m; corrected::Bool=true) = var(iterable, corrected=corrected, mean=m) diff --git a/base/stream.jl b/base/stream.jl index e60d5386ce7a5..36316e0688027 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -104,7 +104,7 @@ mutable struct PipeEndpoint <: LibuvStream readnotify::Condition connectnotify::Condition closenotify::Condition - sendbuf::Nullable{IOBuffer} + sendbuf::Union{IOBuffer, Void} lock::ReentrantLock throttle::Int @@ -154,7 +154,7 @@ mutable struct TTY <: LibuvStream buffer::IOBuffer readnotify::Condition closenotify::Condition - sendbuf::Nullable{IOBuffer} + sendbuf::Union{IOBuffer, Void} lock::ReentrantLock throttle::Int @static if Sys.iswindows(); ispty::Bool; end @@ -166,7 +166,8 @@ mutable struct TTY <: LibuvStream PipeBuffer(), Condition(), Condition(), - nothing, ReentrantLock(), + nothing, + ReentrantLock(), DEFAULT_READ_BUFFER_SZ) associate_julia_struct(handle, tty) finalizer(uvfinalize, tty) @@ -847,11 +848,11 @@ end # - large isbits arrays are unbuffered and written directly function unsafe_write(s::LibuvStream, p::Ptr{UInt8}, n::UInt) - if isnull(s.sendbuf) + if s.sendbuf === nothing return uv_write(s, p, UInt(n)) end - buf = get(s.sendbuf) + buf = s.sendbuf totb = nb_available(buf) + n if totb < buf.maxsize nb = unsafe_write(buf, p, n) @@ -867,10 +868,10 @@ function unsafe_write(s::LibuvStream, p::Ptr{UInt8}, n::UInt) end function flush(s::LibuvStream) - if isnull(s.sendbuf) + if s.sendbuf === nothing return end - buf = get(s.sendbuf) + buf = s.sendbuf if nb_available(buf) > 0 arr = take!(buf) # Array of UInt8s uv_write(s, arr) @@ -883,8 +884,8 @@ buffer_writes(s::LibuvStream, bufsize) = (s.sendbuf=PipeBuffer(bufsize); s) ## low-level calls to libuv ## function write(s::LibuvStream, b::UInt8) - if !isnull(s.sendbuf) - buf = get(s.sendbuf) + if s.sendbuf !== nothing + buf = s.sendbuf if nb_available(buf) + 1 < buf.maxsize return write(buf, b) end diff --git a/base/sysimg.jl b/base/sysimg.jl index fe9676f4f41f9..fbe2a65e30b22 100644 --- a/base/sysimg.jl +++ b/base/sysimg.jl @@ -85,7 +85,6 @@ end include("essentials.jl") include("ctypes.jl") include("gcutils.jl") -include("nullabletype.jl") include("generator.jl") include("reflection.jl") include("options.jl") @@ -242,8 +241,8 @@ include("multidimensional.jl") include("permuteddimsarray.jl") using .PermutedDimsArrays -# nullable types -include("nullable.jl") +# Some type +include("some.jl") include("broadcast.jl") using .Broadcast diff --git a/base/threadcall.jl b/base/threadcall.jl index b058018068f17..fcf258df0f4e9 100644 --- a/base/threadcall.jl +++ b/base/threadcall.jl @@ -1,12 +1,12 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license const max_ccall_threads = parse(Int, get(ENV, "UV_THREADPOOL_SIZE", "4")) -const thread_notifiers = [Nullable{Condition}() for i in 1:max_ccall_threads] +const thread_notifiers = Union{Condition, Void}[nothing for i in 1:max_ccall_threads] const threadcall_restrictor = Semaphore(max_ccall_threads) function notify_fun(idx) global thread_notifiers - notify(get(thread_notifiers[idx])) + notify(thread_notifiers[idx]) return end @@ -83,8 +83,8 @@ function do_threadcall(wrapper::Function, rettype::Type, argtypes::Vector, argva # wait for a worker thread to be available acquire(threadcall_restrictor) - idx = findfirst(isnull, thread_notifiers) - thread_notifiers[idx] = Nullable{Condition}(Condition()) + idx = findfirst(equalto(nothing), thread_notifiers) + thread_notifiers[idx] = Condition() # queue up the work to be done ccall(:jl_queue_work, Void, @@ -92,8 +92,8 @@ function do_threadcall(wrapper::Function, rettype::Type, argtypes::Vector, argva fun_ptr, args_arr, ret_arr, c_notify_fun, idx) # wait for a result & return it - wait(get(thread_notifiers[idx])) - thread_notifiers[idx] = Nullable{Condition}() + wait(thread_notifiers[idx]) + thread_notifiers[idx] = nothing release(threadcall_restrictor) unsafe_load(convert(Ptr{rettype}, pointer(ret_arr))) diff --git a/base/util.jl b/base/util.jl index 9a600fb6506c7..a57683cde5eeb 100644 --- a/base/util.jl +++ b/base/util.jl @@ -426,7 +426,7 @@ getpass(prompt::AbstractString) = unsafe_string(ccall(:getpass, Cstring, (Cstrin end """ - prompt(message; default="", password=false) -> Nullable{String} + prompt(message; default="", password=false) -> Union{String, Void} Displays the `message` then waits for user input. Input is terminated when a newline (\\n) is encountered or EOF (^D) character is entered on a blank line. If a `default` is provided @@ -445,10 +445,10 @@ function prompt(message::AbstractString; default::AbstractString="", password::B else print(msg) uinput = readline(chomp=false) - isempty(uinput) && return Nullable{String}() # Encountered an EOF + isempty(uinput) && return nothing # Encountered an EOF uinput = chomp(uinput) end - Nullable{String}(isempty(uinput) ? default : uinput) + isempty(uinput) ? default : uinput end # Windows authentication prompt @@ -501,9 +501,9 @@ if Sys.iswindows() outbuf_data, outbuf_size, pfSave, dwflags) # 2.3: If that failed for any reason other than the user canceling, error out. - # If the user canceled, just return a nullable + # If the user canceled, just return nothing if code == ERROR_CANCELLED - return Nullable{Tuple{String,String}}() + return nothing elseif code != ERROR_SUCCESS error(Base.Libc.FormatMessage(code)) end @@ -529,8 +529,8 @@ if Sys.iswindows() # Done. passbuf_ = passbuf[1:passlen[]-1] - result = Nullable((String(transcode(UInt8, usernamebuf[1:usernamelen[]-1])), - String(transcode(UInt8, passbuf_)))) + result = (String(transcode(UInt8, usernamebuf[1:usernamelen[]-1])), + String(transcode(UInt8, passbuf_))) securezero!(passbuf_) securezero!(passbuf) diff --git a/doc/src/manual/faq.md b/doc/src/manual/faq.md index 119777b343476..937d037cd450c 100644 --- a/doc/src/manual/faq.md +++ b/doc/src/manual/faq.md @@ -615,10 +615,10 @@ all/many future usages of the other functions in module Foo that depend on calli ### How does "null" or "nothingness" work in Julia? -Unlike many languages (for example, C and Java), Julia does not have a "null" value. When a reference -(variable, object field, or array element) is uninitialized, accessing it will immediately throw -an error. This situation can be detected using the [`isdefined`](@ref) or -[`isassigned`](@ref Base.isassigned) functions. +Unlike many languages (for example, C and Java), Julia objects cannot be "null" by default. +When a reference (variable, object field, or array element) is uninitialized, accessing it +will immediately throw an error. This situation can be detected using the +[`isdefined`](@ref) or [`isassigned`](@ref Base.isassigned) functions. Some functions are used only for their side effects, and do not need to return a value. In these cases, the convention is to return the value `nothing`, which is just a singleton object of type @@ -626,16 +626,20 @@ cases, the convention is to return the value `nothing`, which is just a singleto this convention, and that the REPL does not print anything for it. Some language constructs that would not otherwise have a value also yield `nothing`, for example `if false; end`. +For situations where a value `x` of type `T` exists only sometimes, the `Union{T, Void}` +type can be used. If the value itself can be `nothing` (notably, when `T` is `Any`), +the `Union{Some{T}, Void}` type is more appropriate since `x == nothing` then indicates +the absence of a value, and `x == Some(nothing)` indicates the presence of a value equal +to `nothing`. + To represent missing data in the statistical sense (`NA` in R or `NULL` in SQL), use the [`missing`](@ref) object. See the [`Missing Values`](@ref missing) section for more details. The empty tuple (`()`) is another form of nothingness. But, it should not really be thought of as nothing but rather a tuple of zero values. -In code written for Julia prior to version 0.4 you may occasionally see `None`, which is quite -different. It is the empty (or "bottom") type, a type with no values and no subtypes (except itself). -This is now written as `Union{}` (an empty union type). You will generally not need to use this -type. +The empty (or "bottom") type, written as `Union{}` (an empty union type), is a type with +no values and no subtypes (except itself). You will generally not need to use this type. ## Memory @@ -713,7 +717,7 @@ You can lock your writes with a `ReentrantLock` like this: ```jldoctest julia> l = ReentrantLock() -ReentrantLock(Nullable{Task}(), Condition(Any[]), 0) +ReentrantLock(nothing, Condition(Any[]), 0) julia> @sync for i in 1:3 @async begin diff --git a/doc/src/manual/parallel-computing.md b/doc/src/manual/parallel-computing.md index 277e33459f236..f27289350f34e 100644 --- a/doc/src/manual/parallel-computing.md +++ b/doc/src/manual/parallel-computing.md @@ -57,10 +57,10 @@ argument implicitly loads module `Distributed`. $ ./julia -p 2 julia> r = remotecall(rand, 2, 2, 2) -Future(2, 1, 4, Nullable{Any}()) +Future(2, 1, 4, nothing) julia> s = @spawnat 2 1 .+ fetch(r) -Future(2, 1, 5, Nullable{Any}()) +Future(2, 1, 5, nothing) julia> fetch(s) 2×2 Array{Float64,2}: @@ -98,10 +98,10 @@ the operation for you: ```julia-repl julia> r = @spawn rand(2,2) -Future(2, 1, 4, Nullable{Any}()) +Future(2, 1, 4, nothing) julia> s = @spawn 1 .+ fetch(r) -Future(3, 1, 5, Nullable{Any}()) +Future(3, 1, 5, nothing) julia> fetch(s) 2×2 Array{Float64,2}: @@ -374,10 +374,10 @@ trials on two machines, and add together the results: julia> @everywhere include_string(Main, $(read("count_heads.jl", String)), "count_heads.jl") julia> a = @spawn count_heads(100000000) -Future(2, 1, 6, Nullable{Any}()) +Future(2, 1, 6, nothing) julia> b = @spawn count_heads(100000000) -Future(3, 1, 7, Nullable{Any}()) +Future(3, 1, 7, nothing) julia> fetch(a)+fetch(b) 100001564 @@ -1164,26 +1164,27 @@ appropriate fields initialized) to `launched` ```julia mutable struct WorkerConfig # Common fields relevant to all cluster managers - io::Nullable{IO} - host::Nullable{AbstractString} - port::Nullable{Integer} + io::Union{IO, Void} + host::Union{AbstractString, Void} + port::Union{Integer, Void} # Used when launching additional workers at a host - count::Nullable{Union{Int, Symbol}} - exename::Nullable{AbstractString} - exeflags::Nullable{Cmd} + count::Union{Int, Symbol, Void} + exename::Union{AbstractString, Cmd, Void} + exeflags::Union{Cmd, Void} # External cluster managers can use this to store information at a per-worker level # Can be a dict if multiple fields need to be stored. - userdata::Nullable{Any} + userdata::Any # SSHManager / SSH tunnel connections to workers - tunnel::Nullable{Bool} - bind_addr::Nullable{AbstractString} - sshflags::Nullable{Cmd} - max_parallel::Nullable{Integer} + tunnel::Union{Bool, Void} + bind_addr::Union{AbstractString, Void} + sshflags::Union{Cmd, Void} + max_parallel::Union{Integer, Void} - connect_at::Nullable{Any} + # Used by Local/SSH managers + connect_at::Any [...] end diff --git a/doc/src/manual/stacktraces.md b/doc/src/manual/stacktraces.md index 6d2a06b06ccda..bffbfdc20e6c6 100644 --- a/doc/src/manual/stacktraces.md +++ b/doc/src/manual/stacktraces.md @@ -73,7 +73,7 @@ returned by [`backtrace`](@ref): ```julia-repl julia> top_frame = stacktrace()[1] -eval(::Module, ::Any) at boot.jl:236 +eval(::Module, ::Expr) at REPL.jl:3 julia> top_frame.func :eval @@ -85,7 +85,7 @@ julia> top_frame.line 236 julia> top_frame.linfo -Nullable{Core.MethodInstance}(MethodInstance for eval(::Module, ::Any)) +MethodInstance for eval(::Module, ::Expr) julia> top_frame.inlined false diff --git a/doc/src/manual/style-guide.md b/doc/src/manual/style-guide.md index 6135d734acf21..9108bf3ef4d30 100644 --- a/doc/src/manual/style-guide.md +++ b/doc/src/manual/style-guide.md @@ -139,8 +139,6 @@ some alternatives to consider: * Determine whether there is a simple rule for when `x` is `nothing`. For example, often the field will start as `nothing` but get initialized at some well-defined point. In that case, consider leaving it undefined at first. - * If `x` really needs to hold no value at some times, define it as `::Nullable{T}` instead, as this - guarantees type-stability in the code accessing this field (see [Nullable types](@ref man-nullable-types)). ## Avoid elaborate container types diff --git a/doc/src/manual/types.md b/doc/src/manual/types.md index 9eacec5057752..21b167165da8f 100644 --- a/doc/src/manual/types.md +++ b/doc/src/manual/types.md @@ -1373,190 +1373,3 @@ It's worth noting that it's extremely easy to mis-use parametric "value" types, in unfavorable cases, you can easily end up making the performance of your code much *worse*. In particular, you would never want to write actual code as illustrated above. For more information about the proper (and improper) uses of `Val`, please read the more extensive discussion in [the performance tips](@ref man-performance-tips). - -## [Nullable Types: Representing Missing Values](@id man-nullable-types) - -In many settings, you need to interact with a value of type `T` that may or may not exist. To -handle these settings, Julia provides a parametric type called [`Nullable{T}`](@ref), which can be thought -of as a specialized container type that can contain either zero or one values. `Nullable{T}` provides -a minimal interface designed to ensure that interactions with missing values are safe. At present, -the interface consists of several possible interactions: - - * Construct a `Nullable` object. - * Check if a `Nullable` object has a missing value. - * Access the value of a `Nullable` object with a guarantee that a [`NullException`](@ref) - will be thrown if the object's value is missing. - * Access the value of a `Nullable` object with a guarantee that a default value of type - `T` will be returned if the object's value is missing. - * Perform an operation on the value (if it exists) of a `Nullable` object, getting a - `Nullable` result. The result will be missing if the original value was missing. - * Performing a test on the value (if it exists) of a `Nullable` - object, getting a result that is missing if either the `Nullable` - itself was missing, or the test failed. - * Perform general operations on single `Nullable` objects, propagating the missing data. - -### Constructing [`Nullable`](@ref) objects - -To construct an object representing a missing value of type `T`, use the `Nullable{T}()` function: - -```jldoctest -julia> x1 = Nullable{Int64}() -Nullable{Int64}() - -julia> x2 = Nullable{Float64}() -Nullable{Float64}() - -julia> x3 = Nullable{Vector{Int64}}() -Nullable{Array{Int64,1}}() -``` - -To construct an object representing a non-missing value of type `T`, use the `Nullable(x::T)` -function: - -```jldoctest -julia> x1 = Nullable(1) -Nullable{Int64}(1) - -julia> x2 = Nullable(1.0) -Nullable{Float64}(1.0) - -julia> x3 = Nullable([1, 2, 3]) -Nullable{Array{Int64,1}}([1, 2, 3]) -``` - -Note the core distinction between these two ways of constructing a `Nullable` object: -in one style, you provide a type, `T`, as a function parameter; in the other style, you provide -a single value of type `T` as an argument. - -### Checking if a `Nullable` object has a value - -You can check if a `Nullable` object has any value using [`isnull`](@ref): - -```jldoctest -julia> isnull(Nullable{Float64}()) -true - -julia> isnull(Nullable(0.0)) -false -``` - -### Safely accessing the value of a `Nullable` object - -You can safely access the value of a `Nullable` object using [`get`](@ref): - -```jldoctest -julia> get(Nullable{Float64}()) -ERROR: NullException() -Stacktrace: -[...] - -julia> get(Nullable(1.0)) -1.0 -``` - -If the value is not present, as it would be for `Nullable{Float64}`, a [`NullException`](@ref) -error will be thrown. The error-throwing nature of the `get` function ensures that any -attempt to access a missing value immediately fails. - -In cases for which a reasonable default value exists that could be used when a `Nullable` -object's value turns out to be missing, you can provide this default value as a second argument -to `get`: - -```jldoctest -julia> get(Nullable{Float64}(), 0.0) -0.0 - -julia> get(Nullable(1.0), 0.0) -1.0 -``` - -!!! tip - Make sure the type of the default value passed to `get` and that of the `Nullable` - object match to avoid type instability, which could hurt performance. Use [`convert`](@ref) - manually if needed. - -### Performing operations on `Nullable` objects - -`Nullable` objects represent values that are possibly missing, and it -is possible to write all code using these objects by first testing to see if -the value is missing with [`isnull`](@ref), and then doing an appropriate -action. However, there are some common use cases where the code could be more -concise or clear by using a higher-order function. - -The [`map`](@ref) function takes as arguments a function `f` and a `Nullable` value -`x`. It produces a `Nullable`: - - - If `x` is a missing value, then it produces a missing value; - - If `x` has a value, then it produces a `Nullable` containing - `f(get(x))` as value. - -This is useful for performing simple operations on values that might be missing -if the desired behaviour is to simply propagate the missing values forward. - -The [`filter`](@ref) function takes as arguments a predicate function `p` -(that is, a function returning a boolean) and a `Nullable` value `x`. -It produces a `Nullable` value: - - - If `x` is a missing value, then it produces a missing value; - - If `p(get(x))` is true, then it produces the original value `x`; - - If `p(get(x))` is false, then it produces a missing value. - -In this way, `filter` can be thought of as selecting only allowable -values, and converting non-allowable values to missing values. - -While `map` and `filter` are useful in specific cases, by far the most useful -higher-order function is [`broadcast`](@ref), which can handle a wide variety of cases, -including making existing operations work and propagate `Nullable`s. An example -will motivate the need for `broadcast`. Suppose we have a function that computes the -greater of two real roots of a quadratic equation, using the quadratic formula: - -```jldoctest nullableroot -julia> root(a::Real, b::Real, c::Real) = (-b + √(b^2 - 4a*c)) / 2a -root (generic function with 1 method) -``` - -We may verify that the result of `root(1, -9, 20)` is `5.0`, as we expect, -since `5.0` is the greater of two real roots of the quadratic equation. - -Suppose now that we want to find the greatest real root of a quadratic -equations where the coefficients might be missing values. Having missing values -in datasets is a common occurrence in real-world data, and so it is important -to be able to deal with them. But we cannot find the roots of an equation if we -do not know all the coefficients. The best solution to this will depend on the -particular use case; perhaps we should throw an error. However, for this -example, we will assume that the best solution is to propagate the missing -values forward; that is, if any input is missing, we simply produce a missing -output. - -The `broadcast` function makes this task easy; we can simply pass the -`root` function we wrote to `broadcast`: - -```jldoctest nullableroot -julia> broadcast(root, Nullable(1), Nullable(-9), Nullable(20)) -Nullable{Float64}(5.0) - -julia> broadcast(root, Nullable(1), Nullable{Int}(), Nullable{Int}()) -Nullable{Float64}() - -julia> broadcast(root, Nullable{Int}(), Nullable(-9), Nullable(20)) -Nullable{Float64}() -``` - -If one or more of the inputs is missing, then the output of -`broadcast` will be missing. - -There exists special syntactic sugar for the `broadcast` function -using a dot notation: - -```jldoctest nullableroot -julia> root.(Nullable(1), Nullable(-9), Nullable(20)) -Nullable{Float64}(5.0) -``` - -In particular, the regular arithmetic operators can be `broadcast` -conveniently using `.`-prefixed operators: - -```jldoctest -julia> Nullable(2) ./ Nullable(3) .+ Nullable(1.0) -Nullable{Float64}(1.66667) -``` diff --git a/doc/src/stdlib/base.md b/doc/src/stdlib/base.md index c2fb3c3a4dabc..2d8c3c2d632c2 100644 --- a/doc/src/stdlib/base.md +++ b/doc/src/stdlib/base.md @@ -112,7 +112,6 @@ Core.:(===) Core.isa Base.isequal Base.isless -Base.isless(::Nullable, ::Nullable) Base.ifelse Base.lexcmp Base.lexless @@ -166,15 +165,16 @@ Base.instances ## Special Types ```@docs -Core.Void Core.Any -Base.Enums.@enum Core.Union Union{} Core.UnionAll Core.Tuple Base.Val Core.Vararg +Core.Void +Base.Some +Base.Enums.@enum ``` ## Generic Functions @@ -210,19 +210,11 @@ Base.@label Base.@polly ``` -## Nullables - -```@docs -Base.Nullable -Base.get(::Nullable, ::Any) -Base.isnull -Base.unsafe_get -``` - ## Missing Values ```@docs Base.Missing Base.missing +Base.coalesce Base.ismissing Base.skipmissing ``` @@ -292,7 +284,6 @@ Base.KeyError Base.LoadError Base.MethodError Base.MissingException -Base.NullException Core.OutOfMemoryError Core.ReadOnlyMemoryError Core.OverflowError diff --git a/examples/clustermanager/0mq/ZMQCM.jl b/examples/clustermanager/0mq/ZMQCM.jl index f5f0127179196..69ceb5e0d462a 100644 --- a/examples/clustermanager/0mq/ZMQCM.jl +++ b/examples/clustermanager/0mq/ZMQCM.jl @@ -214,19 +214,19 @@ end function connect(manager::ZMQCMan, pid::Int, config::WorkerConfig) #println("connect_m2w") if myid() == 1 - zid = get(config.userdata)[:zid] + zid = config.userdata[:zid] config.connect_at = zid # This will be useful in the worker-to-worker connection setup. - print_worker_stdout(get(config.userdata)[:io], pid) + print_worker_stdout(config.userdata[:io], pid) else #println("connect_w2w") - zid = get(config.connect_at) + zid = config.connect_at config.userdata = Dict{Symbol, Any}(:zid=>zid) end streams = setup_connection(zid, SELF_INITIATED) - udata = get(config.userdata) + udata = config.userdata udata[:streams] = streams streams @@ -261,13 +261,13 @@ function manage(manager::ZMQCMan, id::Int, config::WorkerConfig, op) end function kill(manager::ZMQCMan, pid::Int, config::WorkerConfig) - send_data(get(config.userdata)[:zid], CONTROL_MSG, KILL_MSG) - (r_s, w_s) = get(config.userdata)[:streams] + send_data(config.userdata[:zid], CONTROL_MSG, KILL_MSG) + (r_s, w_s) = config.userdata[:streams] close(r_s) close(w_s) # remove from our map - delete!(manager.map_zmq_julia, get(config.userdata)[:zid]) + delete!(manager.map_zmq_julia, config.userdata[:zid]) nothing end diff --git a/examples/clustermanager/simple/UnixDomainCM.jl b/examples/clustermanager/simple/UnixDomainCM.jl index f295dbf2b0608..4e96e69b63ef2 100644 --- a/examples/clustermanager/simple/UnixDomainCM.jl +++ b/examples/clustermanager/simple/UnixDomainCM.jl @@ -29,19 +29,20 @@ end function connect(manager::UnixDomainCM, pid::Int, config::WorkerConfig) if myid() == 1 # println("connect_m2w") - config.connect_at = get(config.userdata)[:sockname] # This will be useful in the worker-to-worker connection setup. + # This will be useful in the worker-to-worker connection setup. + config.connect_at = config.userdata[:sockname] - print_worker_stdout(get(config.userdata)[:io], pid) + print_worker_stdout(config.userdata[:io], pid) else # println("connect_w2w") - sockname = get(config.connect_at) + sockname = config.connect_at config.userdata = Dict{Symbol, Any}(:sockname=>sockname) end t = time() while true try - address = get(config.userdata)[:sockname] + address = config.userdata[:sockname] if isa(address, Tuple) sock = connect(address...) else @@ -74,7 +75,7 @@ function manage(manager::UnixDomainCM, id::Int, config::WorkerConfig, op) # Does not seem to be required, filesystem entry cleanup is happening automatically on process exit # if op == :deregister # try -# rm(get(config.userdata)[:sockname]) +# rm(config.userdata[:sockname]) # end # end nothing diff --git a/stdlib/Dates/src/io.jl b/stdlib/Dates/src/io.jl index 379f2cf58b6a7..3a35feb29584a 100644 --- a/stdlib/Dates/src/io.jl +++ b/stdlib/Dates/src/io.jl @@ -19,7 +19,7 @@ the argument out in the method definition. Return a tuple of 2 elements `(res, idx)`, where: -* `res` is a `Nullable{T}` - the result of the parsing, null if parsing failed. +* `res` is either the result of the parsing, or `nothing` if parsing failed. * `idx` is an `Int` - if parsing failed, the index at which it failed; if parsing succeeded, `idx` is the index _after_ the index at which parsing ended. """ @@ -99,11 +99,11 @@ end for (tok, fn) in zip("uUeE", [monthabbr_to_value, monthname_to_value, dayabbr_to_value, dayname_to_value]) @eval @inline function tryparsenext(d::DatePart{$tok}, str, i, len, locale) word, i = tryparsenext_word(str, i, len, locale, max_width(d)) - val = isnull(word) ? 0 : $fn(get(word), locale) + val = word === nothing ? 0 : $fn(word, locale) if val == 0 - return Nullable{Int64}(), i + return nothing, i else - return Nullable{Int64}(val), i + return val, i end end end @@ -113,8 +113,8 @@ struct Decimal3 end @inline function tryparsenext(d::DatePart{'s'}, str, i, len) ms, ii = tryparsenext_base10(str, i, len, min_width(d), max_width(d)) - if !isnull(ms) - val0 = val = get(ms) + if ms !== nothing + val0 = val = ms len = ii - i if len > 3 val, r = divrem(val, Int64(10) ^ (len - 3)) @@ -122,7 +122,7 @@ struct Decimal3 end else val *= Int64(10) ^ (3 - len) end - ms = Nullable{Int64}(val) + ms = val end return ms, ii end @@ -186,30 +186,28 @@ Delim(d::Char) = Delim{Char, 1}(d) Delim(d::String) = Delim{String, length(d)}(d) @inline function tryparsenext(d::Delim{Char, N}, str, i::Int, len) where N - R = Nullable{Bool} for j=1:N - i > len && return (R(), i) + i > len && return (nothing, i) c, i = next(str, i) - c != d.d && return (R(), i) + c != d.d && return (nothing, i) end - return R(true), i + return true, i end @inline function tryparsenext(d::Delim{String, N}, str, i::Int, len) where N - R = Nullable{Bool} i1 = i i2 = start(d.d) for j = 1:N if i1 > len - return R(), i1 + return nothing, i1 end c1, i1 = next(str, i1) c2, i2 = next(d.d, i2) if c1 != c2 - return R(), i1 + return nothing, i1 end end - return R(true), i1 + return true, i1 end @inline function format(io, d::Delim, dt, locale) diff --git a/stdlib/Dates/src/parse.jl b/stdlib/Dates/src/parse.jl index 35829f00e7b5a..8c59189b5687c 100644 --- a/stdlib/Dates/src/parse.jl +++ b/stdlib/Dates/src/parse.jl @@ -24,12 +24,13 @@ genvar(t::DataType) = Symbol(Base.Unicode.lowercase(string(Base.datatype_name(t) Parse the string according to the directives within the `DateFormat`. Parsing will start at character index `pos` and will stop when all directives are used or we have parsed up to the end of the string, `len`. When a directive cannot be parsed the returned value tuple -will be null if `raise` is false otherwise an exception will be thrown. +will be `nothing` if `raise` is false otherwise an exception will be thrown. Return a 3-element tuple `(values, pos, num_parsed)`: -* `values::Nullable{Tuple}`: A tuple which contains a value for each `DatePart` within the - `DateFormat` in the order in which they occur. If the string ends before we finish parsing - all the directives the missing values will be filled in with default values. +* `values::Union{Tuple, Void}`: Either `nothing`, or a tuple which contains a value + for each `DatePart` within the `DateFormat` in the order + in which they occur. If the string ends before we finish parsing all the directives + the missing values will be filled in with default values. * `pos::Int`: The character index at which parsing stopped. * `num_parsed::Int`: The number of values which were parsed and stored within `values`. Useful for distinguishing parsed values from default values. @@ -42,7 +43,6 @@ Return a 3-element tuple `(values, pos, num_parsed)`: tokens = Type[CONVERSION_SPECIFIERS[letter] for letter in letters] value_names = Symbol[genvar(t) for t in tokens] value_defaults = Tuple(CONVERSION_DEFAULTS[t] for t in tokens) - R = typeof(value_defaults) # Pre-assign variables to defaults. Allows us to use `@goto done` without worrying about # unassigned variables. @@ -58,13 +58,13 @@ Return a 3-element tuple `(values, pos, num_parsed)`: begin if directives[i] <: DatePart name = value_names[vi] - nullable = Symbol(:nullable_, name) + val = Symbol(:val, name) vi += 1 quote pos > len && @goto done - $nullable, next_pos = tryparsenext(directives[$i], str, pos, len, locale) - isnull($nullable) && @goto error - $name = unsafe_get($nullable) + $val, next_pos = tryparsenext(directives[$i], str, pos, len, locale) + $val === nothing && @goto error + $name = $val pos = next_pos num_parsed += 1 directive_index += 1 @@ -72,8 +72,8 @@ Return a 3-element tuple `(values, pos, num_parsed)`: else quote pos > len && @goto done - nullable_delim, next_pos = tryparsenext(directives[$i], str, pos, len, locale) - isnull(nullable_delim) && @goto error + delim, next_pos = tryparsenext(directives[$i], str, pos, len, locale) + delim === nothing && @goto error pos = next_pos directive_index += 1 end @@ -95,7 +95,7 @@ Return a 3-element tuple `(values, pos, num_parsed)`: pos > len || @goto error @label done - return Nullable{$R}($(Expr(:tuple, value_names...))), pos, num_parsed + return $(Expr(:tuple, value_names...)), pos, num_parsed @label error if raise @@ -106,7 +106,7 @@ Return a 3-element tuple `(values, pos, num_parsed)`: throw(ArgumentError("Unable to parse date time. Expected directive $d at char $pos")) end end - return Nullable{$R}(), pos, 0 + return nothing, pos, 0 end end @@ -116,12 +116,12 @@ end Parse the string according to the directives within the `DateFormat`. The specified `TimeType` type determines the type of and order of tokens returned. If the given `DateFormat` or string does not provide a required token a default value will be used. When the string cannot be -parsed the returned value tuple will be null if `raise` is false otherwise an exception will +parsed the returned value tuple will be `nothing` if `raise` is false otherwise an exception will be thrown. Return a 2-element tuple `(values, pos)`: -* `values::Nullable{Tuple}`: A tuple which contains a value for each token as specified by - the passed in type. +* `values::Union{Tuple, Void}`: Either `nothing`, or a tuple which contains a value + for each token as specified by the passed in type. * `pos::Int`: The character index at which parsing stopped. """ @generated function tryparsenext_internal(::Type{T}, str::AbstractString, pos::Int, len::Int, @@ -134,7 +134,6 @@ Return a 2-element tuple `(values, pos)`: output_tokens = CONVERSION_TRANSLATIONS[T] output_names = Symbol[genvar(t) for t in output_tokens] output_defaults = Tuple(CONVERSION_DEFAULTS[t] for t in output_tokens) - R = typeof(output_defaults) # Pre-assign output variables to defaults. Ensures that all output variables are # assigned as the value tuple returned from `tryparsenext_core` may not include all @@ -151,15 +150,15 @@ Return a 2-element tuple `(values, pos)`: quote values, pos, num_parsed = tryparsenext_core(str, pos, len, df, raise) - isnull(values) && return Nullable{$R}(), pos + values === nothing && return nothing, pos $(assign_defaults...) - $value_tuple = unsafe_get(values) - return Nullable{$R}($(Expr(:tuple, output_names...))), pos + $value_tuple = values + return $(Expr(:tuple, output_names...)), pos end end @inline function tryparsenext_base10(str::AbstractString, i::Int, len::Int, min_width::Int=1, max_width::Int=0) - i > len && (return Nullable{Int64}(), i) + i > len && (return nothing, i) min_pos = min_width <= 0 ? i : i + min_width - 1 max_pos = max_width <= 0 ? len : min(i + max_width - 1, len) d::Int64 = 0 @@ -173,9 +172,9 @@ end i = ii end if i <= min_pos - return Nullable{Int64}(), i + return nothing, i else - return Nullable{Int64}(d), i + return d, i end end @@ -192,9 +191,9 @@ end i = ii end if word_end == 0 - return Nullable{SubString}(), i + return nothing, i else - return Nullable{SubString}(SubString(str, word_start, word_end)), i + return SubString(str, word_start, word_end), i end end @@ -204,56 +203,56 @@ function Base.parse(::Type{DateTime}, s::AbstractString, df::typeof(ISODateTimeF dm = dd = Int64(1) th = tm = ts = tms = Int64(0) - nv, i = tryparsenext_base10(s, i, end_pos, 1) - dy = isnull(nv) ? (@goto error) : unsafe_get(nv) + val, i = tryparsenext_base10(s, i, end_pos, 1) + dy = val === nothing ? (@goto error) : val i > end_pos && @goto error c, i = next(s, i) c != '-' && @goto error i > end_pos && @goto done - nv, i = tryparsenext_base10(s, i, end_pos, 1, 2) - dm = isnull(nv) ? (@goto error) : unsafe_get(nv) + val, i = tryparsenext_base10(s, i, end_pos, 1, 2) + dm = val === nothing ? (@goto error) : val i > end_pos && @goto done c, i = next(s, i) c != '-' && @goto error i > end_pos && @goto done - nv, i = tryparsenext_base10(s, i, end_pos, 1, 2) - dd = isnull(nv) ? (@goto error) : unsafe_get(nv) + val, i = tryparsenext_base10(s, i, end_pos, 1, 2) + dd = val === nothing ? (@goto error) : val i > end_pos && @goto done c, i = next(s, i) c != 'T' && @goto error i > end_pos && @goto done - nv, i = tryparsenext_base10(s, i, end_pos, 1, 2) - th = isnull(nv) ? (@goto error) : unsafe_get(nv) + val, i = tryparsenext_base10(s, i, end_pos, 1, 2) + th = val === nothing ? (@goto error) : val i > end_pos && @goto done c, i = next(s, i) c != ':' && @goto error i > end_pos && @goto done - nv, i = tryparsenext_base10(s, i, end_pos, 1, 2) - tm = isnull(nv) ? (@goto error) : unsafe_get(nv) + val, i = tryparsenext_base10(s, i, end_pos, 1, 2) + tm = val === nothing ? (@goto error) : val i > end_pos && @goto done c, i = next(s, i) c != ':' && @goto error i > end_pos && @goto done - nv, i = tryparsenext_base10(s, i, end_pos, 1, 2) - ts = isnull(nv) ? (@goto error) : unsafe_get(nv) + val, i = tryparsenext_base10(s, i, end_pos, 1, 2) + ts = val === nothing ? (@goto error) : val i > end_pos && @goto done c, i = next(s, i) c != '.' && @goto error i > end_pos && @goto done - nv, j = tryparsenext_base10(s, i, end_pos, 1, 3) - tms = isnull(nv) ? (@goto error) : unsafe_get(nv) + val, j = tryparsenext_base10(s, i, end_pos, 1, 3) + tms = val === nothing ? (@goto error) : val tms *= 10 ^ (3 - (j - i)) j > end_pos || @goto error @@ -268,19 +267,19 @@ end function Base.parse(::Type{T}, str::AbstractString, df::DateFormat=default_format(T)) where T<:TimeType pos, len = start(str), endof(str) values, pos = tryparsenext_internal(T, str, pos, len, df, true) - T(unsafe_get(values)...) + T(values...) end function Base.tryparse(::Type{T}, str::AbstractString, df::DateFormat=default_format(T)) where T<:TimeType pos, len = start(str), endof(str) values, pos = tryparsenext_internal(T, str, pos, len, df, false) - if isnull(values) - Nullable{T}() - elseif isnull(validargs(T, unsafe_get(values)...)) + if values === nothing + nothing + elseif validargs(T, values...) === nothing # TODO: validargs gets called twice, since it's called again in the T constructor - Nullable{T}(T(unsafe_get(values)...)) + T(values...) else - Nullable{T}() + nothing end end @@ -299,7 +298,7 @@ number of components may be less than the total number of `DatePart`. quote pos, len = start(str), endof(str) values, pos, num_parsed = tryparsenext_core(str, pos, len, df, true) - t = unsafe_get(values) + t = values types = $(Expr(:tuple, tokens...)) result = Vector{Any}(uninitialized, num_parsed) for (i, typ) in enumerate(types) diff --git a/stdlib/Dates/src/types.jl b/stdlib/Dates/src/types.jl index 5294b50cbfdd7..008d6e37afe9e 100644 --- a/stdlib/Dates/src/types.jl +++ b/stdlib/Dates/src/types.jl @@ -152,21 +152,21 @@ daysinmonth(y,m) = DAYSINMONTH[m] + (m == 2 && isleapyear(y)) # we can validate arguments in tryparse. """ - validargs(::Type{<:TimeType}, args...) -> Nullable{ArgumentError} + validargs(::Type{<:TimeType}, args...) -> Union{ArgumentError, Void} Determine whether the given arguments consitute valid inputs for the given type. -Returns a `Nullable{ArgumentError}` where null signifies success. +Returns either an `ArgumentError`, or [`nothing`](@ref) in case of success. """ function validargs end """ - argerror([msg]) -> Nullable{ArgumentError} + argerror([msg]) -> Union{ArgumentError, Void} -Construct a `Nullable{ArgumentError}` with the given message, or null if no message -is provided. For use by `validargs`. +Return an `ArgumentError` object with the given message, +or [`nothing`](@ref) if no message is provided. For use by `validargs`. """ -argerror(msg::String) = Nullable(ArgumentError(msg)) -argerror() = Nullable{ArgumentError}() +argerror(msg::String) = ArgumentError(msg) +argerror() = nothing ### CONSTRUCTORS ### # Core constructors @@ -178,7 +178,7 @@ Construct a `DateTime` type by parts. Arguments must be convertible to [`Int64`] function DateTime(y::Int64, m::Int64=1, d::Int64=1, h::Int64=0, mi::Int64=0, s::Int64=0, ms::Int64=0) err = validargs(DateTime, y, m, d, h, mi, s, ms) - isnull(err) || throw(unsafe_get(err)) + err === nothing || throw(err) rata = ms + 1000 * (s + 60mi + 3600h + 86400 * totaldays(y, m, d)) return DateTime(UTM(rata)) end @@ -201,7 +201,7 @@ Construct a `Date` type by parts. Arguments must be convertible to [`Int64`](@re """ function Date(y::Int64, m::Int64=1, d::Int64=1) err = validargs(Date, y, m, d) - isnull(err) || throw(unsafe_get(err)) + err === nothing || throw(err) return Date(UTD(totaldays(y, m, d))) end @@ -218,7 +218,7 @@ Construct a `Time` type by parts. Arguments must be convertible to [`Int64`](@re """ function Time(h::Int64, mi::Int64=0, s::Int64=0, ms::Int64=0, us::Int64=0, ns::Int64=0) err = validargs(Time, h, mi, s, ms, us, ns) - isnull(err) || throw(unsafe_get(err)) + err === nothing || throw(err) return Time(Nanosecond(ns + 1000us + 1000000ms + 1000000000s + 60000000000mi + 3600000000000h)) end diff --git a/stdlib/Dates/test/io.jl b/stdlib/Dates/test/io.jl index d6e0a2ad774d9..8ddf5367229de 100644 --- a/stdlib/Dates/test/io.jl +++ b/stdlib/Dates/test/io.jl @@ -439,7 +439,7 @@ end end end # Issue #21504 -@test isnull(tryparse(Dates.Date, "0-1000")) +@test tryparse(Dates.Date, "0-1000") === nothing @testset "parse milliseconds, Issue #22100" begin @test Dates.DateTime("2017-Mar-17 00:00:00.0000", "y-u-d H:M:S.s") == Dates.DateTime(2017, 3, 17) diff --git a/stdlib/DelimitedFiles/src/DelimitedFiles.jl b/stdlib/DelimitedFiles/src/DelimitedFiles.jl index f78ac9b73488b..02337e6d7a9bf 100644 --- a/stdlib/DelimitedFiles/src/DelimitedFiles.jl +++ b/stdlib/DelimitedFiles/src/DelimitedFiles.jl @@ -523,18 +523,18 @@ end function colval(sbuff::String, startpos::Int, endpos::Int, cells::Array{Bool,2}, row::Int, col::Int) n = tryparse_internal(Bool, sbuff, startpos, endpos, 0, false) - isnull(n) || (cells[row, col] = unsafe_get(n)) - isnull(n) + n === nothing || (cells[row, col] = n) + n === nothing end function colval(sbuff::String, startpos::Int, endpos::Int, cells::Array{T,2}, row::Int, col::Int) where T<:Integer n = tryparse_internal(T, sbuff, startpos, endpos, 0, false) - isnull(n) || (cells[row, col] = unsafe_get(n)) - isnull(n) + n === nothing || (cells[row, col] = n) + n === nothing end function colval(sbuff::String, startpos::Int, endpos::Int, cells::Array{T,2}, row::Int, col::Int) where T<:Union{Real,Complex} n = tryparse_internal(T, sbuff, startpos, endpos, false) - isnull(n) || (cells[row, col] = unsafe_get(n)) - isnull(n) + n === nothing || (cells[row, col] = n) + n === nothing end function colval(sbuff::String, startpos::Int, endpos::Int, cells::Array{<:AbstractString,2}, row::Int, col::Int) cells[row, col] = SubString(sbuff, startpos, endpos) @@ -546,15 +546,16 @@ function colval(sbuff::String, startpos::Int, endpos::Int, cells::Array{Any,2}, if len > 0 # check Inteter ni64 = tryparse_internal(Int, sbuff, startpos, endpos, 0, false) - isnull(ni64) || (cells[row, col] = unsafe_get(ni64); return false) + ni64 === nothing || (cells[row, col] = ni64; return false) # check Bool nb = tryparse_internal(Bool, sbuff, startpos, endpos, 0, false) - isnull(nb) || (cells[row, col] = unsafe_get(nb); return false) + nb === nothing || (cells[row, col] = nb; return false) # check float64 - nf64 = ccall(:jl_try_substrtod, Nullable{Float64}, (Ptr{UInt8}, Csize_t, Csize_t), sbuff, startpos-1, endpos-startpos+1) - isnull(nf64) || (cells[row, col] = unsafe_get(nf64); return false) + hasvalue, valf64 = ccall(:jl_try_substrtod, Tuple{Bool, Float64}, + (Ptr{UInt8}, Csize_t, Csize_t), sbuff, startpos-1, endpos-startpos+1) + hasvalue && (cells[row, col] = valf64; return false) end cells[row, col] = SubString(sbuff, startpos, endpos) false diff --git a/stdlib/Distributed/src/Distributed.jl b/stdlib/Distributed/src/Distributed.jl index 7a243bfb94f45..d36705747138b 100644 --- a/stdlib/Distributed/src/Distributed.jl +++ b/stdlib/Distributed/src/Distributed.jl @@ -16,7 +16,7 @@ using Base: Process, Semaphore, JLOptions, AnyDict, buffer_writes, wait_connecte VERSION_STRING, sync_begin, sync_add, sync_end, async_run_thunk, binding_module, notify_error, atexit, julia_exename, julia_cmd, AsyncGenerator, acquire, release, invokelatest, - shell_escape_posixly, uv_error + shell_escape_posixly, uv_error, coalesce, notnothing using Base.Unicode: isascii, isdigit, isnumeric # NOTE: clusterserialize.jl imports additional symbols from Base.Serializer for use diff --git a/stdlib/Distributed/src/cluster.jl b/stdlib/Distributed/src/cluster.jl index e658d639664c1..2f21b6e7809f1 100644 --- a/stdlib/Distributed/src/cluster.jl +++ b/stdlib/Distributed/src/cluster.jl @@ -4,47 +4,47 @@ abstract type ClusterManager end mutable struct WorkerConfig # Common fields relevant to all cluster managers - io::Nullable{IO} - host::Nullable{AbstractString} - port::Nullable{Integer} + io::Union{IO, Void} + host::Union{AbstractString, Void} + port::Union{Integer, Void} # Used when launching additional workers at a host - count::Nullable{Union{Int, Symbol}} - exename::Nullable{Union{AbstractString, Cmd}} - exeflags::Nullable{Cmd} + count::Union{Int, Symbol, Void} + exename::Union{AbstractString, Cmd, Void} + exeflags::Union{Cmd, Void} # External cluster managers can use this to store information at a per-worker level # Can be a dict if multiple fields need to be stored. - userdata::Nullable{Any} + userdata::Any # SSHManager / SSH tunnel connections to workers - tunnel::Nullable{Bool} - bind_addr::Nullable{AbstractString} - sshflags::Nullable{Cmd} - max_parallel::Nullable{Integer} + tunnel::Union{Bool, Void} + bind_addr::Union{AbstractString, Void} + sshflags::Union{Cmd, Void} + max_parallel::Union{Integer, Void} # Used by Local/SSH managers - connect_at::Nullable{Any} + connect_at::Any - process::Nullable{Process} - ospid::Nullable{Integer} + process::Union{Process, Void} + ospid::Union{Integer, Void} # Private dictionary used to store temporary information by Local/SSH managers. - environ::Nullable{Dict} + environ::Union{Dict, Void} # Connections to be setup depending on the network topology requested - ident::Nullable{Any} # Worker as identified by the Cluster Manager. + ident::Any # Worker as identified by the Cluster Manager. # List of other worker idents this worker must connect with. Used with topology T_CUSTOM. - connect_idents::Nullable{Array} + connect_idents::Union{Array, Void} # Run multithreaded blas on worker - enable_threaded_blas::Nullable{Bool} + enable_threaded_blas::Union{Bool, Void} function WorkerConfig() wc = new() for n in 1:length(WorkerConfig.types) T = eltype(fieldtype(WorkerConfig, n)) - setfield!(wc, n, Nullable{T}()) + setfield!(wc, n, nothing) end wc end @@ -59,7 +59,7 @@ mutable struct Worker state::WorkerState c_state::Condition # wait for state changes ct_time::Float64 # creation time - conn_func::Nullable{Function} # Used to setup connections lazily + conn_func::Any # used to setup connections lazily r_stream::IO w_stream::IO @@ -67,10 +67,11 @@ mutable struct Worker # serializer as part of the Worker object manager::ClusterManager config::WorkerConfig - version::Nullable{VersionNumber} # Julia version of the remote process + version::Union{VersionNumber, Void} # Julia version of the remote process function Worker(id::Int, r_stream::IO, w_stream::IO, manager::ClusterManager; - version=Nullable{VersionNumber}(), config=WorkerConfig()) + version::Union{VersionNumber, Void}=nothing, + config::WorkerConfig=WorkerConfig()) w = Worker(id) w.r_stream = r_stream w.w_stream = buffer_writes(w_stream) @@ -83,7 +84,7 @@ mutable struct Worker w end - Worker(id::Int) = Worker(id, Nullable{Function}()) + Worker(id::Int) = Worker(id, nothing) function Worker(id::Int, conn_func) @assert id > 0 if haskey(map_pid_wrkr, id) @@ -127,13 +128,10 @@ end exec_conn_func(id::Int) = exec_conn_func(worker_from_id(id)) function exec_conn_func(w::Worker) - if isnull(w.conn_func) - return wait_for_conn(w) # Some other task may be trying to connect at the same time. - end - try - f = get(w.conn_func) - w.conn_func = Nullable{Function}() + f = notnothing(w.conn_func) + # Will be called if some other task tries to connect at the same time. + w.conn_func = () -> wait_for_conn(w) f() catch e w.conn_func = () -> throw(e) @@ -174,16 +172,12 @@ worker_timeout() = parse(Float64, get(ENV, "JULIA_WORKER_TIMEOUT", "60.0")) ## worker creation and setup ## """ - start_worker(out::IO=STDOUT) - start_worker(cookie::AbstractString) - start_worker(out::IO, cookie::AbstractString) + start_worker([out::IO=STDOUT], cookie::AbstractString=readline(STDIN)) `start_worker` is an internal function which is the default entry point for worker processes connecting via TCP/IP. It sets up the process as a Julia cluster worker. -If the cookie is unspecified, the worker tries to read it from its STDIN. - host:port information is written to stream `out` (defaults to STDOUT). The function closes STDIN (after reading the cookie if required), redirects STDERR to STDOUT, @@ -192,15 +186,8 @@ line option) and schedules tasks to process incoming TCP connections and request It does not return. """ -start_worker(out::IO=STDOUT) = start_worker(out, Nullable{AbstractString}()) -start_worker(cookie::AbstractString) = start_worker(STDOUT, Nullable{AbstractString}(cookie)) -start_worker(out::IO, cookie::AbstractString) = start_worker(out, Nullable{AbstractString}(cookie)) -function start_worker(out::IO, cookie_in::Nullable{AbstractString}) - if isnull(cookie_in) - cookie = readline(STDIN) - else - cookie = get(cookie_in) - end +start_worker(cookie::AbstractString=readline(STDIN)) = start_worker(STDOUT, cookie) +function start_worker(out::IO, cookie::AbstractString=readline(STDIN)) close(STDIN) # workers will not use it redirect_stderr(STDOUT) @@ -388,8 +375,8 @@ function addprocs_locked(manager::ClusterManager; kwargs...) params[:lazy] = false end - if isnull(PGRP.lazy) || nprocs() == 1 - PGRP.lazy = Nullable{Bool}(params[:lazy]) + if PGRP.lazy === nothing || nprocs() == 1 + PGRP.lazy = params[:lazy] elseif isclusterlazy() != params[:lazy] throw(ArgumentError(string("Active workers with lazy=", isclusterlazy(), ". Cannot set lazy=", params[:lazy]))) @@ -462,9 +449,9 @@ function setup_launched_worker(manager, wconfig, launched_q) # When starting workers on remote multi-core hosts, `launch` can (optionally) start only one # process on the remote machine, with a request to start additional workers of the # same type. This is done by setting an appropriate value to `WorkerConfig.cnt`. - cnt = get(wconfig.count, 1) + cnt = coalesce(wconfig.count, 1) if cnt === :auto - cnt = get(wconfig.environ)[:cpu_cores] + cnt = wconfig.environ[:cpu_cores] end cnt = cnt - 1 # Removing self from the requested number @@ -476,8 +463,8 @@ end function launch_n_additional_processes(manager, frompid, fromconfig, cnt, launched_q) @sync begin - exename = get(fromconfig.exename) - exeflags = get(fromconfig.exeflags, ``) + exename = notnothing(fromconfig.exename) + exeflags = coalesce(fromconfig.exeflags, ``) cmd = `$exename $exeflags` new_addresses = remotecall_fetch(launch_additional, frompid, cnt, cmd) @@ -561,10 +548,12 @@ function create_worker(manager, wconfig) elseif PGRP.topology == :custom # wait for requested workers to be up before connecting to them. - filterfunc(x) = (x.id != 1) && isdefined(x, :config) && (get(x.config.ident) in get(wconfig.connect_idents, [])) + filterfunc(x) = (x.id != 1) && isdefined(x, :config) && + (notnothing(x.config.ident) in coalesce(wconfig.connect_idents, [])) wlist = filter(filterfunc, PGRP.workers) - while length(wlist) < length(get(wconfig.connect_idents, [])) + while wconfig.connect_idents !== nothing && + length(wlist) < length(wconfig.connect_idents) sleep(1.0) wlist = filter(filterfunc, PGRP.workers) end @@ -575,9 +564,13 @@ function create_worker(manager, wconfig) end end - all_locs = map(x -> isa(x, Worker) ? (get(x.config.connect_at, ()), x.id) : ((), x.id, true), join_list) + all_locs = map(x -> isa(x, Worker) ? + (coalesce(x.config.connect_at, ()), x.id) : + ((), x.id, true), + join_list) send_connection_hdr(w, true) - join_message = JoinPGRPMsg(w.id, all_locs, PGRP.topology, get(wconfig.enable_threaded_blas, false), isclusterlazy()) + enable_threaded_blas = coalesce(wconfig.enable_threaded_blas, false) + join_message = JoinPGRPMsg(w.id, all_locs, PGRP.topology, enable_threaded_blas, isclusterlazy()) send_msg_now(w, MsgHeader(RRID(0,0), ntfy_oid), join_message) @schedule manage(w.manager, w.id, w.config, :register) @@ -679,9 +672,9 @@ mutable struct ProcessGroup workers::Array{Any,1} refs::Dict # global references topology::Symbol - lazy::Nullable{Bool} + lazy::Union{Bool, Void} - ProcessGroup(w::Array{Any,1}) = new("pg-default", w, Dict(), :all_to_all, Nullable{Bool}()) + ProcessGroup(w::Array{Any,1}) = new("pg-default", w, Dict(), :all_to_all, nothing) end const PGRP = ProcessGroup([]) @@ -695,23 +688,17 @@ function topology(t) t end -function isclusterlazy() - if isnull(PGRP.lazy) - return false - else - return get(PGRP.lazy) - end -end +isclusterlazy() = coalesce(PGRP.lazy, false) get_bind_addr(pid::Integer) = get_bind_addr(worker_from_id(pid)) get_bind_addr(w::LocalProcess) = LPROC.bind_addr function get_bind_addr(w::Worker) - if isnull(w.config.bind_addr) + if w.config.bind_addr === nothing if w.id != myid() w.config.bind_addr = remotecall_fetch(get_bind_addr, w.id, w.id) end end - get(w.config.bind_addr) + w.config.bind_addr end # globals @@ -1063,8 +1050,8 @@ function check_same_host(pids) if all(p -> (p==1) || (isa(map_pid_wrkr[p].manager, LocalManager)), pids) return true else - first_bind_addr = get(map_pid_wrkr[pids[1]].config.bind_addr) - return all(p -> (p != 1) && (get(map_pid_wrkr[p].config.bind_addr) == first_bind_addr), pids[2:end]) + first_bind_addr = notnothing(map_pid_wrkr[pids[1]].config.bind_addr) + return all(p -> (p != 1) && (notnothing(map_pid_wrkr[p].config.bind_addr) == first_bind_addr), pids[2:end]) end end end diff --git a/stdlib/Distributed/src/macros.jl b/stdlib/Distributed/src/macros.jl index 32e3ccaf53abb..26f51dc9991ed 100644 --- a/stdlib/Distributed/src/macros.jl +++ b/stdlib/Distributed/src/macros.jl @@ -27,13 +27,13 @@ returning a [`Future`](@ref) to the result. julia> addprocs(3); julia> f = @spawn myid() -Future(2, 1, 5, Nullable{Any}()) +Future(2, 1, 5, nothing) julia> fetch(f) 2 julia> f = @spawn myid() -Future(3, 1, 7, Nullable{Any}()) +Future(3, 1, 7, nothing) julia> fetch(f) 3 @@ -56,7 +56,7 @@ Accepts two arguments, `p` and an expression. julia> addprocs(1); julia> f = @spawnat 2 myid() -Future(2, 1, 3, Nullable{Any}()) +Future(2, 1, 3, nothing) julia> fetch(f) 2 diff --git a/stdlib/Distributed/src/managers.jl b/stdlib/Distributed/src/managers.jl index 9889a3c13fa08..ad5c4318766cc 100644 --- a/stdlib/Distributed/src/managers.jl +++ b/stdlib/Distributed/src/managers.jl @@ -227,10 +227,10 @@ end function manage(manager::SSHManager, id::Integer, config::WorkerConfig, op::Symbol) if op == :interrupt - ospid = get(config.ospid, 0) - if ospid > 0 - host = get(config.host) - sshflags = get(config.sshflags) + ospid = config.ospid + if ospid !== nothing + host = notnothing(config.host) + sshflags = notnothing(config.sshflags) if !success(`ssh -T -a -x -o ClearAllForwardings=yes -n $sshflags $host "kill -2 $ospid"`) @error "Error sending a Ctrl-C to julia worker $id on $host" end @@ -341,7 +341,7 @@ end function manage(manager::LocalManager, id::Integer, config::WorkerConfig, op::Symbol) if op == :interrupt - kill(get(config.process), 2) + kill(config.process, 2) end end @@ -387,24 +387,24 @@ ensure that messages are delivered and received completely and in order. workers. """ function connect(manager::ClusterManager, pid::Int, config::WorkerConfig) - if !isnull(config.connect_at) + if config.connect_at !== nothing # this is a worker-to-worker setup call. return connect_w2w(pid, config) end # master connecting to workers - if !isnull(config.io) - (bind_addr, port) = read_worker_host_port(get(config.io)) - pubhost=get(config.host, bind_addr) + if config.io !== nothing + (bind_addr, port) = read_worker_host_port(config.io) + pubhost = coalesce(config.host, bind_addr) config.host = pubhost config.port = port else - pubhost=get(config.host) - port=get(config.port) - bind_addr=get(config.bind_addr, pubhost) + pubhost = notnothing(config.host) + port = notnothing(config.port) + bind_addr = coalesce(config.bind_addr, pubhost) end - tunnel = get(config.tunnel, false) + tunnel = coalesce(config.tunnel, false) s = split(pubhost,'@') user = "" @@ -422,11 +422,11 @@ function connect(manager::ClusterManager, pid::Int, config::WorkerConfig) if tunnel if !haskey(tunnel_hosts_map, pubhost) - tunnel_hosts_map[pubhost] = Semaphore(get(config.max_parallel, typemax(Int))) + tunnel_hosts_map[pubhost] = Semaphore(coalesce(config.max_parallel, typemax(Int))) end sem = tunnel_hosts_map[pubhost] - sshflags = get(config.sshflags) + sshflags = notnothing(config.sshflags) acquire(sem) try (s, bind_addr) = connect_to_worker(pubhost, bind_addr, port, user, sshflags) @@ -442,9 +442,9 @@ function connect(manager::ClusterManager, pid::Int, config::WorkerConfig) # write out a subset of the connect_at required for further worker-worker connection setups config.connect_at = (bind_addr, port) - if !isnull(config.io) + if config.io !== nothing let pid = pid - redirect_worker_output(pid, get(config.io)) + redirect_worker_output(pid, notnothing(config.io)) end end @@ -452,7 +452,7 @@ function connect(manager::ClusterManager, pid::Int, config::WorkerConfig) end function connect_w2w(pid::Int, config::WorkerConfig) - (rhost, rport) = get(config.connect_at) + (rhost, rport) = notnothing(config.connect_at) config.host = rhost config.port = rport (s, bind_addr) = connect_to_worker(rhost, rport) diff --git a/stdlib/Distributed/src/messages.jl b/stdlib/Distributed/src/messages.jl index f78ed8f21b401..aec20d1f4bb2b 100644 --- a/stdlib/Distributed/src/messages.jl +++ b/stdlib/Distributed/src/messages.jl @@ -32,7 +32,7 @@ struct MsgHeader end # Special oid (0,0) uses to indicate a null ID. -# Used instead of Nullable to decrease wire size of header. +# Used instead of Union{Int, Void} to decrease wire size of header. null_id(id) = id == RRID(0, 0) struct CallMsg{Mode} <: AbstractMsg diff --git a/stdlib/Distributed/src/process_messages.jl b/stdlib/Distributed/src/process_messages.jl index fdbcc544629f8..7fa8b0adb3684 100644 --- a/stdlib/Distributed/src/process_messages.jl +++ b/stdlib/Distributed/src/process_messages.jl @@ -309,7 +309,7 @@ function handle_msg(msg::JoinPGRPMsg, header, r_stream, w_stream, version) end lazy = msg.lazy - PGRP.lazy = Nullable{Bool}(lazy) + PGRP.lazy = lazy wait_tasks = Task[] for (connect_at, rpid) in msg.other_workers @@ -319,7 +319,7 @@ function handle_msg(msg::JoinPGRPMsg, header, r_stream, w_stream, version) let rpid=rpid, wconfig=wconfig if lazy # The constructor registers the object with a global registry. - Worker(rpid, Nullable{Function}(()->connect_to_peer(cluster_manager, rpid, wconfig))) + Worker(rpid, ()->connect_to_peer(cluster_manager, rpid, wconfig)) else t = @async connect_to_peer(cluster_manager, rpid, wconfig) push!(wait_tasks, t) @@ -348,7 +348,7 @@ end function handle_msg(msg::JoinCompleteMsg, header, r_stream, w_stream, version) w = map_sock_wrkr[r_stream] - environ = get(w.config.environ, Dict()) + environ = coalesce(w.config.environ, Dict()) environ[:cpu_cores] = msg.cpu_cores w.config.environ = environ w.config.ospid = msg.ospid diff --git a/stdlib/Distributed/src/remotecall.jl b/stdlib/Distributed/src/remotecall.jl index 845ca6bf075e3..522fecdd57862 100644 --- a/stdlib/Distributed/src/remotecall.jl +++ b/stdlib/Distributed/src/remotecall.jl @@ -22,12 +22,12 @@ mutable struct Future <: AbstractRemoteRef where::Int whence::Int id::Int - v::Nullable{Any} + v::Union{Some{Any}, Void} - Future(w::Int, rrid::RRID) = Future(w, rrid, Nullable{Any}()) - Future(w::Int, rrid::RRID, v) = (r = new(w,rrid.whence,rrid.id,v); return test_existing_ref(r)) + Future(w::Int, rrid::RRID, v::Union{Some, Void}=nothing) = + (r = new(w,rrid.whence,rrid.id,v); return test_existing_ref(r)) - Future(t::Tuple) = new(t[1],t[2],t[3],t[4]) # Useful for creating dummy, zeroed-out instances + Future(t::NTuple{4, Any}) = new(t[1],t[2],t[3],t[4]) # Useful for creating dummy, zeroed-out instances end """ @@ -65,7 +65,7 @@ function test_existing_ref(r::AbstractRemoteRef) found = getkey(client_refs, r, nothing) if found !== nothing @assert r.where > 0 - if isa(r, Future) && isnull(found.v) && !isnull(r.v) + if isa(r, Future) && found.v === nothing && r.v !== nothing # we have recd the value from another source, probably a deserialized ref, send a del_client message send_del_client(r) found.v = r.v @@ -90,8 +90,8 @@ function finalize_ref(r::AbstractRemoteRef) send_del_client(r) else # send_del_client only if the reference has not been set - isnull(r.v) && send_del_client(r) - r.v = Nullable{Any}() + r.v === nothing && send_del_client(r) + r.v = nothing end r.where = 0 end @@ -182,7 +182,7 @@ or to use a local [`Channel`](@ref) as a proxy: isready(c) # will not block """ function isready(rr::Future) - !isnull(rr.v) && return true + rr.v === nothing || return true rid = remoteref_id(rr) return if rr.where == myid() @@ -281,7 +281,7 @@ end channel_type(rr::RemoteChannel{T}) where {T} = T -serialize(s::ClusterSerializer, f::Future) = serialize(s, f, isnull(f.v)) +serialize(s::ClusterSerializer, f::Future) = serialize(s, f, f.v === nothing) serialize(s::ClusterSerializer, rr::RemoteChannel) = serialize(s, rr, true) function serialize(s::ClusterSerializer, rr::AbstractRemoteRef, addclient) if addclient @@ -315,7 +315,7 @@ end # Future and RemoteChannel are serializable only in a running cluster. # Serialize zeroed-out values to non ClusterSerializer objects function serialize(s::AbstractSerializer, ::Future) - zero_fut = Future((0,0,0,Nullable{Any}())) + zero_fut = Future((0,0,0,nothing)) invoke(serialize, Tuple{AbstractSerializer, Any}, s, zero_fut) end @@ -478,7 +478,7 @@ end Wait for a value to become available for the specified future. """ -wait(r::Future) = (!isnull(r.v) && return r; call_on_owner(wait_ref, r, myid()); r) +wait(r::Future) = (r.v !== nothing && return r; call_on_owner(wait_ref, r, myid()); r) """ wait(r::RemoteChannel, args...) @@ -488,9 +488,9 @@ Wait for a value to become available on the specified remote channel. wait(r::RemoteChannel, args...) = (call_on_owner(wait_ref, r, myid(), args...); r) function fetch(r::Future) - !isnull(r.v) && return get(r.v) - v=call_on_owner(fetch_ref, r) - r.v=v + r.v !== nothing && return coalesce(r.v) + v = call_on_owner(fetch_ref, r) + r.v = Some(v) send_del_client(r) v end @@ -525,9 +525,9 @@ All asynchronous remote calls return `Future`s and set the value to the return value of the call upon completion. """ function put!(rr::Future, v) - !isnull(rr.v) && error("Future can be set only once") + rr.v !== nothing && error("Future can be set only once") call_on_owner(put_future, rr, v, myid()) - rr.v = v + rr.v = Some(v) rr end function put_future(rid, v, callee) diff --git a/stdlib/Distributed/src/workerpool.jl b/stdlib/Distributed/src/workerpool.jl index 1c444062e6a4f..a5fba93f2623e 100644 --- a/stdlib/Distributed/src/workerpool.jl +++ b/stdlib/Distributed/src/workerpool.jl @@ -185,7 +185,7 @@ performs a `remote_do` on it. """ remote_do(f, pool::AbstractWorkerPool, args...; kwargs...) = remotecall_pool(remote_do, f, pool, args...; kwargs...) -const _default_worker_pool = Ref{Nullable}(Nullable{WorkerPool}()) +const _default_worker_pool = Ref{Union{WorkerPool, Void}}(nothing) """ default_worker_pool() @@ -195,14 +195,14 @@ const _default_worker_pool = Ref{Nullable}(Nullable{WorkerPool}()) function default_worker_pool() # On workers retrieve the default worker pool from the master when accessed # for the first time - if isnull(_default_worker_pool[]) + if _default_worker_pool[] === nothing if myid() == 1 - _default_worker_pool[] = Nullable(WorkerPool()) + _default_worker_pool[] = WorkerPool() else - _default_worker_pool[] = Nullable(remotecall_fetch(()->default_worker_pool(), 1)) + _default_worker_pool[] = remotecall_fetch(()->default_worker_pool(), 1) end end - return get(_default_worker_pool[]) + return _default_worker_pool[] end """ diff --git a/stdlib/Distributed/test/distributed_exec.jl b/stdlib/Distributed/test/distributed_exec.jl index 80112777bb264..82a4e9621e3fb 100644 --- a/stdlib/Distributed/test/distributed_exec.jl +++ b/stdlib/Distributed/test/distributed_exec.jl @@ -83,10 +83,10 @@ end function testf(id) f=Future(id) @test isready(f) == false - @test isnull(f.v) == true + @test f.v === nothing put!(f, :OK) @test isready(f) == true - @test isnull(f.v) == false + @test f.v !== nothing @test_throws ErrorException put!(f, :OK) # Cannot put! to a already set future @test_throws MethodError take!(f) # take! is unsupported on a Future @@ -104,9 +104,9 @@ function test_futures_dgc(id) # remote value should be deleted after a fetch @test remotecall_fetch(k->(yield();haskey(Distributed.PGRP.refs, k)), id, fid) == true - @test isnull(f.v) == true + @test f.v === nothing @test fetch(f) == id - @test isnull(f.v) == false + @test f.v !== nothing yield(); # flush gc msgs @test remotecall_fetch(k->(yield();haskey(Distributed.PGRP.refs, k)), id, fid) == false @@ -115,7 +115,7 @@ function test_futures_dgc(id) f = remotecall(myid, id) fid = remoteref_id(f) @test remotecall_fetch(k->(yield();haskey(Distributed.PGRP.refs, k)), id, fid) == true - @test isnull(f.v) == true + @test f.v === nothing finalize(f) yield(); # flush gc msgs @test remotecall_fetch(k->(yield();haskey(Distributed.PGRP.refs, k)), id, fid) == false @@ -260,9 +260,7 @@ function test_regular_io_ser(ref::Distributed.AbstractRemoteRef) v = getfield(ref2, fld) if isa(v, Number) @test v === zero(typeof(v)) - elseif isa(v, Nullable) - @test v === Nullable{Any}() - else + elseif v !== nothing error(string("Add test for field ", fld)) end end @@ -604,7 +602,7 @@ if DoFullTest # error message but should not terminate. for w in Distributed.PGRP.workers if isa(w, Distributed.Worker) - local s = connect(get(w.config.host), get(w.config.port)) + local s = connect(w.config.host, w.config.port) write(s, randstring(32)) end end @@ -943,7 +941,7 @@ end function test_blas_config(pid, expected) for worker in Distributed.PGRP.workers if worker.id == pid - @test get(worker.config.enable_threaded_blas) == expected + @test worker.config.enable_threaded_blas == expected return end end diff --git a/stdlib/Distributed/test/topology.jl b/stdlib/Distributed/test/topology.jl index ff898351d3f52..7769e3bd1587f 100644 --- a/stdlib/Distributed/test/topology.jl +++ b/stdlib/Distributed/test/topology.jl @@ -61,9 +61,9 @@ end const map_pid_ident=Dict() function manage(manager::TopoTestManager, id::Integer, config::WorkerConfig, op::Symbol) if op == :register - map_pid_ident[id] = get(config.ident) + map_pid_ident[id] = config.ident elseif op == :interrupt - kill(get(config.process), 2) + kill(config.process, 2) end end diff --git a/stdlib/Profile/src/Profile.jl b/stdlib/Profile/src/Profile.jl index a948f09896c37..0a6f547bd86c6 100644 --- a/stdlib/Profile/src/Profile.jl +++ b/stdlib/Profile/src/Profile.jl @@ -429,7 +429,7 @@ function print_flat(io::IO, lilist::Vector{StackFrame}, n::Vector{Int}, Base.print(io, rpad(rtruncto(string(li.file), wfile), wfile, " "), " ") Base.print(io, lpad(string(li.line), wline, " "), " ") fname = string(li.func) - if !li.from_c && !isnull(li.linfo) + if !li.from_c && li.linfo !== nothing fname = sprint(show_spec_linfo, li) end Base.print(io, rpad(ltruncto(fname, wfunc), wfunc, " ")) @@ -492,7 +492,7 @@ function tree_format(lilist::Vector{StackFrame}, counts::Vector{Int}, level::Int ")") else fname = string(li.func) - if !li.from_c && !isnull(li.linfo) + if !li.from_c && li.linfo !== nothing fname = sprint(show_spec_linfo, li) end strs[i] = string(base, diff --git a/test/ambiguous.jl b/test/ambiguous.jl index 7f292a7a0d2a3..2a7aa3c8e1efb 100644 --- a/test/ambiguous.jl +++ b/test/ambiguous.jl @@ -288,6 +288,8 @@ end pop!(need_to_handle_undef_sparam, which(Base.one, Tuple{Type{Union{Missing, T}} where T})) pop!(need_to_handle_undef_sparam, which(Base.oneunit, Tuple{Type{Union{Missing, T}} where T})) pop!(need_to_handle_undef_sparam, which(Base.nonmissingtype, Tuple{Type{Union{Missing, T}} where T})) + pop!(need_to_handle_undef_sparam, which(Base.convert, (Type{Union{Some{T}, Void}} where T, Some))) + pop!(need_to_handle_undef_sparam, which(Base.convert, (Type{Union{T, Void}} where T, Some))) @test need_to_handle_undef_sparam == Set() end end diff --git a/test/broadcast.jl b/test/broadcast.jl index 444b088012772..849d44be7f694 100644 --- a/test/broadcast.jl +++ b/test/broadcast.jl @@ -518,10 +518,10 @@ Base.BroadcastStyle(a2::Broadcast.ArrayStyle{AD2C}, a1::Broadcast.ArrayStyle{AD1 end # broadcast should only "peel off" one container layer -@test get.([Nullable(1), Nullable(2)]) == [1, 2] +@test getindex.([Ref(1), Ref(2)]) == [1, 2] let io = IOBuffer() - broadcast(x -> print(io, x), [Nullable(1.0)]) - @test String(take!(io)) == "Nullable{Float64}(1.0)" + broadcast(x -> print(io, x), [Ref(1.0)]) + @test String(take!(io)) == "Base.RefValue{Float64}(1.0)" end # Test that broadcast's promotion mechanism handles closures accepting more than one argument. @@ -575,9 +575,6 @@ end @test isequal( [Set([1]), Set([2])] .∪ Set([3]), [Set([1, 3]), Set([2, 3])]) - - @test isequal(@inferred(broadcast(foo, "world", Nullable(1))), - Nullable("hello")) end @testset "broadcast resulting in tuples" begin @@ -608,7 +605,7 @@ end # end # Issue #22180 -@test isequal(convert.(Nullable, [1,2]), [Nullable(1), Nullable(2)]) +@test convert.(Any, [1, 2]) == [1, 2] # Issue #24944 let n = 1 diff --git a/test/ccall.jl b/test/ccall.jl index 770f6c0763ba8..1ca337c6ec262 100644 --- a/test/ccall.jl +++ b/test/ccall.jl @@ -1242,8 +1242,8 @@ end @test_throws(UndefVarError(:Something_not_defined_20835), eval(:(f20835(x) = ccall(:fn, Something_not_defined_20835, (Ptr{typeof(x)},), x)))) -@noinline f21104at(::Type{T}) where {T} = ccall(:fn, Void, (Nullable{T},), 0) -@noinline f21104rt(::Type{T}) where {T} = ccall(:fn, Nullable{T}, ()) +@noinline f21104at(::Type{T}) where {T} = ccall(:fn, Void, (Some{T},), Some(0)) +@noinline f21104rt(::Type{T}) where {T} = ccall(:fn, Some{T}, ()) @test code_llvm(DevNull, f21104at, (Type{Float64},)) === nothing @test code_llvm(DevNull, f21104rt, (Type{Float64},)) === nothing @test_throws(ErrorException("ccall: the type of argument 1 doesn't correspond to a C type"), diff --git a/test/choosetests.jl b/test/choosetests.jl index aa8e1ceb31800..b619a00cad52c 100644 --- a/test/choosetests.jl +++ b/test/choosetests.jl @@ -46,7 +46,7 @@ function choosetests(choices = []) "combinatorics", "sysinfo", "env", "rounding", "ranges", "mod2pi", "euler", "show", "lineedit", "replcompletions", "repl", "replutil", "sets", "goto", "llvmcall", "llvmcall2", "grisu", - "nullable", "meta", "stacktraces", "libgit2", "docs", + "some", "meta", "stacktraces", "libgit2", "docs", "markdown", "serialize", "misc", "threads", "enums", "cmdlineargs", "i18n", "libdl", "int", "checked", "bitset", "floatfuncs", "compile", "inline", diff --git a/test/codegen.jl b/test/codegen.jl index 7664b90d29854..2937e80593118 100644 --- a/test/codegen.jl +++ b/test/codegen.jl @@ -169,7 +169,6 @@ breakpoint_ptrstruct(a::RealStruct) = ccall(:jl_breakpoint, Void, (Ref{PtrStruct},), a) if opt_level > 0 - @test !contains(get_llvm(isequal, Tuple{Nullable{BigFloat}, Nullable{BigFloat}}), "%gcframe") @test !contains(get_llvm(pointer_not_safepoint, Tuple{}), "%gcframe") compare_large_struct_ir = get_llvm(compare_large_struct, Tuple{typeof(create_ref_struct())}) @test contains(compare_large_struct_ir, "call i32 @memcmp") diff --git a/test/compile.jl b/test/compile.jl index daafb6091e8c6..e7743eff2718c 100644 --- a/test/compile.jl +++ b/test/compile.jl @@ -128,8 +128,8 @@ try struct Value18343{T, R} pool::Pool18343{R, Value18343{T, R}} end - Base.convert(::Type{Nullable{S}}, ::Value18343{Nullable}) where {S} = 2 - Base.convert(::Type{Nullable{Value18343}}, ::Value18343{Nullable}) = 2 + Base.convert(::Type{Some{S}}, ::Value18343{Some}) where {S} = 2 + Base.convert(::Type{Some{Value18343}}, ::Value18343{Some}) = 2 Base.convert(::Type{Ref}, ::Value18343{T}) where {T} = 3 @@ -252,7 +252,7 @@ try some_method, Tuple{typeof(Base.include), String}, Core.svec(), typemax(UInt)) @test Foo.some_linfo::Core.MethodInstance === some_linfo - PV = Foo.Value18343{Nullable}.body.types[1] + PV = Foo.Value18343{Some}.body.types[1] VR = PV.types[1].parameters[1] @test PV.types[1] === Array{VR,1} @test pointer_from_objref(PV.types[1]) === diff --git a/test/copy.jl b/test/copy.jl index 103ad6c849d81..96b6f9a706455 100644 --- a/test/copy.jl +++ b/test/copy.jl @@ -83,7 +83,13 @@ end end # issue #14027 -@test isnull(deepcopy(Nullable{Array}())) +struct Nullable14027{T} + hasvalue::Bool + value::T + + Nullable14027{T}() where {T} = new(false) +end +@test !deepcopy(Nullable14027{Array}()).hasvalue @testset "issue #15250" begin a1 = Core.svec(1, 2, 3, []) diff --git a/test/core.jl b/test/core.jl index 254ccdd38afb8..d00edb0d35e5a 100644 --- a/test/core.jl +++ b/test/core.jl @@ -3234,17 +3234,21 @@ f11858(Any[Type{Foo11858}, Type{Bar11858}, typeof(g11858)]) @test Bar11858(1).x == 1.0 # issue 11904 +struct Nullable11904{T} + value::T + hasvalue::Bool +end @noinline throw_error() = error() foo11904(x::Int) = x -@inline function foo11904(x::Nullable{S}) where S +@inline function foo11904(x::Nullable11904{S}) where S if isbits(S) - Nullable(foo11904(x.value), x.hasvalue) + Nullable11904(foo11904(x.value), x.hasvalue) else throw_error() end end -@test !isnull(foo11904(Nullable(1))) +@test foo11904(Nullable11904(1, true)).hasvalue # issue 11874 struct Foo11874 @@ -3469,7 +3473,7 @@ call13007(::Type{Array}) = 1 @test length(Base._methods(call13007, Tuple{Type{x} where x<:Array}, 4, typemax(UInt))) == 2 # detecting cycles during type intersection, e.g. #1631 -cycle_in_solve_tvar_constraints(::Type{Nullable{S}}, x::S) where {S} = 0 +cycle_in_solve_tvar_constraints(::Type{Some{S}}, x::S) where {S} = 0 cycle_in_solve_tvar_constraints(::Type{T}, x::Val{T}) where {T} = 1 @test length(methods(cycle_in_solve_tvar_constraints)) == 2 diff --git a/test/libgit2-helpers.jl b/test/libgit2-helpers.jl index d7fc9cb067667..ff43d5524aeca 100644 --- a/test/libgit2-helpers.jl +++ b/test/libgit2-helpers.jl @@ -2,6 +2,7 @@ import Base.LibGit2: AbstractCredential, UserPasswordCredential, SSHCredential, CachedCredentials, CredentialPayload, Payload +using Base: coalesce const DEFAULT_PAYLOAD = CredentialPayload(allow_ssh_agent=false, allow_git_helpers=false) @@ -12,7 +13,7 @@ without having to authenticate against a real server. function credential_loop( valid_credential::AbstractCredential, url::AbstractString, - user::Nullable{<:AbstractString}, + user::Union{AbstractString, Void}, allowed_types::UInt32, payload::CredentialPayload; shred::Bool=true) @@ -28,11 +29,12 @@ function credential_loop( err = Cint(0) while err == 0 err = ccall(cb, Cint, (Ptr{Ptr{Void}}, Cstring, Cstring, Cuint, Any), - libgitcred_ptr_ptr, url, get(user, C_NULL), allowed_types, payload) + libgitcred_ptr_ptr, url, coalesce(user, C_NULL), + allowed_types, payload) num_authentications += 1 # Check if the callback provided us with valid credentials - if !isnull(payload.credential) && get(payload.credential) == valid_credential + if payload.credential !== nothing && payload.credential == valid_credential LibGit2.approve(payload, shred=shred) break end @@ -60,7 +62,7 @@ end function credential_loop( valid_credential::UserPasswordCredential, url::AbstractString, - user::Nullable{<:AbstractString}=Nullable{String}(), + user::Union{AbstractString, Void}=nothing, payload::CredentialPayload=DEFAULT_PAYLOAD; shred::Bool=true) credential_loop(valid_credential, url, user, 0x000001, payload, shred=shred) @@ -69,17 +71,8 @@ end function credential_loop( valid_credential::SSHCredential, url::AbstractString, - user::Nullable{<:AbstractString}=Nullable{String}(), + user::Union{AbstractString, Void}=nothing, payload::CredentialPayload=DEFAULT_PAYLOAD; shred::Bool=true) credential_loop(valid_credential, url, user, 0x000046, payload, shred=shred) -end - -function credential_loop( - valid_credential::AbstractCredential, - url::AbstractString, - user::AbstractString, - payload::CredentialPayload=DEFAULT_PAYLOAD; - shred::Bool=true) - credential_loop(valid_credential, url, Nullable(user), payload, shred=shred) -end +end \ No newline at end of file diff --git a/test/libgit2.jl b/test/libgit2.jl index b008bd3ed1480..1235cb6d4c88f 100644 --- a/test/libgit2.jl +++ b/test/libgit2.jl @@ -371,7 +371,7 @@ end @test cred.use_http_path cred.use_http_path = false - @test get(cred.path, "") == "dir/file" + @test cred.path == "dir/file" @test sprint(write, cred) == expected end @@ -752,7 +752,7 @@ mktempdir() do dir @test LibGit2.name(brref) == "refs/heads/master" @test LibGit2.shortname(brref) == master_branch @test LibGit2.ishead(brref) - @test isnull(LibGit2.upstream(brref)) + @test LibGit2.upstream(brref) === nothing # showing the GitReference to this branch show_strs = split(sprint(show, brref), "\n") @@ -766,19 +766,19 @@ mktempdir() do dir # create a branch *without* setting its tip as HEAD LibGit2.branch!(repo, test_branch, string(commit_oid1), set_head=false) # null because we are looking for a REMOTE branch - @test isnull(LibGit2.lookup_branch(repo, test_branch, true)) - # not null because we are now looking for a LOCAL branch - LibGit2.with(Base.get(LibGit2.lookup_branch(repo, test_branch, false))) do tbref + @test LibGit2.lookup_branch(repo, test_branch, true) === nothing + # not nothing because we are now looking for a LOCAL branch + LibGit2.with(LibGit2.lookup_branch(repo, test_branch, false)) do tbref @test LibGit2.shortname(tbref) == test_branch - @test isnull(LibGit2.upstream(tbref)) + @test LibGit2.upstream(tbref) === nothing end - @test isnull(LibGit2.lookup_branch(repo, test_branch2, true)) + @test LibGit2.lookup_branch(repo, test_branch2, true) === nothing # test deleting the branch LibGit2.branch!(repo, test_branch2; set_head=false) - LibGit2.with(Base.get(LibGit2.lookup_branch(repo, test_branch2, false))) do tbref + LibGit2.with(LibGit2.lookup_branch(repo, test_branch2, false)) do tbref @test LibGit2.shortname(tbref) == test_branch2 LibGit2.delete_branch(tbref) - @test isnull(LibGit2.lookup_branch(repo, test_branch2, true)) + @test LibGit2.lookup_branch(repo, test_branch2, true) === nothing end end branches = map(b->LibGit2.shortname(b[1]), LibGit2.GitBranchIter(repo)) @@ -1278,15 +1278,15 @@ mktempdir() do dir # check index for file LibGit2.with(LibGit2.GitIndex(repo)) do idx i = find(test_file, idx) - @test !isnull(i) - idx_entry = idx[get(i)] + @test i !== nothing + idx_entry = idx[i] @test idx_entry !== nothing idx_entry_str = sprint(show, idx_entry) @test idx_entry_str == "IndexEntry($(string(idx_entry.id)))" @test LibGit2.stage(idx_entry) == 0 i = find("zzz", idx) - @test isnull(i) + @test i === nothing idx_str = sprint(show, idx) @test idx_str == "GitIndex:\nRepository: $(LibGit2.repository(idx))\nNumber of elements: 1\n" @@ -1300,12 +1300,12 @@ mktempdir() do dir # check non-existent file status st = LibGit2.status(repo, "XYZ") - @test isnull(st) + @test st === nothing # check file status st = LibGit2.status(repo, test_file) - @test !isnull(st) - @test LibGit2.isset(get(st), LibGit2.Consts.STATUS_CURRENT) + @test st !== nothing + @test LibGit2.isset(st, LibGit2.Consts.STATUS_CURRENT) # modify file open(joinpath(test_repo, test_file), "a") do io @@ -1314,28 +1314,28 @@ mktempdir() do dir # file modified but not staged st_mod = LibGit2.status(repo, test_file) - @test !LibGit2.isset(get(st_mod), LibGit2.Consts.STATUS_INDEX_MODIFIED) - @test LibGit2.isset(get(st_mod), LibGit2.Consts.STATUS_WT_MODIFIED) + @test !LibGit2.isset(st_mod, LibGit2.Consts.STATUS_INDEX_MODIFIED) + @test LibGit2.isset(st_mod, LibGit2.Consts.STATUS_WT_MODIFIED) # stage file LibGit2.add!(repo, test_file) # modified file staged st_stg = LibGit2.status(repo, test_file) - @test LibGit2.isset(get(st_stg), LibGit2.Consts.STATUS_INDEX_MODIFIED) - @test !LibGit2.isset(get(st_stg), LibGit2.Consts.STATUS_WT_MODIFIED) + @test LibGit2.isset(st_stg, LibGit2.Consts.STATUS_INDEX_MODIFIED) + @test !LibGit2.isset(st_stg, LibGit2.Consts.STATUS_WT_MODIFIED) # try to unstage to unknown commit @test_throws LibGit2.Error.GitError LibGit2.reset!(repo, "XYZ", test_file) # status should not change st_new = LibGit2.status(repo, test_file) - @test get(st_new) == get(st_stg) + @test st_new == st_stg # try to unstage to HEAD new_head = LibGit2.reset!(repo, LibGit2.Consts.HEAD_FILE, test_file) st_uns = LibGit2.status(repo, test_file) - @test get(st_uns) == get(st_mod) + @test st_uns == st_mod # reset repo @test_throws LibGit2.Error.GitError LibGit2.reset!(repo, LibGit2.GitHash(), LibGit2.Consts.RESET_HARD) @@ -1353,38 +1353,38 @@ mktempdir() do dir remote_name = "test" url = "https://test.com/repo" - @test isnull(LibGit2.lookup_remote(repo, remote_name)) + @test LibGit2.lookup_remote(repo, remote_name) === nothing for r in (repo, path) # Set just the fetch URL LibGit2.set_remote_fetch_url(r, remote_name, url) - remote = get(LibGit2.lookup_remote(repo, remote_name)) + remote = LibGit2.lookup_remote(repo, remote_name) @test LibGit2.name(remote) == remote_name @test LibGit2.url(remote) == url @test LibGit2.push_url(remote) == "" LibGit2.remote_delete(repo, remote_name) - @test isnull(LibGit2.lookup_remote(repo, remote_name)) + @test LibGit2.lookup_remote(repo, remote_name) === nothing # Set just the push URL LibGit2.set_remote_push_url(r, remote_name, url) - remote = get(LibGit2.lookup_remote(repo, remote_name)) + remote = LibGit2.lookup_remote(repo, remote_name) @test LibGit2.name(remote) == remote_name @test LibGit2.url(remote) == "" @test LibGit2.push_url(remote) == url LibGit2.remote_delete(repo, remote_name) - @test isnull(LibGit2.lookup_remote(repo, remote_name)) + @test LibGit2.lookup_remote(repo, remote_name) === nothing # Set the fetch and push URL LibGit2.set_remote_url(r, remote_name, url) - remote = get(LibGit2.lookup_remote(repo, remote_name)) + remote = LibGit2.lookup_remote(repo, remote_name) @test LibGit2.name(remote) == remote_name @test LibGit2.url(remote) == url @test LibGit2.push_url(remote) == url LibGit2.remote_delete(repo, remote_name) - @test isnull(LibGit2.lookup_remote(repo, remote_name)) + @test LibGit2.lookup_remote(repo, remote_name) === nothing end # Invalid remote name @test_throws LibGit2.GitError LibGit2.set_remote_url(repo, "", url) @@ -1569,19 +1569,18 @@ mktempdir() do dir # No credential settings in configuration. cred = LibGit2.GitCredential("https", "github.com") username = LibGit2.default_username(cfg, cred) - @test isnull(username) + @test username === nothing # Add a credential setting for a specific for a URL LibGit2.set!(cfg, "credential.https://github.com.username", "foo") cred = LibGit2.GitCredential("https", "github.com") username = LibGit2.default_username(cfg, cred) - @test !isnull(username) - @test get(username) == "foo" + @test username == "foo" cred = LibGit2.GitCredential("https", "mygithost") username = LibGit2.default_username(cfg, cred) - @test isnull(username) + @test username === nothing # Add a global credential setting after the URL specific setting. The first # setting to match will be the one that is used. @@ -1589,13 +1588,11 @@ mktempdir() do dir cred = LibGit2.GitCredential("https", "github.com") username = LibGit2.default_username(cfg, cred) - @test !isnull(username) - @test get(username) == "foo" + @test username == "foo" cred = LibGit2.GitCredential("https", "mygithost") username = LibGit2.default_username(cfg, cred) - @test !isnull(username) - @test get(username) == "bar" + @test username == "bar" end end @@ -1613,13 +1610,11 @@ mktempdir() do dir cred = LibGit2.GitCredential("https", "github.com") username = LibGit2.default_username(cfg, cred) - @test !isnull(username) - @test get(username) == "" + @test username == "" cred = LibGit2.GitCredential("https", "mygithost", "path") username = LibGit2.default_username(cfg, cred) - @test !isnull(username) - @test get(username) == "name" + @test username == "name" end end end @@ -1753,7 +1748,7 @@ mktempdir() do dir function without_path(cred) c = deepcopy(cred) - c.path = Nullable() + c.path = nothing c end @@ -1826,7 +1821,7 @@ mktempdir() do dir url *= "github.com:test/package.jl" quote include($LIBGIT2_HELPER_PATH) - credential_loop($cred, $url, Nullable{String}($username)) + credential_loop($cred, $url, $username) end end @@ -2124,7 +2119,7 @@ mktempdir() do dir include($LIBGIT2_HELPER_PATH) payload = CredentialPayload(allow_prompt=false, allow_ssh_agent=true, allow_git_helpers=false) - credential_loop($valid_cred, $url, Nullable{String}($username), payload) + credential_loop($valid_cred, $url, $username, payload) end end @@ -2187,8 +2182,8 @@ mktempdir() do dir err, auth_attempts, p = challenge_prompt(ex, []) @test err == git_ok @test auth_attempts == 1 - @test get(p.credential).prvkey == default_key - @test get(p.credential).pubkey == default_key * ".pub" + @test p.credential.prvkey == default_key + @test p.credential.pubkey == default_key * ".pub" # Confirm the private key if any other prompting is required ex = gen_ex(valid_p_cred) @@ -2230,7 +2225,7 @@ mktempdir() do dir err, auth_attempts, p = challenge_prompt(ssh_ex, challenges) @test err == git_ok @test auth_attempts == 1 - @test get(p.credential).prvkey == abspath(valid_key) + @test p.credential.prvkey == abspath(valid_key) end withenv("SSH_KEY_PATH" => valid_key, @@ -2246,7 +2241,7 @@ mktempdir() do dir err, auth_attempts, p = challenge_prompt(ssh_ex, challenges) @test err == git_ok @test auth_attempts == 2 - @test get(p.credential).pubkey == abspath(valid_key * ".pub") + @test p.credential.pubkey == abspath(valid_key * ".pub") end end @@ -2277,16 +2272,16 @@ mktempdir() do dir err, auth_attempts, p = challenge_prompt(ex, []) @test err == git_ok @test auth_attempts == 1 - @test get(p.explicit) == valid_cred - @test get(p.credential) != valid_cred + @test p.explicit == valid_cred + @test p.credential != valid_cred # Explicitly provided credential is incorrect ex = gen_ex(invalid_cred, allow_prompt=false, allow_ssh_agent=false) err, auth_attempts, p = challenge_prompt(ex, []) @test err == exhausted_error @test auth_attempts == 3 - @test get(p.explicit) == invalid_cred - @test get(p.credential) != invalid_cred + @test p.explicit == invalid_cred + @test p.credential != invalid_cred end @testset "HTTPS explicit credentials" begin @@ -2309,16 +2304,16 @@ mktempdir() do dir err, auth_attempts, p = challenge_prompt(ex, []) @test err == git_ok @test auth_attempts == 1 - @test get(p.explicit) == valid_cred - @test get(p.credential) != valid_cred + @test p.explicit == valid_cred + @test p.credential != valid_cred # Explicitly provided credential is incorrect ex = gen_ex(invalid_cred, allow_prompt=false) err, auth_attempts, p = challenge_prompt(ex, []) @test err == exhausted_error @test auth_attempts == 2 - @test get(p.explicit) == invalid_cred - @test get(p.credential) != invalid_cred + @test p.explicit == invalid_cred + @test p.credential != invalid_cred end @testset "Cached credentials" begin @@ -2358,12 +2353,12 @@ mktempdir() do dir "Password for 'https://$valid_username@github.com':" => "$valid_password\n", ] err, auth_attempts, p = challenge_prompt(ex, challenges) - cache = get(p.cache) + cache = p.cache @test err == git_ok @test auth_attempts == 1 @test typeof(cache) == LibGit2.CachedCredentials @test cache.cred == Dict(cred_id => valid_cred) - @test get(p.credential) == valid_cred + @test p.credential == valid_cred # Replace a credential in the cache ex = gen_ex(cached_cred=invalid_cred) @@ -2372,12 +2367,12 @@ mktempdir() do dir "Password for 'https://$valid_username@github.com':" => "$valid_password\n", ] err, auth_attempts, p = challenge_prompt(ex, challenges) - cache = get(p.cache) + cache = p.cache @test err == git_ok @test auth_attempts == 2 @test typeof(cache) == LibGit2.CachedCredentials @test cache.cred == Dict(cred_id => valid_cred) - @test get(p.credential) == valid_cred + @test p.credential == valid_cred # Canceling a credential request should leave the cache unmodified ex = gen_ex(cached_cred=invalid_cred) @@ -2387,22 +2382,22 @@ mktempdir() do dir "Username for 'https://github.com' [foo]:" => "\x04", ] err, auth_attempts, p = challenge_prompt(ex, challenges) - cache = get(p.cache) + cache = p.cache @test err == abort_prompt @test auth_attempts == 3 @test typeof(cache) == LibGit2.CachedCredentials @test cache.cred == Dict(cred_id => invalid_cred) - @test get(p.credential) != invalid_cred + @test p.credential != invalid_cred # An EAUTH error should remove credentials from the cache ex = gen_ex(cached_cred=invalid_cred, allow_prompt=false) err, auth_attempts, p = challenge_prompt(ex, []) - cache = get(p.cache) + cache = p.cache @test err == exhausted_error @test auth_attempts == 2 @test typeof(cache) == LibGit2.CachedCredentials @test cache.cred == Dict() - @test get(p.credential) != invalid_cred + @test p.credential != invalid_cred end @testset "HTTPS git helper username" begin @@ -2421,10 +2416,10 @@ mktempdir() do dir https_ex = quote include($LIBGIT2_HELPER_PATH) LibGit2.with(LibGit2.GitConfig($config_path, LibGit2.Consts.CONFIG_LEVEL_APP)) do cfg - payload = CredentialPayload(Nullable{AbstractCredential}(), - Nullable{CachedCredentials}(), cfg, + payload = CredentialPayload(nothing, + nothing, cfg, allow_git_helpers=true) - credential_loop($valid_cred, $url, Nullable{String}(), payload, shred=false) + credential_loop($valid_cred, $url, nothing, payload, shred=false) end end @@ -2438,7 +2433,7 @@ mktempdir() do dir @test auth_attempts == 1 # Verify credential wasn't accidentally zeroed (#24731) - @test get(p.credential) == valid_cred + @test p.credential == valid_cred end @testset "Incompatible explicit credentials" begin @@ -2448,15 +2443,15 @@ mktempdir() do dir include($LIBGIT2_HELPER_PATH) payload = CredentialPayload($valid_cred, allow_ssh_agent=false, allow_git_helpers=false) - credential_loop($valid_cred, "ssh://github.com/repo", Nullable(""), + credential_loop($valid_cred, "ssh://github.com/repo", "", Cuint(LibGit2.Consts.CREDTYPE_SSH_KEY), payload) end err, auth_attempts, p = challenge_prompt(expect_ssh_ex, []) @test err == incompatible_error @test auth_attempts == 1 - @test get(p.explicit) == valid_cred - @test get(p.credential) != valid_cred + @test p.explicit == valid_cred + @test p.credential != valid_cred # User provides a SSH credential where a user/password credential is required. @@ -2465,15 +2460,15 @@ mktempdir() do dir include($LIBGIT2_HELPER_PATH) payload = CredentialPayload($valid_cred, allow_ssh_agent=false, allow_git_helpers=false) - credential_loop($valid_cred, "https://github.com/repo", Nullable(""), + credential_loop($valid_cred, "https://github.com/repo", "", Cuint(LibGit2.Consts.CREDTYPE_USERPASS_PLAINTEXT), payload) end err, auth_attempts, p = challenge_prompt(expect_https_ex, []) @test err == incompatible_error @test auth_attempts == 1 - @test get(p.explicit) == valid_cred - @test get(p.credential) != valid_cred + @test p.explicit == valid_cred + @test p.credential != valid_cred end # A hypothetical scenario where the the allowed authentication can either be @@ -2488,7 +2483,7 @@ mktempdir() do dir valid_cred = LibGit2.UserPasswordCredential("foo", "bar") payload = CredentialPayload(valid_cred, allow_ssh_agent=false, allow_git_helpers=false) - credential_loop(valid_cred, "foo://github.com/repo", Nullable(""), + credential_loop(valid_cred, "foo://github.com/repo", "", $allowed_types, payload) end @@ -2510,7 +2505,7 @@ mktempdir() do dir ex = quote include($LIBGIT2_HELPER_PATH) valid_cred = LibGit2.UserPasswordCredential($valid_username, $valid_password) - user = Nullable{String}() + user = nothing payload = CredentialPayload(allow_git_helpers=false) first_result = credential_loop(valid_cred, $(urls[1]), user, payload) LibGit2.reset!(payload) diff --git a/test/misc.jl b/test/misc.jl index 8ee89c78102ee..84199a6ce063a 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -683,10 +683,10 @@ if Bool(parse(Int,(get(ENV, "JULIA_TESTFULL", "0")))) Demo_20254(string.(arr)) end - _unsafe_get_19433(x::NTuple{1}) = (unsafe_get(x[1]),) - _unsafe_get_19433(xs::Vararg) = (unsafe_get(xs[1]), _unsafe_get_19433(xs[2:end])...) + _get(x::NTuple{1}) = (get(x[1]),) + _get_19433(xs::Vararg) = (get(xs[1]), _get_19433(xs[2:end])...) - f_19433(f_19433, xs...) = f_19433(_unsafe_get_19433(xs)...) + f_19433(f_19433, xs...) = f_19433(_get_19433(xs)...) @testset "test this does not crash, issue #19433 and #20254" begin @test_throws StackOverflowError Demo_20254() diff --git a/test/missing.jl b/test/missing.jl index dcf2d37daf821..35a0e385758e9 100644 --- a/test/missing.jl +++ b/test/missing.jl @@ -13,7 +13,11 @@ end @testset "convert" begin @test convert(Union{Int, Missing}, 1) === 1 @test convert(Union{Int, Missing}, 1.0) === 1 + @test convert(Union{Void, Missing}, missing) === missing + @test convert(Union{Void, Missing}, nothing) === nothing + @test_throws MethodError convert(Missing, 1) + @test_throws MethodError convert(Union{Void, Missing}, 1) @test_throws MethodError convert(Union{Int, Missing}, "a") end diff --git a/test/nullable.jl b/test/nullable.jl deleted file mode 100644 index 2edc67cdc3cae..0000000000000 --- a/test/nullable.jl +++ /dev/null @@ -1,520 +0,0 @@ -# This file is a part of Julia. License is MIT: https://julialang.org/license - -# "is a null with type T", curried on 2nd argument -isnull_oftype(x::Nullable, T::Type) = eltype(x) == T && isnull(x) -isnull_oftype(T::Type) = x -> isnull_oftype(x, T) - -# return true if nullables (or arrays of nullables) have the same type, -# nullity, and value (if they are non-null) -istypeequal(x::Nullable, y::Nullable) = - typeof(x) == typeof(y) && isnull(filter(!, x .== y)) -istypeequal(x::AbstractArray, y::AbstractArray) = - length(x) == length(y) && all(xy -> istypeequal(xy...), zip(x, y)) - -types = [ - Bool, - Float16, - Float32, - Float64, - Int128, - Int16, - Int32, - Int64, - Int8, - UInt16, - UInt32, - UInt64, - UInt8, -] - -# Nullable{T}() = new(true) -for T in types - x = Nullable{T}() - @test x.hasvalue === false - @test isa(x.value, T) - @test eltype(Nullable{T}) === T - @test eltype(x) === T -end - -# Nullable{T}(value::T) = new(false, value) -for T in types - x = Nullable{T}(zero(T)) - @test x.hasvalue === true - @test isa(x.value, T) - @test x.value === zero(T) - @test eltype(x) === T - - x = Nullable{T}(one(T)) - @test x.hasvalue === true - @test isa(x.value, T) - @test x.value === one(T) - @test eltype(x) === T -end - -# Nullable{T}(value::T, hasvalue::Bool) = new(hasvalue, value) -for T in types - x = Nullable{T}(zero(T), true) - @test x.hasvalue === true - @test isa(x.value, T) - @test x.value === zero(T) - @test eltype(x) === T - - x = Nullable{T}(zero(T), false) - @test x.hasvalue === false - @test isa(x.value, T) - @test eltype(Nullable{T}) === T - @test eltype(x) === T -end - - -# struct NullException <: Exception -@test isa(NullException(), NullException) -@test_throws NullException throw(NullException()) - -# Nullable{T}(value::T) = Nullable{T}(value) -for T in types - v = zero(T) - x = Nullable(v) - @test x.hasvalue === true - @test isa(x.value, T) - @test x.value === v - - v = one(T) - x = Nullable(v) - @test x.hasvalue === true - @test isa(x.value, T) - @test x.value === v -end - -# show{T}(io::IO, x::Nullable{T}) -io1 = IOBuffer() -io2 = IOBuffer() -for (i, T) in enumerate(types) - x1 = Nullable{T}() - x2 = Nullable(zero(T)) - x3 = Nullable(one(T)) - show(io1, x1) - @test String(take!(io1)) == @sprintf("Nullable{%s}()", T) - show(io1, x2) - showcompact(io2, get(x2)) - @test String(take!(io1)) == @sprintf("Nullable{%s}(%s)", T, String(take!(io2))) - show(io1, x3) - showcompact(io2, get(x3)) - @test String(take!(io1)) == @sprintf("Nullable{%s}(%s)", T, String(take!(io2))) -end - -module NullableTestEnum -import Test -# For curmod_* -include("testenv.jl") -io = IOBuffer() -@enum TestEnum a b -show(io, Nullable(a)) -Test.@test String(take!(io)) == "Nullable{$(curmod_prefix)TestEnum}(a)" -end - -# showcompact(io::IO, x::Nullable) -io1 = IOBuffer() -io2 = IOBuffer() -for (i, T) in enumerate(types) - x1 = Nullable{T}() - x2 = Nullable(zero(T)) - x3 = Nullable(one(T)) - showcompact(io1, x1) - @test String(take!(io1)) == "#NULL" - showcompact(io1, x2) - showcompact(io2, get(x2)) - @test String(take!(io1)) == String(take!(io2)) - showcompact(io1, x3) - showcompact(io2, get(x3)) - @test String(take!(io1)) == String(take!(io2)) - - a1 = [x2] - showcompact(io1, a1) - showcompact(io2, x2) - @test String(take!(io1)) == - @sprintf("Nullable{%s}[%s]", string(T), String(take!(io2))) -end - -# get(x::Nullable) -for T in types - x1 = Nullable{T}() - x2 = Nullable(zero(T)) - x3 = Nullable(one(T)) - - @test_throws NullException get(x1) - @test get(x2) === zero(T) - @test get(x3) === one(T) -end - -@test_throws NullException get(Nullable()) - -# get{S, T}(x::Nullable{S}, y::T) -for T in types - x0 = Nullable() - x1 = Nullable{T}() - x2 = Nullable(zero(T)) - x3 = Nullable(one(T)) - - @test get(x0, zero(T)) === zero(T) - @test get(x0, one(T)) === one(T) - @test get(x1, zero(T)) === zero(T) - @test get(x1, one(T)) === one(T) - @test get(x2, one(T)) === zero(T) - @test get(x3, zero(T)) === one(T) -end - -for T in types - # unsafe_get(x::Nullable) - x1 = Nullable{T}() - x2 = Nullable(zero(T)) - x3 = Nullable(one(T)) - a = rand(T) - x4 = Nullable(a) - - @test isa(unsafe_get(x1), T) - @test unsafe_get(x2) === zero(T) - @test unsafe_get(x3) === one(T) - @test unsafe_get(x4) === a - - # unsafe_get(x) - x2 = zero(T) - x3 = one(T) - x4 = rand(T) - - @test unsafe_get(x2) === zero(T) - @test unsafe_get(x3) === one(T) - @test unsafe_get(x4) === x4 -end - -@test_throws UndefRefError unsafe_get(Nullable()) -@test_throws UndefRefError unsafe_get(Nullable{String}()) -@test_throws UndefRefError unsafe_get(Nullable{Array}()) - -for T in types - # isnull(x::Nullable) - x1 = Nullable{T}() - x2 = Nullable(zero(T)) - x3 = Nullable(one(T)) - - @test isnull(x1) === true - @test isnull(x2) === false - @test isnull(x3) === false - - # isnull(x) - x1 = zero(T) - x2 = one(T) - x3 = rand(T) - - @test isnull(x1) === false - @test isnull(x2) === false - @test isnull(x3) === false -end - -@test isnull(Nullable()) - -# function =={S, T}(x::Nullable{S}, y::Nullable{T}) -for T in types - x0 = Nullable() - x1 = Nullable{T}() - x2 = Nullable{T}() - x3 = Nullable(zero(T)) - x4 = Nullable(one(T)) - - @test_throws NullException (x0 == x1) - @test_throws NullException (x0 == x2) - @test_throws NullException (x0 == x3) - @test_throws NullException (x0 == x4) - - @test_throws NullException (x1 == x1) - @test_throws NullException (x1 == x2) - @test_throws NullException (x1 == x3) - @test_throws NullException (x1 == x4) - - @test_throws NullException (x2 == x1) - @test_throws NullException (x2 == x2) - @test_throws NullException (x2 == x3) - @test_throws NullException (x2 == x4) - - @test_throws NullException (x3 == x1) - @test_throws NullException (x3 == x2) - @test_throws NullException (x3 == x3) - @test_throws NullException (x3 == x4) - - @test_throws NullException (x4 == x1) - @test_throws NullException (x4 == x2) - @test_throws NullException (x4 == x3) - @test_throws NullException (x4 == x4) -end - -# function hash(x::Nullable, h::UInt) -for T in types - x0 = Nullable() - x1 = Nullable{T}() - x2 = Nullable{T}() - x3 = Nullable(zero(T)) - x4 = Nullable(one(T)) - - @test isa(hash(x0), UInt) - @test isa(hash(x1), UInt) - @test isa(hash(x2), UInt) - @test isa(hash(x3), UInt) - @test isa(hash(x4), UInt) - - @test hash(x0) == hash(x2) - @test hash(x0) != hash(x3) - @test hash(x0) != hash(x4) - @test hash(x1) == hash(x2) - @test hash(x1) != hash(x3) - @test hash(x1) != hash(x4) - @test hash(x2) != hash(x3) - @test hash(x2) != hash(x4) - @test hash(x3) != hash(x4) -end - -mutable struct TestNType{T} - v::Nullable{T} -end - -for T in types - x1 = TestNType{T}(Nullable{T}()) - @test isnull(x1.v) - x1.v = one(T) - @test !isnull(x1.v) - @test get(x1.v, one(T)) === one(T) -end - -# Operators -TestTypes = [[T.parameters[1] for T in Base.uniontypes(Base.NullSafeTypes)]; - [BigInt, BigFloat, - Complex{Int}, Complex{Float64}, Complex{BigFloat}, - Rational{Int}, Rational{BigInt}]] -for S in TestTypes, T in TestTypes - u0 = zero(S) - u1 = one(S) - if S <: AbstractFloat - u2 = S(NaN) - elseif S <: Complex && S.parameters[1] <: AbstractFloat - u2 = S(NaN, NaN) - else - u2 = u1 - end - - v0 = zero(T) - v1 = one(T) - if T <: AbstractFloat - v2 = T(NaN) - elseif T <: Complex && T.parameters[1] <: AbstractFloat - v2 = T(NaN, NaN) - else - v2 = v1 - end - - for u in (u0, u1, u2), v in (v0, v1, v2) - # function isequal(x::Nullable, y::Nullable) - @test isequal(Nullable(u), Nullable(v)) === isequal(u, v) - @test isequal(Nullable(u), Nullable(u)) === true - @test isequal(Nullable(v), Nullable(v)) === true - - @test isequal(Nullable(u), Nullable(v, false)) === false - @test isequal(Nullable(u, false), Nullable(v)) === false - @test isequal(Nullable(u, false), Nullable(v, false)) === true - - @test isequal(Nullable(u), Nullable{T}()) === false - @test isequal(Nullable{S}(), Nullable(v)) === false - @test isequal(Nullable{S}(), Nullable{T}()) === true - - @test isequal(Nullable(u), Nullable()) === false - @test isequal(Nullable(), Nullable(v)) === false - @test isequal(Nullable{S}(), Nullable()) === true - @test isequal(Nullable(), Nullable{T}()) === true - @test isequal(Nullable(), Nullable()) === true - - # function isless(x::Nullable, y::Nullable) - if S <: Real && T <: Real - @test isless(Nullable(u), Nullable(v)) === isless(u, v) - @test isless(Nullable(u), Nullable(u)) === false - @test isless(Nullable(v), Nullable(v)) === false - - @test isless(Nullable(u), Nullable(v, false)) === true - @test isless(Nullable(u, false), Nullable(v)) === false - @test isless(Nullable(u, false), Nullable(v, false)) === false - - @test isless(Nullable(u), Nullable{T}()) === true - @test isless(Nullable{S}(), Nullable(v)) === false - @test isless(Nullable{S}(), Nullable{T}()) === false - - @test isless(Nullable(u), Nullable()) === true - @test isless(Nullable(), Nullable(v)) === false - @test isless(Nullable{S}(), Nullable()) === false - @test isless(Nullable(), Nullable{T}()) === false - @test isless(Nullable(), Nullable()) === false - end - end -end - -# issue #9462 -for T in types - @test isa(convert(Nullable{Number}, Nullable(one(T))), Nullable{Number}) - @test isa(convert(Nullable{Number}, one(T)), Nullable{Number}) - @test isa(convert(Nullable{T}, one(T)), Nullable{T}) - @test isa(convert(Nullable{Any}, Nullable(one(T))), Nullable{Any}) - @test isa(convert(Nullable{Any}, one(T)), Nullable{Any}) - - # one(T) is convertible to every type in types - # let's test that with Nullables - for S in types - @test isa(convert(Nullable{T}, one(S)), Nullable{T}) - end -end - -@test isnull(convert(Nullable, nothing)) -@test isnull(convert(Nullable{Int}, nothing)) -@test isa(convert(Nullable{Int}, nothing), Nullable{Int}) - -@test convert(Nullable, 1) === Nullable(1) -@test convert(Nullable, Nullable(1)) === Nullable(1) -@test isequal(convert(Nullable, "a"), Nullable("a")) -@test isequal(convert(Nullable, Nullable("a")), Nullable("a")) - -using Dates -@test promote_type(Nullable{Int}, Int) === Nullable{Int} -@test promote_type(Nullable{Union{}}, Int) === Nullable{Int} -@test promote_type(Nullable{Float64}, Nullable{Int}) === Nullable{Float64} -@test promote_type(Nullable{Union{}}, Nullable{Int}) === Nullable{Int} -@test promote_type(Nullable{Date}, Nullable{DateTime}) === Nullable{DateTime} - -@test Base.promote_op(+, Nullable{Int}, Nullable{Int}) == Nullable{Int} -@test Base.promote_op(-, Nullable{Int}, Nullable{Int}) == Nullable{Int} -@test Base.promote_op(+, Nullable{Float64}, Nullable{Int}) == Nullable{Float64} -@test Base.promote_op(-, Nullable{Float64}, Nullable{Int}) == Nullable{Float64} -@test Base.promote_op(-, Nullable{DateTime}, Nullable{DateTime}) == Nullable{Dates.Millisecond} - -# tests for istypeequal (which uses filter, broadcast) -@test istypeequal(Nullable(0), Nullable(0)) -@test !istypeequal(Nullable(0), Nullable(0.0)) -@test !istypeequal(Nullable(0), Nullable(1)) -@test !istypeequal(Nullable(0), Nullable(1.0)) -@test istypeequal([Nullable(0), Nullable(1)], [Nullable(0), Nullable(1)]) -@test istypeequal([Nullable(0), Nullable(1)], Any[Nullable(0), Nullable(1)]) -@test !istypeequal([Nullable(0), Nullable(1)], Any[Nullable(0.0), Nullable(1)]) -@test !istypeequal([Nullable(0), Nullable(1)], [Nullable(0), Nullable(2)]) -@test !istypeequal([Nullable(0), Nullable(1)], - [Nullable(0), Nullable(1), Nullable(2)]) - -# filter -for p in (_ -> true, _ -> false) - @test @inferred(filter(p, Nullable())) |> isnull_oftype(Union{}) - @test @inferred(filter(p, Nullable{Int}())) |> isnull_oftype(Int) -end -@test @inferred(filter(_ -> true, Nullable(85))) === Nullable(85) -@test @inferred(filter(_ -> false, Nullable(85))) |> isnull_oftype(Int) -@test @inferred(filter(x -> x > 0, Nullable(85))) === Nullable(85) -@test @inferred(filter(x -> x < 0, Nullable(85))) |> isnull_oftype(Int) -@test get(@inferred(filter(x -> length(x) > 2, Nullable("test")))) == "test" -@test @inferred(filter(x -> length(x) > 5, Nullable("test"))) |> - isnull_oftype(String) - -# map -sqr(x) = x^2 -@test @inferred(map(sqr, Nullable())) |> isnull_oftype(Union{}) -@test @inferred(map(sqr, Nullable{Int}())) |> isnull_oftype(Int) -@test @inferred(map(sqr, Nullable(2))) === Nullable(4) -@test @inferred(map(+, Nullable(0.0))) === Nullable(0.0) -@test @inferred(map(+, Nullable(3.0, false)))=== Nullable(3.0, false) -@test @inferred(map(-, Nullable(1.0))) === Nullable(-1.0) -@test @inferred(map(-, Nullable{Float64}())) |> isnull_oftype(Float64) -@test @inferred(map(sin, Nullable(1))) === Nullable(sin(1)) -@test @inferred(map(sin, Nullable{Int}())) |> isnull_oftype(Float64) - -# should not throw if function wouldn't be called -@test map(x -> x ? 0 : 0.0, Nullable()) |> isnull_oftype(Union{}) -@test map(x -> x ? 0 : 0.0, Nullable(true)) === Nullable(0) -@test map(x -> x ? 0 : 0.0, Nullable(false)) === Nullable(0.0) -@test map(x -> x ? 0 : 0.0, Nullable{Bool}()) |> isnull_oftype(Union{}) - -# broadcast and elementwise -@test sin.(Nullable(0.0)) === Nullable(0.0) -@test sin.(Nullable{Float64}()) |> isnull_oftype(Float64) -@test @inferred(broadcast(sin, Nullable(0.0))) === Nullable(0.0) -@test @inferred(broadcast(sin, Nullable{Float64}())) |> isnull_oftype(Float64) - -@test Nullable(8) .+ Nullable(10) === Nullable(18) -@test Nullable(8) .- Nullable(10) === Nullable(-2) -@test Nullable(8) .+ Nullable{Int}() |> isnull_oftype(Int) -@test Nullable{Int}() .- Nullable(10) |> isnull_oftype(Int) - -@test @inferred(broadcast(log, 10, Nullable(1.0))) === - Nullable(0.0) -@test @inferred(broadcast(log, 10, Nullable{Float64}())) |> - isnull_oftype(Float64) -@test @inferred(broadcast(log, Nullable(10), Nullable(1.0))) === - Nullable(0.0) -@test @inferred(broadcast(log, Nullable(10), Nullable{Float64}())) |> - isnull_oftype(Float64) - -@test Nullable(2) .^ Nullable(4) === Nullable(16) -@test Nullable(2) .^ Nullable{Int}() |> isnull_oftype(Int) - -# multi-arg broadcast -@test (Nullable(1) .+ Nullable(1) .+ Nullable(1) .+ Nullable(1) .+ Nullable(1) .+ - Nullable(1) === Nullable(6)) -@test (Nullable(1) .+ Nullable(1) .+ Nullable(1) .+ Nullable{Int}() .+ - Nullable(1) .+ Nullable(1) |> isnull_oftype(Int)) - -# these are not inferrable because there are too many arguments -us = map(Nullable, 1:20) -@test broadcast(max, us...) === Nullable(20) -@test isnull(broadcast(max, us..., Nullable{Int}())) - -# test all elementwise operations -# note that elementwise operations are the same as broadcast -for op in (+, -, *, /, \, //, ==, <, !=, <=, ÷, %, <<, >>, ^) - # op(1, 1) chosen because it works for all operations - res = op(1, 1) - @test @inferred(broadcast(op, Nullable(1), Nullable(1))) === - Nullable(res) - @test @inferred(broadcast(op, Nullable{Int}(), Nullable(1))) |> - isnull_oftype(typeof(res)) - @test @inferred(broadcast(op, Nullable(1), Nullable{Int}())) |> - isnull_oftype(typeof(res)) - @test @inferred(broadcast(op, Nullable{Int}(), Nullable{Int}())) |> - isnull_oftype(typeof(res)) - @test @inferred(broadcast(op, Nullable(1), 1)) === - Nullable(res) - @test @inferred(broadcast(op, 1, Nullable(1))) === - Nullable(res) -end - -# test reasonable results for Union{} -# the exact types of these is finnicky and depends on implementation details -# but is guaranteed to be at worst concrete and possibly Union{} on a good day -@test isnull(@inferred(Nullable() .+ Nullable())) -@test isnull(@inferred(Nullable() .+ 1)) -@test isnull(@inferred(Nullable() .+ Nullable(1))) - -# test that things don't pessimize because of non-homogenous types -@test Nullable(10.5) === - @inferred(broadcast(+, 1, 2, Nullable(3), Nullable(4.0), Nullable(1//2))) - -# test fast path taken -for op in (+, *, -) - for b1 in (false, true) - for b2 in (false, true) - @test Nullable{Int}(op(1, 2), b1 & b2) === - @inferred(broadcast(op, Nullable{Int}(1, b1), - Nullable{Int}(2, b2))) - end - end -end - -# issue #11675 -@test repr(Nullable()) == "Nullable{Union{}}()" - -# issue #19270 -let f19270(x::S, y::T) where {S,T} = Base.promote_op(^, S, T) - @test f19270(Nullable(0.0f0), Nullable(BigInt(0))) == Nullable{Float32} -end - -# issue #21397 -@test Nullable(Tuple) === Nullable{DataType}(Tuple) diff --git a/test/parse.jl b/test/parse.jl index 2792058e18237..0c17bdc59a474 100644 --- a/test/parse.jl +++ b/test/parse.jl @@ -59,15 +59,15 @@ for T in vcat(subtypes(Signed), subtypes(Unsigned)) # Test `tryparse_internal` with part of a string let b = " " - result = @test_throws ArgumentError get(Base.tryparse_internal(Bool, b, 7, 11, 0, true)) + result = @test_throws ArgumentError Base.tryparse_internal(Bool, b, 7, 11, 0, true) exception_bool = result.value @test exception_bool.msg == "input string only contains whitespace" - result = @test_throws ArgumentError get(Base.tryparse_internal(Int, b, 7, 11, 0, true)) + result = @test_throws ArgumentError Base.tryparse_internal(Int, b, 7, 11, 0, true) exception_int = result.value @test exception_int.msg == "input string is empty or only contains whitespace" - result = @test_throws ArgumentError get(Base.tryparse_internal(UInt128, b, 7, 11, 0, true)) + result = @test_throws ArgumentError Base.tryparse_internal(UInt128, b, 7, 11, 0, true) exception_uint = result.value @test exception_uint.msg == "input string is empty or only contains whitespace" end @@ -75,7 +75,7 @@ for T in vcat(subtypes(Signed), subtypes(Unsigned)) # Test that the entire input string appears in error messages let s = " false true " result = @test_throws(ArgumentError, - get(Base.tryparse_internal(Bool, s, start(s), endof(s), 0, true))) + Base.tryparse_internal(Bool, s, start(s), endof(s), 0, true)) @test result.value.msg == "invalid Bool representation: $(repr(s))" end @@ -212,8 +212,8 @@ end @test parse(Int, "2") === 2 @test parse(Bool, "true") === true @test parse(Bool, "false") === false -@test get(tryparse(Bool, "true")) === get(Nullable{Bool}(true)) -@test get(tryparse(Bool, "false")) === get(Nullable{Bool}(false)) +@test tryparse(Bool, "true") === true +@test tryparse(Bool, "false") === false @test_throws ArgumentError parse(Int, "2", 1) @test_throws ArgumentError parse(Int, "2", 63) @@ -225,9 +225,9 @@ end # error throwing branch from #10560 @test_throws ArgumentError Base.tryparse_internal(Bool, "foo", 1, 2, 10, true) -@test tryparse(Float64, "1.23") === Nullable(1.23) -@test tryparse(Float32, "1.23") === Nullable(1.23f0) -@test tryparse(Float16, "1.23") === Nullable(Float16(1.23)) +@test tryparse(Float64, "1.23") === 1.23 +@test tryparse(Float32, "1.23") === 1.23f0 +@test tryparse(Float16, "1.23") === Float16(1.23) # parsing complex numbers (#22250) @testset "complex parsing" begin diff --git a/test/reflection.jl b/test/reflection.jl index d471f005db531..e79374018f4a5 100644 --- a/test/reflection.jl +++ b/test/reflection.jl @@ -580,35 +580,35 @@ function has_backslashes(mod::Module) continue end h = has_backslashes(f) - isnull(h) || return h + h === nothing || return h end - return Nullable{Method}() + return nothing end function has_backslashes(f::Function) for m in methods(f) h = has_backslashes(m) - isnull(h) || return h + h === nothing || return h end - return Nullable{Method}() + return nothing end function has_backslashes(meth::Method) if '\\' in string(meth.file) - return Nullable{Method}(meth) + return meth else - return Nullable{Method}() + return nothing end end -has_backslashes(x) = Nullable{Method}() +has_backslashes(x) = nothing h16850 = has_backslashes(Base) if Sys.iswindows() - if isnull(h16850) + if h16850 === nothing @warn """No methods found in Base with backslashes in file name, skipping test for `Base.url`""" else - @test !('\\' in Base.url(get(h16850))) + @test !('\\' in Base.url(h16850)) end else - @test isnull(h16850) + @test h16850 === nothing end # Adds test for PR #17636 diff --git a/test/serialize.jl b/test/serialize.jl index 770654007e41f..2f68f74fd9dbb 100644 --- a/test/serialize.jl +++ b/test/serialize.jl @@ -357,11 +357,17 @@ create_serialization_stream() do s # user-defined type array end # corner case: undefined inside immutable struct +struct MyNullable{T} + hasvalue::Bool + value::T + + MyNullable{T}() where {T} = new(false) +end create_serialization_stream() do s - serialize(s, Nullable{Any}()) + serialize(s, MyNullable{Any}()) seekstart(s) n = deserialize(s) - @test isa(n, Nullable) + @test isa(n, MyNullable) @test !isdefined(n, :value) end diff --git a/test/some.jl b/test/some.jl new file mode 100644 index 0000000000000..4816f791513d4 --- /dev/null +++ b/test/some.jl @@ -0,0 +1,84 @@ +# This file is a part of Julia. License is MIT: https://julialang.org/license + +## promote() + +@test promote_type(Some{Int}, Some{Float64}) === Some{Float64} + +## convert() + +# These conversions must fail to prevent ambiguities +# when a value to wrap is already a Some or a Void +@test_throws MethodError convert(Some, 1) +@test_throws MethodError convert(Union{Some, Void}, 1) +@test_throws MethodError convert(Some{Int}, 1) +@test_throws MethodError convert(Union{Some{Int}, Void}, 1) + +@test convert(Some, Some(1)) === convert(Union{Some, Void}, Some(1)) === Some(1) +@test convert(Some{Int}, Some(1)) === convert(Union{Some{Int}, Void}, Some(1)) === Some(1) +@test convert(Some{Int}, Some(1.0)) === Some(1) +@test convert(Union{Some{Int}, Void}, Some(1.0)) === Some(1) + +@test_throws MethodError convert(Some, nothing) +@test_throws MethodError convert(Some{Int}, nothing) + +@test convert(Some, Some(nothing)) === Some(nothing) +@test convert(Some{Void}, Some(nothing)) === Some(nothing) + +@test convert(Union{Some, Void}, nothing) === nothing +@test convert(Union{Some{Int}, Void}, nothing) === nothing + +@test convert(Union{Int, Void}, nothing) === nothing +@test convert(Union{Int, Void}, 1) === 1 +@test convert(Union{Int, Void}, 1.0) === 1 +@test convert(Void, nothing) === nothing +@test_throws MethodError convert(Void, 1) + +## show() + +@test sprint(show, Some(1)) == "Some(1)" +@test sprint(show, Some(Some(1))) == "Some(Some(1))" +@test repr([Some(1)]) == "Some{$Int}[1]" +@test repr([Some(Some(1))]) == "Some{Some{$Int}}[Some(1)]" + +## == and isequal nothing + +@test Some(1) != nothing +@test Some(nothing) != nothing +@test !isequal(Some(1), nothing) +@test !isequal(Some(nothing), nothing) + +# coalesce() + +for v in (nothing, missing) + @test coalesce(1) === 1 + @test coalesce(v) === v + @test coalesce(v, 1) === 1 + @test coalesce(1, v) === 1 + @test coalesce(v, v) === v + @test coalesce(v, 1, 2) === 1 + @test coalesce(1, v, 2) === 1 + @test coalesce(v, v, 2) === 2 + @test coalesce(v, v, v) === v + + @test coalesce(Some(1)) === 1 + @test coalesce(Some(v)) === v + @test coalesce(Some(1), 0) === 1 + @test coalesce(Some(v), 0) === v + @test coalesce(v, Some(v)) === v + @test coalesce(Some(1), v) === 1 + @test coalesce(v, Some(1)) === 1 + @test coalesce(v, Some(1), v) === 1 + @test coalesce(v, Some(1), Some(2)) === 1 + @test coalesce(Some(1), v, Some(2)) === 1 + + @test coalesce(v, missing) === missing + @test coalesce(v, nothing) === nothing + @test coalesce(v, missing, v) === v + @test coalesce(v, nothing, v) === v +end + +# notnothing() + +using Base: notnothing +@test notnothing(1) === 1 +@test_throws ArgumentError notnothing(nothing) \ No newline at end of file diff --git a/test/spawn.jl b/test/spawn.jl index 93703cef90c44..48ee76874296d 100644 --- a/test/spawn.jl +++ b/test/spawn.jl @@ -453,12 +453,12 @@ if Sys.isunix() let ps = Pipe[] ulimit_n = tryparse(Int, readchomp(`sh -c 'ulimit -n'`)) try - for i = 1 : 100 * get(ulimit_n, 1000) + for i = 1 : 100 * coalesce(ulimit_n, 1000) p = Pipe() Base.link_pipe(p) push!(ps, p) end - if isnull(ulimit_n) + if ulimit_n === nothing @warn "`ulimit -n` is set to unlimited, fd exhaustion cannot be tested" @test_broken false else diff --git a/test/specificity.jl b/test/specificity.jl index ba58adb53bf7c..bc36a14553709 100644 --- a/test/specificity.jl +++ b/test/specificity.jl @@ -127,8 +127,8 @@ f17016(f, t1::Tuple) = 1 @test !args_morespecific(Tuple{Real, Real, Vararg{Real}}, Tuple{T, T, T} where T <: Real) @test args_morespecific(Tuple{Real, Real, Vararg{Int}}, Tuple{T, T, T} where T <: Real) -@test args_morespecific(Tuple{Type{Base.Nullable{T}}} where T, Tuple{Type{T}, Any} where T) -@test !args_morespecific(Tuple{Type{Base.Nullable{T}}, T} where T, Tuple{Type{Base.Nullable{T}}} where T) +@test args_morespecific(Tuple{Type{Base.Some{T}}} where T, Tuple{Type{T}, Any} where T) +@test !args_morespecific(Tuple{Type{Base.Some{T}}, T} where T, Tuple{Type{Base.Some{T}}} where T) @test args_morespecific(Tuple{Union{Base.StepRange{T, S} where S, Base.StepRangeLen{T, T, S} where S}, Union{Base.StepRange{T, S} where S, Base.StepRangeLen{T, T, S} where S}} where T, diff --git a/test/strings/basic.jl b/test/strings/basic.jl index 1203272b8a780..19886bc08da26 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -242,19 +242,19 @@ end end @testset "issue #10307" begin - @test typeof(map(x -> parse(Int16, x), AbstractString[])) == Vector{Int16} + @test typeof(map(x -> parse(Int16, x), AbstractString[])) == Vector{Union{Int16, Void}} for T in [Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128] for i in [typemax(T), typemin(T)] s = "$i" - @test get(tryparse(T, s)) == i + @test tryparse(T, s) == i end end for T in [Int8, Int16, Int32, Int64, Int128] for i in [typemax(T), typemin(T)] f = "$(i)0" - @test isnull(tryparse(T, f)) + @test tryparse(T, f) === nothing end end end @@ -271,13 +271,13 @@ end @test unsafe_string(sp,5) == "abcde" @test typeof(unsafe_string(sp)) == String - @test get(tryparse(BigInt, "1234567890")) == BigInt(1234567890) - @test isnull(tryparse(BigInt, "1234567890-")) + @test tryparse(BigInt, "1234567890") == BigInt(1234567890) + @test tryparse(BigInt, "1234567890-") === nothing - @test get(tryparse(Float64, "64")) == 64.0 - @test isnull(tryparse(Float64, "64o")) - @test get(tryparse(Float32, "32")) == 32.0f0 - @test isnull(tryparse(Float32, "32o")) + @test tryparse(Float64, "64") == 64.0 + @test tryparse(Float64, "64o") === nothing + @test tryparse(Float32, "32") == 32.0f0 + @test tryparse(Float32, "32o") === nothing end @testset "issue #10994: handle embedded NUL chars for string parsing" begin @@ -285,7 +285,7 @@ end @test_throws ArgumentError parse(T, "1\0") end for T in [BigInt, Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Int128, UInt128, Float64, Float32] - @test isnull(tryparse(T, "1\0")) + @test tryparse(T, "1\0") === nothing end let s = Base.Unicode.normalize("tést",:NFKC) @test unsafe_string(Base.unsafe_convert(Cstring, Base.cconvert(Cstring, s))) == s