diff --git a/base/compiler/abstractinterpretation.jl b/base/compiler/abstractinterpretation.jl index 2acb39c1a9e5e..bb42a434855f6 100644 --- a/base/compiler/abstractinterpretation.jl +++ b/base/compiler/abstractinterpretation.jl @@ -249,7 +249,7 @@ function abstract_call_method(method::Method, @nospecialize(sig), sparams::Simpl comparison = method.sig end # see if the type is actually too big (relative to the caller), and limit it if required - newsig = limit_type_size(sig, comparison, sv.linfo.specTypes, spec_len) + newsig = limit_type_size(sig, comparison, sv.linfo.specTypes, sv.params.TUPLE_COMPLEXITY_LIMIT_DEPTH, spec_len) if newsig !== sig # continue inference, but note that we've limited parameter complexity diff --git a/base/compiler/params.jl b/base/compiler/params.jl index dc376e03c0f4f..4d812e79a4757 100644 --- a/base/compiler/params.jl +++ b/base/compiler/params.jl @@ -26,8 +26,9 @@ struct Params # when inferring a call to _apply MAX_APPLY_UNION_ENUM::Int - # parameters limiting large types + # parameters limiting large (tuple) types MAX_TUPLETYPE_LEN::Int + TUPLE_COMPLEXITY_LIMIT_DEPTH::Int # when attempting to inlining _apply, abort the optimization if the tuple # contains more than this many elements @@ -41,12 +42,13 @@ struct Params inline_tupleret_bonus::Int = 400, max_methods::Int = 4, tupletype_len::Int = 15, + tupletype_depth::Int = 3, tuple_splat::Int = 16, union_splitting::Int = 4, apply_union_enum::Int = 8) return new(Vector{InferenceResult}(), world, inlining, true, false, inline_cost_threshold, inline_nonleaf_penalty, inline_tupleret_bonus, max_methods, union_splitting, apply_union_enum, - tupletype_len, tuple_splat) + tupletype_len, tupletype_depth, tuple_splat) end end diff --git a/base/compiler/tfuncs.jl b/base/compiler/tfuncs.jl index 406a26000c5fb..45aa51dfe1de3 100644 --- a/base/compiler/tfuncs.jl +++ b/base/compiler/tfuncs.jl @@ -712,9 +712,6 @@ function apply_type_tfunc(@nospecialize(headtypetype), @nospecialize args...) if isvarargtype(headtype) return Type end - if uncertain && type_too_complex(appl, MAX_TYPE_DEPTH) - return Type{<:headtype} - end if istuple return Type{<:appl} end @@ -914,3 +911,10 @@ function return_type_tfunc(argtypes::Vector{Any}, vtypes::VarTable, sv::Inferenc end return NOT_FOUND end + +# N.B.: typename maps type equivalence classes to a single value +function typename_static(@nospecialize(t)) + t = unwrap_unionall(t) + return isType(t) ? _typename(t.parameters[1]) : Core.TypeName +end +typename_static(t::Const) = _typename(t.val) diff --git a/base/compiler/typelattice.jl b/base/compiler/typelattice.jl index 3a2fb6a19e634..cb19b7794164b 100644 --- a/base/compiler/typelattice.jl +++ b/base/compiler/typelattice.jl @@ -49,11 +49,14 @@ struct PartialTypeVar end # Wraps a type and represents that the value may also be undef at this point. +# (only used in optimize, not abstractinterpret) struct MaybeUndef typ + MaybeUndef(@nospecialize(typ)) = new(typ) end # The type of a variable load is either a value or an UndefVarError +# (only used in abstractinterpret, doesn't appear in optimize) struct VarState typ undef::Bool @@ -72,32 +75,6 @@ struct NotFound end const NOT_FOUND = NotFound() -##################### -# lattice utilities # -##################### - -function rewrap(@nospecialize(t), @nospecialize(u)) - isa(t, Const) && return t - isa(t, Conditional) && return t - return rewrap_unionall(t, u) -end - -_typename(a) = Union{} -_typename(a::Vararg) = Any -_typename(a::TypeVar) = Any -function _typename(a::Union) - ta = _typename(a.a) - tb = _typename(a.b) - ta === tb ? tb : (ta === Any || tb === Any) ? Any : Union{} -end -_typename(union::UnionAll) = _typename(union.body) - -_typename(a::DataType) = Const(a.name) - -# N.B.: typename maps type equivalence classes to a single value -typename_static(@nospecialize(t)) = isType(t) ? _typename(t.parameters[1]) : Any -typename_static(t::Const) = _typename(t.val) - ################# # lattice logic # ################# @@ -176,49 +153,6 @@ widenconst(@nospecialize(t)) = t issubstate(a::VarState, b::VarState) = (a.typ ⊑ b.typ && a.undef <= b.undef) -function tmerge(@nospecialize(typea), @nospecialize(typeb)) - typea ⊑ typeb && return typeb - typeb ⊑ typea && return typea - if isa(typea, MaybeUndef) || isa(typeb, MaybeUndef) - return MaybeUndef(tmerge( - isa(typea, MaybeUndef) ? typea.typ : typea, - isa(typeb, MaybeUndef) ? typeb.typ : typeb)) - end - if isa(typea, Conditional) && isa(typeb, Conditional) - if typea.var === typeb.var - vtype = tmerge(typea.vtype, typeb.vtype) - elsetype = tmerge(typea.elsetype, typeb.elsetype) - if vtype != elsetype - return Conditional(typea.var, vtype, elsetype) - end - end - return Bool - end - typea, typeb = widenconst(typea), widenconst(typeb) - typea === typeb && return typea - if !(isa(typea,Type) || isa(typea,TypeVar)) || !(isa(typeb,Type) || isa(typeb,TypeVar)) - return Any - end - if (typea <: Tuple) && (typeb <: Tuple) - if isa(typea, DataType) && isa(typeb, DataType) && length(typea.parameters) == length(typeb.parameters) && !isvatuple(typea) && !isvatuple(typeb) - return typejoin(typea, typeb) - end - if isa(typea, Union) || isa(typeb, Union) || (isa(typea,DataType) && length(typea.parameters)>3) || - (isa(typeb,DataType) && length(typeb.parameters)>3) - # widen tuples faster (see #6704), but not too much, to make sure we can infer - # e.g. (t::Union{Tuple{Bool},Tuple{Bool,Int}})[1] - return Tuple - end - end - u = Union{typea, typeb} - if unionlen(u) > MAX_TYPEUNION_LEN || type_too_complex(u, MAX_TYPE_DEPTH) - # don't let type unions get too big - # TODO: something smarter, like a common supertype - return Any - end - return u -end - function smerge(sa::Union{NotFound,VarState}, sb::Union{NotFound,VarState}) sa === sb && return sa sa === NOT_FOUND && return sb diff --git a/base/compiler/typelimits.jl b/base/compiler/typelimits.jl index fb5c0a87d84fc..6313f6aecbee1 100644 --- a/base/compiler/typelimits.jl +++ b/base/compiler/typelimits.jl @@ -4,10 +4,8 @@ # limitation parameters # ######################### -const MAX_TYPEUNION_LEN = 3 -const MAX_TYPE_DEPTH = 8 +const MAX_TYPEUNION_LEN = 4 const MAX_INLINE_CONST_SIZE = 256 -const TUPLE_COMPLEXITY_LIMIT_DEPTH = 3 ######################### # limitation heuristics # @@ -31,10 +29,10 @@ end # limit the complexity of type `t` to be simpler than the comparison type `compare` # no new values may be introduced, so the parameter `source` encodes the set of all values already present # the outermost tuple type is permitted to have up to `allowed_tuplelen` parameters -function limit_type_size(@nospecialize(t), @nospecialize(compare), @nospecialize(source), allowed_tuplelen::Int) +function limit_type_size(@nospecialize(t), @nospecialize(compare), @nospecialize(source), allowed_tupledepth::Int, allowed_tuplelen::Int) source = svec(unwrap_unionall(compare), unwrap_unionall(source)) source[1] === source[2] && (source = svec(source[1])) - type_more_complex(t, compare, source, 1, TUPLE_COMPLEXITY_LIMIT_DEPTH, allowed_tuplelen) || return t + type_more_complex(t, compare, source, 1, allowed_tupledepth, allowed_tuplelen) || return t r = _limit_type_size(t, compare, source, 1, allowed_tuplelen) @assert t <: r #@assert r === _limit_type_size(r, t, source) # this monotonicity constraint is slightly stronger than actually required, @@ -304,21 +302,118 @@ function type_more_complex(@nospecialize(t), @nospecialize(c), sources::SimpleVe return true end -function type_too_complex(@nospecialize(t), d::Int) - if d < 0 - return true - elseif isa(t, Union) - return type_too_complex(t.a, d - 1) || type_too_complex(t.b, d - 1) - elseif isa(t, TypeVar) - return type_too_complex(t.lb, d - 1) || type_too_complex(t.ub, d - 1) - elseif isa(t, UnionAll) - return type_too_complex(t.var, d) || type_too_complex(t.body, d) - elseif isa(t, DataType) - for x in (t.parameters)::SimpleVector - if type_too_complex(x, d - 1) - return true +function tmerge(@nospecialize(typea), @nospecialize(typeb)) + typea ⊑ typeb && return typeb + typeb ⊑ typea && return typea + if isa(typea, MaybeUndef) || isa(typeb, MaybeUndef) + return MaybeUndef(tmerge( + isa(typea, MaybeUndef) ? typea.typ : typea, + isa(typeb, MaybeUndef) ? typeb.typ : typeb)) + end + if isa(typea, Conditional) && isa(typeb, Conditional) + if typea.var === typeb.var + vtype = tmerge(typea.vtype, typeb.vtype) + elsetype = tmerge(typea.elsetype, typeb.elsetype) + if vtype != elsetype + return Conditional(typea.var, vtype, elsetype) end end + return Bool end - return false + typea, typeb = widenconst(typea), widenconst(typeb) + typea === typeb && return typea + if !(isa(typea, Type) || isa(typea, TypeVar)) || + !(isa(typeb, Type) || isa(typeb, TypeVar)) + # XXX: this should never happen + return Any + end + # if we didn't start with any unions, then always OK to form one now + if !(typea isa Union || typeb isa Union) + # except if we might have switched Union and Tuple below, or would do so + if (isconcretetype(typea) && isconcretetype(typeb)) || !(typea <: Tuple && typeb <: Tuple) + return Union{typea, typeb} + end + end + # collect the list of types from past tmerge calls returning Union + # and then reduce over that list + types = Any[] + _uniontypes(typea, types) + _uniontypes(typeb, types) + typenames = Vector{Core.TypeName}(undef, length(types)) + for i in 1:length(types) + # check that we will be able to analyze (and simplify) everything + # bail if everything isn't a well-formed DataType + ti = types[i] + uw = unwrap_unionall(ti) + (uw isa DataType && ti <: uw.name.wrapper) || return Any + typenames[i] = uw.name + end + # see if any of the union elements have the same TypeName + # in which case, simplify this tmerge by replacing it with + # the widest possible version of itself (the wrapper) + for i in 1:length(types) + ti = types[i] + for j in (i + 1):length(types) + if typenames[i] === typenames[j] + tj = types[j] + if ti <: tj + types[i] = Union{} + break + elseif tj <: ti + types[j] = Union{} + typenames[j] = Any.name + else + widen = typenames[i].wrapper + if typenames[i] === Tuple.name + # try to widen Tuple slower: make a single non-concrete Tuple containing both + # converge the Tuple element-wise if they are the same length + # see 4ee2b41552a6bc95465c12ca66146d69b354317b, be59686f7613a2ccfd63491c7b354d0b16a95c05, + if nothing !== tuplelen(ti) === tuplelen(tj) + widen = tuplemerge(ti, tj) + end + # TODO: else, try to merge them into a single Tuple{Vararg{T}} instead (#22120)? + end + types[i] = Union{} + types[j] = widen + break + end + end + end + end + u = Union{types...} + if unionlen(u) <= MAX_TYPEUNION_LEN + # don't let type unions get too big, if the above didn't reduce it enough + return u + end + # finally, just return the widest possible type + return Any +end + +# the inverse of switchtupleunion, with limits on max element union size +function tuplemerge(@nospecialize(a), @nospecialize(b)) + if isa(a, UnionAll) + return UnionAll(a.var, tuplemerge(a.body, b)) + elseif isa(b, UnionAll) + return UnionAll(b.var, tuplemerge(a, b.body)) + elseif isa(a, Union) + return tuplemerge(tuplemerge(a.a, a.b), b) + elseif isa(b, Union) + return tuplemerge(a, tuplemerge(b.a, b.b)) + end + a = a::DataType + b = b::DataType + ap, bp = a.parameters, b.parameters + lar = length(ap)::Int + lbr = length(bp)::Int + @assert lar === lbr && a.name === b.name === Tuple.name "assertion failure" + p = Vector{Any}(undef, lar) + for i = 1:lar + ui = Union{ap[i], bp[i]} + if unionlen(ui) < MAX_TYPEUNION_LEN + p[i] = ui + else + p[i] = Any + end + end + return Tuple{p...} end diff --git a/base/compiler/typeutils.jl b/base/compiler/typeutils.jl index a8ce3e4c956f1..c5b0ccaf04b7d 100644 --- a/base/compiler/typeutils.jl +++ b/base/compiler/typeutils.jl @@ -1,7 +1,16 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license -const _TYPE_NAME = Type.body.name +##################### +# lattice utilities # +##################### + +function rewrap(@nospecialize(t), @nospecialize(u)) + isa(t, Const) && return t + isa(t, Conditional) && return t + return rewrap_unionall(t, u) +end +const _TYPE_NAME = Type.body.name isType(@nospecialize t) = isa(t, DataType) && (t::DataType).name === _TYPE_NAME # true if Type{T} is inlineable as constant T @@ -73,6 +82,19 @@ function tvar_extent(@nospecialize t) return t end +_typename(@nospecialize a) = Union{} +_typename(a::TypeVar) = Core.TypeName +function _typename(a::Union) + ta = _typename(a.a) + tb = _typename(a.b) + ta === tb && return ta # same type-name + (ta === Union{} || tb === Union{}) && return Union{} # threw an error + (ta isa Const && tb isa Const) && return Union{} # will throw an error (different type-names) + return Core.TypeName # uncertain result +end +_typename(union::UnionAll) = _typename(union.body) +_typename(a::DataType) = Const(a.name) + function tuple_tail_elem(@nospecialize(init), ct) return Vararg{widenconst(foldl((a, b) -> tmerge(a, tvar_extent(unwrapva(b))), init, ct))} end @@ -117,3 +139,23 @@ function _switchtupleunion(t::Vector{Any}, i::Int, tunion::Vector{Any}, @nospeci end return tunion end + +tuplelen(@nospecialize tpl) = nothing +function tuplelen(tpl::DataType) + l = length(tpl.parameters)::Int + if l > 0 + last = unwrap_unionall(tpl.parameters[l]) + if isvarargtype(last) + N = last.parameters[2] + N isa Int || return nothing + l += N - 1 + end + end + return l +end +tuplelen(tpl::UnionAll) = tuplelen(tpl.body) +function tuplelen(tpl::Union) + la, lb = tuplelen(tpl.a), tuplelen(tpl.b) + la == lb && return la + return nothing +end diff --git a/base/promotion.jl b/base/promotion.jl index 35f8e886fa8e4..b9633c53f2217 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -18,26 +18,25 @@ function typejoin(@nospecialize(a), @nospecialize(b)) return b elseif b <: a return a - elseif isa(a,UnionAll) + elseif isa(a, UnionAll) return UnionAll(a.var, typejoin(a.body, b)) - elseif isa(b,UnionAll) + elseif isa(b, UnionAll) return UnionAll(b.var, typejoin(a, b.body)) - elseif isa(a,TypeVar) + elseif isa(a, TypeVar) return typejoin(a.ub, b) - elseif isa(b,TypeVar) + elseif isa(b, TypeVar) return typejoin(a, b.ub) - elseif isa(a,Union) - a′ = typejoin(a.a, a.b) - return a′ === a ? typejoin(a, b) : typejoin(a′, b) - elseif isa(b,Union) - b′ = typejoin(b.a, b.b) - return b′ === b ? typejoin(a, b) : typejoin(a, b′) + elseif isa(a, Union) + return typejoin(typejoin(a.a, a.b), b) + elseif isa(b, Union) + return typejoin(a, typejoin(b.a, b.b)) elseif a <: Tuple if !(b <: Tuple) return Any end ap, bp = a.parameters, b.parameters - lar = length(ap)::Int; lbr = length(bp)::Int + lar = length(ap)::Int + lbr = length(bp)::Int if lar == 0 return Tuple{Vararg{tailjoin(bp, 1)}} end @@ -289,9 +288,9 @@ end # promote numeric types T and S to typejoin(T,S) if T<:S or S<:T # for example this makes promote_type(Integer,Real) == Real without # promoting arbitrary pairs of numeric types to Number. -promote_to_supertype(::Type{T}, ::Type{T}, ::Type{T}) where {T<:Number} = (@_inline_meta; T) -promote_to_supertype(::Type{T}, ::Type{S}, ::Type{T}) where {T<:Number,S<:Number} = (@_inline_meta; T) -promote_to_supertype(::Type{T}, ::Type{S}, ::Type{S}) where {T<:Number,S<:Number} = (@_inline_meta; S) +promote_to_supertype(::Type{T}, ::Type{T}, ::Type{T}) where {T<:Number} = T +promote_to_supertype(::Type{T}, ::Type{S}, ::Type{T}) where {T<:Number,S<:Number} = T +promote_to_supertype(::Type{T}, ::Type{S}, ::Type{S}) where {T<:Number,S<:Number} = S promote_to_supertype(::Type{T}, ::Type{S}, ::Type) where {T<:Number,S<:Number} = error("no promotion exists for ", T, " and ", S) @@ -386,7 +385,7 @@ minmax(x::Real, y::Real) = minmax(promote(x, y)...) # "Promotion" that takes a function into account and tries to preserve # non-concrete types. These are meant to be used mainly by elementwise # operations, so it is advised against overriding them -_default_type(T::Type) = (@_inline_meta; T) +_default_type(T::Type) = T if isdefined(Core, :Compiler) const _return_type = Core.Compiler.return_type @@ -410,16 +409,14 @@ Guess what an appropriate container eltype would be for storing results of the container eltype on the type of the actual elements. Only in the absence of any elements (for an empty result container), it may be unavoidable to call `promote_op`. """ -promote_op(::Any...) = (@_inline_meta; Any) +promote_op(::Any...) = Any function promote_op(f, ::Type{S}) where S - @_inline_meta TT = Tuple{_default_type(S)} T = _return_type(f, TT) isdispatchtuple(Tuple{S}) && return isdispatchtuple(Tuple{T}) ? T : Any return typejoin(S, T) end function promote_op(f, ::Type{R}, ::Type{S}) where {R,S} - @_inline_meta TT = Tuple{_default_type(R), _default_type(S)} T = _return_type(f, TT) isdispatchtuple(Tuple{R}) && isdispatchtuple(Tuple{S}) && return isdispatchtuple(Tuple{T}) ? T : Any diff --git a/stdlib/Dates/src/io.jl b/stdlib/Dates/src/io.jl index 92a2482669cb5..fadab27191714 100644 --- a/stdlib/Dates/src/io.jl +++ b/stdlib/Dates/src/io.jl @@ -17,11 +17,10 @@ abstract type AbstractDateToken end `locale`. If a `tryparsenext` method does not need a locale, it can leave the argument out in the method definition. -Return a tuple of 2 elements `(res, idx)`, where: +If parsing succeeds, returns a tuple of 2 elements `(res, idx)`, where: -* `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. +* `res` is the result of the parsing. +* `idx::Int`, is the index _after_ the index at which parsing ended. """ function tryparsenext end @@ -39,7 +38,7 @@ function format end # fallback to tryparsenext/format methods that don't care about locale @inline function tryparsenext(d::AbstractDateToken, str, i, len, locale) - tryparsenext(d, str, i, len) + return tryparsenext(d, str, i, len) end function Base.string(t::Time) @@ -91,20 +90,19 @@ end for c in "yYmdHMS" @eval begin @inline function tryparsenext(d::DatePart{$c}, str, i, len) - tryparsenext_base10(str, i, len, min_width(d), max_width(d)) + return tryparsenext_base10(str, i, len, min_width(d), max_width(d)) end end 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 = word === nothing ? 0 : $fn(word, locale) - if val == 0 - return nothing, i - else - return val, i - end + next = tryparsenext_word(str, i, len, locale, max_width(d)) + next === nothing && return nothing + word, i = next + val = $fn(word, locale) + val == 0 && return nothing + return val, i end end @@ -112,17 +110,15 @@ end 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 ms !== nothing - val0 = val = ms - len = ii - i - if len > 3 - val, r = divrem(val, Int64(10) ^ (len - 3)) - r == 0 || throw(InexactError(:convert, Decimal3, val0)) - else - val *= Int64(10) ^ (3 - len) - end - ms = val + val = tryparsenext_base10(str, i, len, min_width(d), max_width(d)) + val === nothing && return nothing + ms0, ii = val + len = ii - i + if len > 3 + ms, r = divrem(ms0, Int64(10) ^ (len - 3)) + r == 0 || throw(InexactError(:convert, Decimal3, ms0)) + else + ms = ms0 * Int64(10) ^ (3 - len) end return ms, ii end @@ -186,10 +182,10 @@ Delim(d::T) where {T<:AbstractChar} = Delim{T, 1}(d) Delim(d::String) = Delim{String, length(d)}(d) @inline function tryparsenext(d::Delim{<:AbstractChar, N}, str, i::Int, len) where N - for j=1:N - i > len && return (nothing, i) + for j = 1:N + i > len && return nothing c, i = next(str, i) - c != d.d && return (nothing, i) + c != d.d && return nothing end return true, i end @@ -199,12 +195,12 @@ end i2 = start(d.d) for j = 1:N if i1 > len - return nothing, i1 + return nothing end c1, i1 = next(str, i1) c2, i2 = next(d.d, i2) if c1 != c2 - return nothing, i1 + return nothing end end return true, i1 @@ -415,7 +411,7 @@ parsing many date time strings of the same format, consider creating a [`DateFormat`](@ref) object once and using that as the second argument instead. """ function DateTime(dt::AbstractString, format::AbstractString; locale::Locale=ENGLISH) - parse(DateTime, dt, DateFormat(format, locale)) + return parse(DateTime, dt, DateFormat(format, locale)) end """ diff --git a/stdlib/Dates/src/parse.jl b/stdlib/Dates/src/parse.jl index a46638fc97344..53734d8994a44 100644 --- a/stdlib/Dates/src/parse.jl +++ b/stdlib/Dates/src/parse.jl @@ -23,11 +23,11 @@ genvar(t::DataType) = Symbol(lowercase(string(nameof(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 +the end of the string, `len`. When a directive cannot be parsed the returned value will be `nothing` if `raise` is false otherwise an exception will be thrown. -Return a 3-element tuple `(values, pos, num_parsed)`: -* `values::Union{Tuple, Nothing}`: Either `nothing`, or a tuple which contains a value +If successful, return a 3-element tuple `(values, pos, num_parsed)`: +* `values::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. @@ -58,29 +58,29 @@ Return a 3-element tuple `(values, pos, num_parsed)`: for i = 1:length(directives) if directives[i] <: DatePart name = value_names[vi] - val = Symbol(:val, name) vi += 1 push!(parsers, quote pos > len && @goto done - $val, next_pos = tryparsenext(directives[$i], str, pos, len, locale) - $val === nothing && @goto error - $name = $val - pos = next_pos + let val = tryparsenext(directives[$i], str, pos, len, locale) + val === nothing && @goto error + $name, pos = val + end num_parsed += 1 directive_index += 1 end) else push!(parsers, quote pos > len && @goto done - delim, next_pos = tryparsenext(directives[$i], str, pos, len, locale) - delim === nothing && @goto error - pos = next_pos + let val = tryparsenext(directives[$i], str, pos, len, locale) + val === nothing && @goto error + delim, pos = val + end directive_index += 1 end) end end - quote + return quote directives = df.tokens locale::DateLocale = df.locale @@ -104,7 +104,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 nothing, pos, 0 + return nothing end end @@ -114,11 +114,11 @@ 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 `nothing` if `raise` is false otherwise an exception will +parsed the returned value will be `nothing` if `raise` is false otherwise an exception will be thrown. -Return a 2-element tuple `(values, pos)`: -* `values::Union{Tuple, Nothing}`: Either `nothing`, or a tuple which contains a value +If successful, returns a 2-element tuple `(values, pos)`: +* `values::Tuple`: 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. """ @@ -146,9 +146,10 @@ Return a 2-element tuple `(values, pos)`: # Unpacks the value tuple returned by `tryparsenext_core` into separate variables. value_tuple = Expr(:tuple, value_names...) - quote - values, pos, num_parsed = tryparsenext_core(str, pos, len, df, raise) - values === nothing && return nothing, pos + return quote + val = tryparsenext_core(str, pos, len, df, raise) + val === nothing && return nothing + values, pos, num_parsed = val $(assign_defaults...) $value_tuple = values return $(Expr(:tuple, output_names...)), pos @@ -156,7 +157,7 @@ Return a 2-element tuple `(values, pos)`: end @inline function tryparsenext_base10(str::AbstractString, i::Int, len::Int, min_width::Int=1, max_width::Int=0) - i > len && (return nothing, i) + i > len && return nothing 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 @@ -170,7 +171,7 @@ end i = ii end if i <= min_pos - return nothing, i + return nothing else return d, i end @@ -189,7 +190,7 @@ end i = ii end if word_end == 0 - return nothing, i + return nothing else return SubString(str, word_start, word_end), i end @@ -198,62 +199,76 @@ end function Base.parse(::Type{DateTime}, s::AbstractString, df::typeof(ISODateTimeFormat)) i, end_pos = firstindex(s), lastindex(s) + local dy dm = dd = Int64(1) th = tm = ts = tms = Int64(0) - val, i = tryparsenext_base10(s, i, end_pos, 1) - dy = val === nothing ? (@goto error) : val - i > end_pos && @goto error + let val = tryparsenext_base10(s, i, end_pos, 1) + val === nothing && @goto error + dy, i = val + i > end_pos && @goto error + end c, i = next(s, i) c != '-' && @goto error i > end_pos && @goto done - val, i = tryparsenext_base10(s, i, end_pos, 1, 2) - dm = val === nothing ? (@goto error) : val - i > end_pos && @goto done + let val = tryparsenext_base10(s, i, end_pos, 1, 2) + val === nothing && @goto error + dm, i = val + i > end_pos && @goto done + end c, i = next(s, i) c != '-' && @goto error i > end_pos && @goto done - val, i = tryparsenext_base10(s, i, end_pos, 1, 2) - dd = val === nothing ? (@goto error) : val - i > end_pos && @goto done + let val = tryparsenext_base10(s, i, end_pos, 1, 2) + val === nothing && @goto error + dd, i = val + i > end_pos && @goto done + end c, i = next(s, i) c != 'T' && @goto error i > end_pos && @goto done - val, i = tryparsenext_base10(s, i, end_pos, 1, 2) - th = val === nothing ? (@goto error) : val - i > end_pos && @goto done + let val = tryparsenext_base10(s, i, end_pos, 1, 2) + val === nothing && @goto error + th, i = val + i > end_pos && @goto done + end c, i = next(s, i) c != ':' && @goto error i > end_pos && @goto done - val, i = tryparsenext_base10(s, i, end_pos, 1, 2) - tm = val === nothing ? (@goto error) : val - i > end_pos && @goto done + let val = tryparsenext_base10(s, i, end_pos, 1, 2) + val === nothing && @goto error + tm, i = val + i > end_pos && @goto done + end c, i = next(s, i) c != ':' && @goto error i > end_pos && @goto done - val, i = tryparsenext_base10(s, i, end_pos, 1, 2) - ts = val === nothing ? (@goto error) : val - i > end_pos && @goto done + let val = tryparsenext_base10(s, i, end_pos, 1, 2) + val === nothing && @goto error + ts, i = val + i > end_pos && @goto done + end c, i = next(s, i) c != '.' && @goto error i > end_pos && @goto done - 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 + let val = tryparsenext_base10(s, i, end_pos, 1, 3) + val === nothing && @goto error + tms, j = val + tms *= 10 ^ (3 - (j - i)) + j > end_pos || @goto error + end @label done return DateTime(dy, dm, dd, th, tm, ts, tms) @@ -264,21 +279,22 @@ end function Base.parse(::Type{T}, str::AbstractString, df::DateFormat=default_format(T)) where T<:TimeType pos, len = firstindex(str), lastindex(str) - values, pos = tryparsenext_internal(T, str, pos, len, df, true) - T(values...) + val = tryparsenext_internal(T, str, pos, len, df, true) + @assert val !== nothing + values, endpos = val + return T(values...) end function Base.tryparse(::Type{T}, str::AbstractString, df::DateFormat=default_format(T)) where T<:TimeType pos, len = firstindex(str), lastindex(str) - values, pos = tryparsenext_internal(T, str, pos, len, df, false) - if values === nothing - nothing - elseif validargs(T, values...) === nothing + res = tryparsenext_internal(T, str, pos, len, df, false) + res === nothing && return nothing + values, endpos = res + if validargs(T, values...) === nothing # TODO: validargs gets called twice, since it's called again in the T constructor - T(values...) - else - nothing + return T(values...) end + return nothing end """ @@ -293,15 +309,16 @@ number of components may be less than the total number of `DatePart`. letters = character_codes(df) tokens = Type[CONVERSION_SPECIFIERS[letter] for letter in letters] - quote + return quote pos, len = firstindex(str), lastindex(str) - values, pos, num_parsed = tryparsenext_core(str, pos, len, df, true) - t = values + val = tryparsenext_core(str, pos, len, df, #=raise=#true) + @assert val !== nothing + values, pos, num_parsed = val types = $(Expr(:tuple, tokens...)) result = Vector{Any}(undef, num_parsed) for (i, typ) in enumerate(types) i > num_parsed && break - result[i] = typ(t[i]) # Constructing types takes most of the time + result[i] = typ(values[i]) # Constructing types takes most of the time end return result end diff --git a/stdlib/Dates/test/io.jl b/stdlib/Dates/test/io.jl index ba30b84cd2005..64ce2cce9e887 100644 --- a/stdlib/Dates/test/io.jl +++ b/stdlib/Dates/test/io.jl @@ -390,7 +390,7 @@ end Zulu = String function Dates.tryparsenext(d::Dates.DatePart{'Z'}, str, i, len) - Dates.tryparsenext_word(str, i, len, Dates.min_width(d), Dates.max_width(d)) + return Dates.tryparsenext_word(str, i, len, Dates.min_width(d), Dates.max_width(d)) end str = "2015-07-24T05:38:19.591Z" diff --git a/stdlib/LinearAlgebra/src/symmetric.jl b/stdlib/LinearAlgebra/src/symmetric.jl index 4605f2a766fa9..4c2f5e44ff507 100644 --- a/stdlib/LinearAlgebra/src/symmetric.jl +++ b/stdlib/LinearAlgebra/src/symmetric.jl @@ -42,7 +42,7 @@ Note that `Supper` will not be equal to `Slower` unless `A` is itself symmetric """ function Symmetric(A::AbstractMatrix, uplo::Symbol=:U) checksquare(A) - symmetric_type(typeof(A))(A, char_uplo(uplo)) + return symmetric_type(typeof(A))(A, char_uplo(uplo)) end """ @@ -66,8 +66,8 @@ The type of the object returned by `symmetric(::T, ::Symbol)`. For matrices, thi appropriately typed `Symmetric`, for `Number`s, it is the original type. If `symmetric` is implemented for a custom type, so should be `symmetric_type`, and vice versa. """ -function symmetric_type(::Type{T}) where {S,T<:AbstractMatrix{S}} - Symmetric{Union{S,promote_op(transpose, S),symmetric_type(S)},T} +function symmetric_type(::Type{T}) where {S, T<:AbstractMatrix{S}} + return Symmetric{Union{S, promote_op(transpose, S), symmetric_type(S)}, T} end symmetric_type(::Type{T}) where {T<:Number} = T @@ -112,7 +112,7 @@ Hermitian(fill(complex(1,1), 1, 1)) == fill(1, 1, 1) """ function Hermitian(A::AbstractMatrix, uplo::Symbol=:U) n = checksquare(A) - hermitian_type(typeof(A))(A, char_uplo(uplo)) + return hermitian_type(typeof(A))(A, char_uplo(uplo)) end """ @@ -138,7 +138,7 @@ appropriately typed `Hermitian`, for `Number`s, it is the original type. If `her implemented for a custom type, so should be `hermitian_type`, and vice versa. """ function hermitian_type(::Type{T}) where {S,T<:AbstractMatrix{S}} - Hermitian{Union{S,promote_op(adjoint, S),hermitian_type(S)},T} + return Hermitian{Union{S, promote_op(adjoint, S), hermitian_type(S)}, T} end hermitian_type(::Type{T}) where {T<:Number} = T diff --git a/test/compiler/compiler.jl b/test/compiler/compiler.jl index 76c206037d976..89072eddcd111 100644 --- a/test/compiler/compiler.jl +++ b/test/compiler/compiler.jl @@ -7,15 +7,15 @@ using Random, Core.IR using InteractiveUtils: code_llvm # demonstrate some of the type-size limits -@test Core.Compiler.limit_type_size(Ref{Complex{T} where T}, Ref, Ref, 0) == Ref -@test Core.Compiler.limit_type_size(Ref{Complex{T} where T}, Ref{Complex{T} where T}, Ref, 0) == Ref{Complex{T} where T} +@test Core.Compiler.limit_type_size(Ref{Complex{T} where T}, Ref, Ref, 100, 0) == Ref +@test Core.Compiler.limit_type_size(Ref{Complex{T} where T}, Ref{Complex{T} where T}, Ref, 100, 0) == Ref{Complex{T} where T} let comparison = Tuple{X, X} where X<:Tuple sig = Tuple{X, X} where X<:comparison ref = Tuple{X, X} where X - @test Core.Compiler.limit_type_size(sig, comparison, comparison, 10) == comparison - @test Core.Compiler.limit_type_size(sig, ref, comparison, 10) == ref - @test Core.Compiler.limit_type_size(Tuple{sig}, Tuple{ref}, comparison, 10) == Tuple{ref} - @test Core.Compiler.limit_type_size(sig, ref, Tuple{comparison}, 10) == sig + @test Core.Compiler.limit_type_size(sig, comparison, comparison, 100, 10) == comparison + @test Core.Compiler.limit_type_size(sig, ref, comparison, 100, 10) == ref + @test Core.Compiler.limit_type_size(Tuple{sig}, Tuple{ref}, comparison, 100, 10) == Tuple{ref} + @test Core.Compiler.limit_type_size(sig, ref, Tuple{comparison}, 100, 10) == sig end diff --git a/test/worlds.jl b/test/worlds.jl index 7a69c6adbe1f6..1b8e4e03546ba 100644 --- a/test/worlds.jl +++ b/test/worlds.jl @@ -66,7 +66,7 @@ A265(fld::Int) = A265(Float64(fld)) mutable struct B265{T} field1::T # dummy arg is present to prevent (::Type{T}){T}(arg) from matching the test calls - B265{T}(field1::Any, dummy::Nothing) where T = new(field1) # prevent generation of outer ctor + B265{T}(field1::Any, dummy::Nothing) where {T} = new(field1) # prevent generation of outer ctor end # define some constructors B265(x::Int, dummy::Nothing) = B265{Int}(x, dummy) @@ -89,8 +89,8 @@ B265(x::Any, dummy::Nothing) = B265{UInt8}(x, dummy) @test (B265_(2)::B265{Float64}).field1 === 2.0e0 @test (B265_(3)::B265{UInt8}).field1 === 0x03 -@test Base.return_types(B265_, (Int,)) == Any[Union{B265{Float64}, B265{Int}, B265{UInt8}}] -@test Core.Compiler.return_type(B265_, (Int,)) == Union{B265{Float64}, B265{Int}, B265{UInt8}} +@test Base.return_types(B265_, (Int,)) == Any[B265] +@test Core.Compiler.return_type(B265_, (Int,)) == B265 # test oldworld call / inference