From 5d1e1d0765dce4c03927075b0b9032912278918c Mon Sep 17 00:00:00 2001 From: Milan Bouchet-Valat Date: Sat, 16 Dec 2017 00:31:26 +0100 Subject: [PATCH] Replace Nullable{T} with Union{T, Void} or Union{Some{T}, Void} (#23642) Also add coalesce() function to return first non-nothing value and unwrap Some objects. Use the notnothing() function internally where it makes sense to assert that the result is different from nothing. Use custom MaybeValue wrapper for ProductIterator to work around a performance regression due to type instability (information about whether a value is present or not is carried separately). --- NEWS.md | 6 + base/broadcast.jl | 50 +- base/channels.jl | 18 +- base/client.jl | 2 +- base/deprecated.jl | 21 +- base/docs/Docs.jl | 14 +- base/docs/basedocs.jl | 2 +- base/exports.jl | 8 +- base/gmp.jl | 10 +- base/int.jl | 6 +- base/iterators.jl | 22 +- base/libgit2/callbacks.jl | 67 ++- base/libgit2/config.jl | 12 +- base/libgit2/gitcredential.jl | 78 ++- base/libgit2/index.jl | 8 +- base/libgit2/libgit2.jl | 51 +- base/libgit2/rebase.jl | 4 +- base/libgit2/reference.jl | 22 +- base/libgit2/remote.jl | 14 +- base/libgit2/repository.jl | 12 +- base/libgit2/status.jl | 6 +- base/libgit2/types.jl | 80 ++- base/libgit2/utils.jl | 2 +- base/lock.jl | 6 +- base/methodshow.jl | 16 +- base/missing.jl | 6 +- base/mpfr.jl | 2 +- base/nullable.jl | 419 -------------- base/nullabletype.jl | 9 - base/parse.jl | 120 ++-- base/pkg/entry.jl | 10 +- base/pkg/pkg.jl | 8 +- base/pkg/read.jl | 4 +- base/precompile.jl | 100 ++-- base/process.jl | 34 +- base/repl/REPL.jl | 3 +- base/repl/REPLCompletions.jl | 2 +- base/socket.jl | 2 +- base/some.jl | 80 +++ base/statistics.jl | 4 +- base/stream.jl | 19 +- base/sysimg.jl | 5 +- base/threadcall.jl | 12 +- base/util.jl | 14 +- doc/src/manual/faq.md | 22 +- doc/src/manual/parallel-computing.md | 37 +- doc/src/manual/stacktraces.md | 4 +- doc/src/manual/style-guide.md | 2 - doc/src/manual/types.md | 187 ------- doc/src/stdlib/base.md | 17 +- examples/clustermanager/0mq/ZMQCM.jl | 14 +- .../clustermanager/simple/UnixDomainCM.jl | 11 +- stdlib/Dates/src/io.jl | 28 +- stdlib/Dates/src/parse.jl | 91 ++- stdlib/Dates/src/types.jl | 20 +- stdlib/Dates/test/io.jl | 2 +- stdlib/DelimitedFiles/src/DelimitedFiles.jl | 21 +- stdlib/Distributed/src/Distributed.jl | 2 +- stdlib/Distributed/src/cluster.jl | 119 ++-- stdlib/Distributed/src/macros.jl | 6 +- stdlib/Distributed/src/managers.jl | 36 +- stdlib/Distributed/src/messages.jl | 2 +- stdlib/Distributed/src/process_messages.jl | 6 +- stdlib/Distributed/src/remotecall.jl | 32 +- stdlib/Distributed/src/workerpool.jl | 10 +- stdlib/Distributed/test/distributed_exec.jl | 18 +- stdlib/Distributed/test/topology.jl | 4 +- stdlib/Profile/src/Profile.jl | 4 +- test/ambiguous.jl | 2 + test/broadcast.jl | 11 +- test/ccall.jl | 4 +- test/choosetests.jl | 2 +- test/codegen.jl | 1 - test/compile.jl | 6 +- test/copy.jl | 8 +- test/core.jl | 12 +- test/libgit2-helpers.jl | 23 +- test/libgit2.jl | 145 +++-- test/misc.jl | 6 +- test/missing.jl | 4 + test/nullable.jl | 520 ------------------ test/parse.jl | 18 +- test/reflection.jl | 20 +- test/serialize.jl | 10 +- test/some.jl | 84 +++ test/spawn.jl | 4 +- test/specificity.jl | 4 +- test/strings/basic.jl | 20 +- 88 files changed, 995 insertions(+), 1994 deletions(-) delete mode 100644 base/nullable.jl delete mode 100644 base/nullabletype.jl create mode 100644 base/some.jl delete mode 100644 test/nullable.jl create mode 100644 test/some.jl diff --git a/NEWS.md b/NEWS.md index 87f645dfb30be..4061fa6b83729 100644 --- a/NEWS.md +++ b/NEWS.md @@ -779,6 +779,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 393a8813bd3aa..030b828eb43d6 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 @@ -3254,6 +3254,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 8220c3ae1b2f9..1aa49686a6c03 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 624f3550f4f1b..7bc9548149df7 100644 --- a/base/lock.jl +++ b/base/lock.jl @@ -11,7 +11,7 @@ Each [`lock`](@ref) must be matched with an [`unlock`](@ref). This lock is NOT threadsafe. See [`Threads.Mutex`](@ref) 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 13031d956f703..dd4623be6c958 100644 --- a/test/strings/basic.jl +++ b/test/strings/basic.jl @@ -255,19 +255,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 @@ -284,13 +284,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 @@ -298,7 +298,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