From 3b57645ef7bd4a1d126be0dc22e607b14bd6f64a Mon Sep 17 00:00:00 2001 From: Keno Fischer Date: Sat, 29 Jul 2023 17:53:46 -0400 Subject: [PATCH] WIP: Add explicitly wrapping versions of integer arithmetic This adds operators `+%`, `-%`, `*%`, which are equivalent to the non-`%` versions, but indicate an explicit semantic expectation that twos completement wrapping behavior is expected and correct. As discussed at JuliaCon 2014 and every year since, users have often requested a way to opt into explicit overflow checking of arithmetic, whether for debugging or because they have regulatory or procedural requirements that expect to be able to do this. Having explicit operators for overflowing semantics allows use cases that depend on overflow behavior for correct functioning to explicitly opt-out of any such checking. I want to explicitly emphasize that there are no plans to change the default behavior of arithmetic in Julia, neither by introducing error checking nor by making it undefined behavior (as in C). The general consensus here is that while overflow checking can be useful, and would be a fine default, even if hardware supported it efficiently (which it doesn't), the performance costs of performing the check (through inhibition of other optimization) is too high. In our experience it also tends to be relatively harmless, even if it can be a very rude awakeing to users coming from Python or other languages with big-default integers. The idea here is simply to give users another tool in their arsenal for checking correctness. Think sanitizers, not language change. This PR includes a macro `@Base.Experimental.make_all_arithmetic_checked`, that will define overrides to make arithmetic checked, but does not include any mechanism (e.g. #50239) to make this fast. What is included in this PR: - Flisp parser changes to parse the new operators - Definitions of the new operators - Some basic replacements in base to give a flavor for using the new operator and make sure it works Still to be done: - [] Parser changes in JuliaSyntax - [] Correct parsing for `+%` by itself, which currently parses as `+(%)` The places to change in base were found by using the above-mentioned macro and running the test suite. I did not work through the tests exhaustively. We have many tests that explicitly expect overflow and many others that we should go through on a case by case basis. The idea here is merely to give an idea of the kind of changes that may be required if overflow checking is enabled. I think they can broadly be classed into: - Crypto and hashing code that explicitly want modular arithmetic - Bit twidelling code for arithmetic tricks (though many of these, particularly in Ryu, could probably be replaced with better abstractions). - UInt8 range checks written by Stefan - Misc --- base/abstractarray.jl | 2 +- base/abstractset.jl | 4 +- base/bool.jl | 15 ++++--- base/char.jl | 11 +++-- base/checked.jl | 26 ++++++----- base/deprecated.jl | 11 +++++ base/docs/basedocs.jl | 69 +++++++++++++++++++++++++++++ base/experimental.jl | 21 +++++++++ base/exports.jl | 3 ++ base/filesystem.jl | 5 ++- base/float.jl | 4 +- base/gmp.jl | 15 ++++--- base/hashing.jl | 40 ++++++++--------- base/int.jl | 5 +++ base/intfuncs.jl | 7 +-- base/io.jl | 5 ++- base/multidimensional.jl | 2 +- base/number.jl | 4 +- base/operators.jl | 2 +- base/pkgid.jl | 2 +- base/pointer.jl | 1 + base/promotion.jl | 4 ++ base/range.jl | 4 +- base/rational.jl | 22 ++++++++- base/regex.jl | 2 +- base/ryu/shortest.jl | 26 +++++------ base/ryu/utils.jl | 30 +++++++++++-- base/special/log.jl | 4 +- base/special/rem_pio2.jl | 6 +-- base/special/trig.jl | 2 +- base/strings/substring.jl | 4 +- base/tuple.jl | 4 +- src/julia-parser.scm | 10 ++--- src/julia-syntax.scm | 3 ++ stdlib/FileWatching/test/pidfile.jl | 2 +- stdlib/Printf/src/Printf.jl | 6 +-- stdlib/Random/src/Xoshiro.jl | 6 +-- stdlib/Random/src/XoshiroSimd.jl | 10 ++--- stdlib/Random/src/generation.jl | 2 +- stdlib/SHA.version | 2 +- test/arrayops.jl | 2 +- test/intfuncs.jl | 30 +++++++------ test/ryu.jl | 2 +- test/testhelpers/Furlongs.jl | 4 +- 44 files changed, 310 insertions(+), 131 deletions(-) diff --git a/base/abstractarray.jl b/base/abstractarray.jl index f946b1458a776..116053c11897a 100644 --- a/base/abstractarray.jl +++ b/base/abstractarray.jl @@ -3405,7 +3405,7 @@ pushfirst!(A, a, b, c...) = pushfirst!(pushfirst!(A, c...), a, b) const hash_abstractarray_seed = UInt === UInt64 ? 0x7e2d6fb6448beb77 : 0xd4514ce5 function hash(A::AbstractArray, h::UInt) - h += hash_abstractarray_seed + h +%= hash_abstractarray_seed # Axes are themselves AbstractArrays, so hashing them directly would stack overflow # Instead hash the tuple of firsts and lasts along each dimension h = hash(map(first, axes(A)), h) diff --git a/base/abstractset.jl b/base/abstractset.jl index 5d0d65dad2de6..fe8f015254d43 100644 --- a/base/abstractset.jl +++ b/base/abstractset.jl @@ -93,7 +93,9 @@ max_values(T::Union{map(X -> Type{X}, BitIntegerSmall_types)...}) = 1 << (8*size function max_values(T::Union) a = max_values(T.a)::Int b = max_values(T.b)::Int - return max(a, b, a + b) + r, o = add_with_overflow(a, b) + o && return typemax(Int) + return r end max_values(::Type{Bool}) = 2 max_values(::Type{Nothing}) = 1 diff --git a/base/bool.jl b/base/bool.jl index d7dcf76caa91b..fdfde93e8dd7c 100644 --- a/base/bool.jl +++ b/base/bool.jl @@ -160,12 +160,17 @@ isone(x::Bool) = x ## do arithmetic as Int ## -+(x::Bool) = Int(x) --(x::Bool) = -Int(x) - -+(x::Bool, y::Bool) = Int(x) + Int(y) --(x::Bool, y::Bool) = Int(x) - Int(y) ++(x::Bool) = Int(x) ++%(x::Bool) = Int(x) +-(x::Bool) = -%(Int(x)) +-%(x::Bool) = -%(Int(x)) + ++(x::Bool, y::Bool) = Int(x) +% Int(y) +-(x::Bool, y::Bool) = Int(x) -% Int(y) ++%(x::Bool, y::Bool) = Int(x) +% Int(y) +-%(x::Bool, y::Bool) = Int(x) -% Int(y) *(x::Bool, y::Bool) = x & y +*%(x::Bool, y::Bool) = x & y ^(x::Bool, y::Bool) = x | !y ^(x::Integer, y::Bool) = ifelse(y, x, one(x)) diff --git a/base/char.jl b/base/char.jl index 08d661c41de56..6ff2b86a734ec 100644 --- a/base/char.jl +++ b/base/char.jl @@ -223,8 +223,9 @@ isless(x::AbstractChar, y::AbstractChar) = isless(Char(x), Char(y)) hash(x::AbstractChar, h::UInt) = hash(Char(x), h) widen(::Type{T}) where {T<:AbstractChar} = T +@inline -%(x::AbstractChar, y::AbstractChar) = Int(x) -% Int(y) @inline -(x::AbstractChar, y::AbstractChar) = Int(x) - Int(y) -@inline function -(x::T, y::Integer) where {T<:AbstractChar} +@inline function -%(x::T, y::Integer) where {T<:AbstractChar} if x isa Char u = Int32((bitcast(UInt32, x) >> 24) % Int8) if u >= 0 # inline the runtime fast path @@ -234,7 +235,7 @@ widen(::Type{T}) where {T<:AbstractChar} = T end return T(Int32(x) - Int32(y)) end -@inline function +(x::T, y::Integer) where {T<:AbstractChar} +@inline function +%(x::T, y::Integer) where {T<:AbstractChar} if x isa Char u = Int32((bitcast(UInt32, x) >> 24) % Int8) if u >= 0 # inline the runtime fast path @@ -244,7 +245,11 @@ end end return T(Int32(x) + Int32(y)) end -@inline +(x::Integer, y::AbstractChar) = y + x +@inline +%(x::Integer, y::AbstractChar) = y + x + +-(x::AbstractChar, y::Integer) = x -% y ++(x::AbstractChar, y::Integer) = x +% y ++(x::Integer, y::AbstractChar) = x +% y # `print` should output UTF-8 by default for all AbstractChar types. # (Packages may implement other IO subtypes to specify different encodings.) diff --git a/base/checked.jl b/base/checked.jl index d5b4112397e84..5fa9c9fd019fa 100644 --- a/base/checked.jl +++ b/base/checked.jl @@ -21,7 +21,7 @@ import Core.Intrinsics: checked_srem_int, checked_uadd_int, checked_usub_int, checked_umul_int, checked_udiv_int, checked_urem_int -import ..no_op_err, ..@inline, ..@noinline, ..checked_length +import ..no_op_err, ..@inline, ..@noinline, ..checked_length, ..BitInteger # define promotion behavior for checked operations checked_add(x::Integer, y::Integer) = checked_add(promote(x,y)...) @@ -98,7 +98,7 @@ throw_overflowerr_negation(x) = (@noinline; throw(OverflowError(Base.invokelatest(string, "checked arithmetic: cannot compute -x for x = ", x, "::", typeof(x))))) if BrokenSignedInt != Union{} function checked_neg(x::BrokenSignedInt) - r = -x + r = -%(x) (x<0) & (r<0) && throw_overflowerr_negation(x) r end @@ -140,11 +140,11 @@ Calculates `r = x+y`, with the flag `f` indicating whether overflow has occurred function add_with_overflow end add_with_overflow(x::T, y::T) where {T<:SignedInt} = checked_sadd_int(x, y) add_with_overflow(x::T, y::T) where {T<:UnsignedInt} = checked_uadd_int(x, y) -add_with_overflow(x::Bool, y::Bool) = (x+y, false) +add_with_overflow(x::Bool, y::Bool) = (x +% y, false) if BrokenSignedInt != Union{} function add_with_overflow(x::T, y::T) where T<:BrokenSignedInt - r = x + y + r = x +% y # x and y have the same sign, and the result has a different sign f = (x<0) == (y<0) != (r<0) r, f @@ -154,7 +154,7 @@ if BrokenUnsignedInt != Union{} function add_with_overflow(x::T, y::T) where T<:BrokenUnsignedInt # x + y > typemax(T) # Note: ~y == -y-1 - x + y, x > ~y + x +% y, x > ~y end end @@ -171,7 +171,11 @@ The overflow protection may impose a perceptible performance penalty. """ function checked_add(x::T, y::T) where T<:Integer @inline - z, b = add_with_overflow(x, y) + zb = add_with_overflow(x, y) + # Avoid use of tuple destructuring, which uses aritmetic internally, + # so that this can be used as a replacement for + + z = getfield(zb, 1) + b = getfield(zb, 2) b && throw_overflowerr_binaryop(:+, x, y) z end @@ -206,7 +210,7 @@ sub_with_overflow(x::Bool, y::Bool) = (x-y, false) if BrokenSignedInt != Union{} function sub_with_overflow(x::T, y::T) where T<:BrokenSignedInt - r = x - y + r = x -% y # x and y have different signs, and the result has a different sign than x f = (x<0) != (y<0) == (r<0) r, f @@ -215,7 +219,7 @@ end if BrokenUnsignedInt != Union{} function sub_with_overflow(x::T, y::T) where T<:BrokenUnsignedInt # x - y < 0 - x - y, x < y + x -% y, x < y end end @@ -242,7 +246,7 @@ Calculates `r = x*y`, with the flag `f` indicating whether overflow has occurred function mul_with_overflow end mul_with_overflow(x::T, y::T) where {T<:SignedInt} = checked_smul_int(x, y) mul_with_overflow(x::T, y::T) where {T<:UnsignedInt} = checked_umul_int(x, y) -mul_with_overflow(x::Bool, y::Bool) = (x*y, false) +mul_with_overflow(x::Bool, y::Bool) = (x *% y, false) if BrokenSignedIntMul != Union{} && BrokenSignedIntMul != Int128 function mul_with_overflow(x::T, y::T) where T<:BrokenSignedIntMul @@ -273,14 +277,14 @@ if Int128 <: BrokenSignedIntMul else false end - x*y, f + x *% y, f end end if UInt128 <: BrokenUnsignedIntMul # Avoid BigInt function mul_with_overflow(x::T, y::T) where T<:UInt128 # x * y > typemax(T) - x * y, y > 0 && x > fld(typemax(T), y) + x *% y, y > 0 && x > fld(typemax(T), y) end end diff --git a/base/deprecated.jl b/base/deprecated.jl index 1b661716cc2d9..29dec793d980b 100644 --- a/base/deprecated.jl +++ b/base/deprecated.jl @@ -384,3 +384,14 @@ macro pure(ex) end # END 1.10 deprecations + +# BEGIN 1.11 deprecations + +# These operators are new in 1.11, but these fallback methods are added for +# compatibility while packages adjust to defining both operators, to allow +# Base and other packages to start using these. +*%(a::T, b::T) where {T} = *(a, b) ++%(a::T, b::T) where {T} = +(a, b) +-%(a::T, b::T) where {T} = -(a, b) + +# END 1.11 deprecations diff --git a/base/docs/basedocs.jl b/base/docs/basedocs.jl index fd8c35a5fdf76..21b947c8c9faf 100644 --- a/base/docs/basedocs.jl +++ b/base/docs/basedocs.jl @@ -2644,6 +2644,30 @@ julia> +(1, 20, 4) """ (+)(x, y...) +""" + +%(x::Integer, y::Integer...) + +Addition operator with semantic wrapping. In the default Julia environment, this +is equivalent to the regular addition operator `+`. However, some users may choose to overwrite +`+` in their local environment to perform checked arithmetic instead (e.g. using +[`Experimental.@make_all_arithmetic_checked`](@ref)). The `+%` operator may be used to indicate +that wrapping behavior is semantically expected and correct and should thus be exempted from +any opt-in overflow checking. + +# Examples +```jldoctest +julia> 1 +% 20 +% 4 +25 + +julia> +%(1, 20, 4) +25 + +julia> typemax(Int) +% 1 +-9223372036854775808 +``` +""" +(+%)(x, y...) + """ -(x) @@ -2683,6 +2707,27 @@ julia> -(2, 4.5) """ -(x, y) +""" + -%(x::Integer, y::Integer...) + +Subtraction operator with semantic wrapping. In the default Julia environment, this +is equivalent to the regular subtraction operator `-`. However, some users may choose to overwrite +`-` in their local environment to perform checked arithmetic instead (e.g. using +[`Experimental.@make_all_arithmetic_checked`](@ref)). The `-%` operator may be used to indicate +that wrapping behavior is semantically expected and correct and should thus be exempted from +any opt-in overflow checking. + +# Examples +```jldoctest +julia> 2 -% 3 +-1 + +julia> -(typemin(Int)) +-9223372036854775808 +``` +""" +(-%)(x, y...) + """ *(x, y...) @@ -2699,6 +2744,30 @@ julia> *(2, 7, 8) """ (*)(x, y...) +""" + *%(x::Integer, y::Integer, z::Integer...) + +Multiplication operator with semantic wrapping. In the default Julia environment, this +is equivalent to the regular multiplication operator `*`. However, some users may choose to overwrite +`*` in their local environment to perform checked arithmetic instead (e.g. using +[`Experimental.@make_all_arithmetic_checked`](@ref)). The `*%` operator may be used to indicate +that wrapping behavior is semantically expected and correct and should thus be exempted from +any opt-in overflow checking. + +# Examples +```jldoctest +julia> 2 *% 7 *% 8 +112 + +julia> *(2, 7, 8) +112 + +julia> 0xff *% 0xff +0x01 +``` +""" +(*%)(x, y, z...) + """ /(x, y) diff --git a/base/experimental.jl b/base/experimental.jl index cc8d368023b49..cef0b1bbc4fb6 100644 --- a/base/experimental.jl +++ b/base/experimental.jl @@ -368,4 +368,25 @@ adding them to the global method table. """ :@MethodTable +""" + Experimental.@make_all_arithmetic_checked() + +This macro defines methods that overwrite the base definition of basic arithmetic (+,-,*), +to use their checked variants instead. Explicitly overflowing arithmetic operators (+%,-%,*%) +are not affected. + +!!! warning + This macro is temporary and will likely be replaced by a more complete mechanism in the + future. It is subject to change or removal without notice. +""" +macro make_all_arithmetic_checked() + esc(quote + Base.:(-)(x::BitInteger) = Base.Checked.checked_neg(x) + Base.:(-)(x::T, y::T) where {T<:BitInteger} = Base.Checked.checked_sub(x, y) + Base.:(+)(x::T, y::T) where {T<:BitInteger} = Base.Checked.checked_add(x, y) + Base.:(*)(x::T, y::T) where {T<:BitInteger} = Base.Checked.checked_mul(x, y) + Base.:(-)(x::AbstractChar, y::AbstractChar) = Int(x) - Int(y) + end) +end + end diff --git a/base/exports.jl b/base/exports.jl index 0959fa1c391e2..14bf86a8247c7 100644 --- a/base/exports.jl +++ b/base/exports.jl @@ -183,8 +183,11 @@ export ÷, &, *, + *%, +, + +%, -, + -%, /, //, <, diff --git a/base/filesystem.jl b/base/filesystem.jl index d291dd3b31630..61f506d5c508e 100644 --- a/base/filesystem.jl +++ b/base/filesystem.jl @@ -200,9 +200,10 @@ end function read(f::File, ::Type{Char}) b0 = read(f, UInt8) - l = 0x08 * (0x04 - UInt8(leading_ones(b0))) + lo = UInt8(leading_ones(b0)) c = UInt32(b0) << 24 - if l ≤ 0x10 + if 0x02 ≤ lo ≤ 0x04 + l = 0x08 * (0x04 - lo) s = 16 while s ≥ l && !eof(f) # this works around lack of peek(::File) diff --git a/base/float.jl b/base/float.jl index fc4cef09b48ad..f2a27e7a6dcf1 100644 --- a/base/float.jl +++ b/base/float.jl @@ -650,7 +650,7 @@ function hash(x::Float64, h::UInt) elseif isnan(x) return hx_NaN ⊻ h # NaN does not have a stable bit pattern end - return hash_uint64(bitcast(UInt64, x)) - 3h + return hash_uint64(bitcast(UInt64, x)) -% (3 *% h) end hash(x::Float32, h::UInt) = hash(Float64(x), h) @@ -665,7 +665,7 @@ function hash(x::Float16, h::UInt) elseif isnan(x) return hx_NaN ⊻ h # NaN does not have a stable bit pattern end - return hash_uint64(bitcast(UInt64, Float64(x))) - 3h + return hash_uint64(bitcast(UInt64, Float64(x))) - (3 *% h) end ## generic hashing for rational values ## diff --git a/base/gmp.jl b/base/gmp.jl index 8a1451be7a590..6699362f98973 100644 --- a/base/gmp.jl +++ b/base/gmp.jl @@ -4,7 +4,7 @@ module GMP export BigInt -import .Base: *, +, -, /, <, <<, >>, >>>, <=, ==, >, >=, ^, (~), (&), (|), xor, nand, nor, +import .Base: *, *%, +, +%, -, -%, /, <, <<, >>, >>>, <=, ==, >, >=, ^, (~), (&), (|), xor, nand, nor, binomial, cmp, convert, div, divrem, factorial, cld, fld, gcd, gcdx, lcm, mod, ndigits, promote_rule, rem, show, isqrt, string, powermod, sum, prod, trailing_zeros, trailing_ones, count_ones, count_zeros, tryparse_internal, @@ -334,7 +334,7 @@ function BigInt(x::Integer) isbits(x) && typemin(Clong) <= x <= typemax(Clong) && return BigInt((x % Clong)::Clong) nd = ndigits(x, base=2) z = MPZ.realloc2(nd) - ux = unsigned(x < 0 ? -x : x) + ux = unsigned(x < 0 ? -%(x) : x) size = 0 limbnbits = sizeof(Limb) << 3 while nd > 0 @@ -494,6 +494,7 @@ big(n::Integer) = convert(BigInt, n) # Binary ops for (fJ, fC) in ((:+, :add), (:-,:sub), (:*, :mul), + (:+%, :add), (:-%,:sub), (:*%, :mul), (:mod, :fdiv_r), (:rem, :tdiv_r), (:gcd, :gcd), (:lcm, :lcm), (:&, :and), (:|, :ior), (:xor, :xor)) @@ -552,10 +553,10 @@ end -(x::BigInt, c::CulongMax) = MPZ.sub_ui(x, c) -(c::CulongMax, x::BigInt) = MPZ.ui_sub(c, x) -+(x::BigInt, c::ClongMax) = c < 0 ? -(x, -(c % Culong)) : x + convert(Culong, c) -+(c::ClongMax, x::BigInt) = c < 0 ? -(x, -(c % Culong)) : x + convert(Culong, c) --(x::BigInt, c::ClongMax) = c < 0 ? +(x, -(c % Culong)) : -(x, convert(Culong, c)) --(c::ClongMax, x::BigInt) = c < 0 ? -(x + -(c % Culong)) : -(convert(Culong, c), x) ++(x::BigInt, c::ClongMax) = c < 0 ? -(x, -%(c % Culong)) : x + convert(Culong, c) ++(c::ClongMax, x::BigInt) = c < 0 ? -(x, -%(c % Culong)) : x + convert(Culong, c) +-(x::BigInt, c::ClongMax) = c < 0 ? +(x, -%(c % Culong)) : -(x, convert(Culong, c)) +-(c::ClongMax, x::BigInt) = c < 0 ? -(x + -%(c % Culong)) : -(convert(Culong, c), x) *(x::BigInt, c::CulongMax) = MPZ.mul_ui(x, c) *(c::CulongMax, x::BigInt) = x * c @@ -873,7 +874,7 @@ if Limb === UInt64 === UInt return hash(unsafe_load(ptr), h) elseif sz == -1 limb = unsafe_load(ptr) - limb <= typemin(Int) % UInt && return hash(-(limb % Int), h) + limb <= typemin(Int) % UInt && return hash(-%(limb % Int), h) end pow = trailing_zeros(x) nd = Base.ndigits0z(x, 2) diff --git a/base/hashing.jl b/base/hashing.jl index 5dbae09123bd6..d325b30034140 100644 --- a/base/hashing.jl +++ b/base/hashing.jl @@ -29,11 +29,11 @@ See also: [`objectid`](@ref), [`Dict`](@ref), [`Set`](@ref). """ hash(x::Any) = hash(x, zero(UInt)) hash(w::WeakRef, h::UInt) = hash(w.value, h) -hash(T::Type, h::UInt) = hash_uint(3h - ccall(:jl_type_hash, UInt, (Any,), T)) +hash(T::Type, h::UInt) = hash_uint(3 *% h -% ccall(:jl_type_hash, UInt, (Any,), T)) ## hashing general objects ## -hash(@nospecialize(x), h::UInt) = hash_uint(3h - objectid(x)) +hash(@nospecialize(x), h::UInt) = hash_uint(3 *% h -% objectid(x)) hash(x::Symbol) = objectid(x) @@ -41,34 +41,34 @@ hash(x::Symbol) = objectid(x) function hash_64_64(n::UInt64) a::UInt64 = n - a = ~a + a << 21 + a = ~a +% a << 21 a = a ⊻ a >> 24 - a = a + a << 3 + a << 8 + a = a +% a << 3 +% a << 8 a = a ⊻ a >> 14 - a = a + a << 2 + a << 4 + a = a +% a << 2 +% a << 4 a = a ⊻ a >> 28 - a = a + a << 31 + a = a +% a << 31 return a end function hash_64_32(n::UInt64) a::UInt64 = n - a = ~a + a << 18 + a = ~a +% a << 18 a = a ⊻ a >> 31 a = a * 21 a = a ⊻ a >> 11 - a = a + a << 6 + a = a +% a << 6 a = a ⊻ a >> 22 return a % UInt32 end function hash_32_32(n::UInt32) a::UInt32 = n - a = a + 0x7ed55d16 + a << 12 + a = a +% 0x7ed55d16 +% a << 12 a = a ⊻ 0xc761c23c ⊻ a >> 19 - a = a + 0x165667b1 + a << 5 - a = a + 0xd3a2646c ⊻ a << 9 - a = a + 0xfd7046c5 + a << 3 + a = a +% 0x165667b1 +% a << 5 + a = a +% 0xd3a2646c ⊻ a << 9 + a = a +% 0xfd7046c5 +% a << 3 a = a ⊻ 0xb55a4f09 ⊻ a >> 16 return a end @@ -83,8 +83,8 @@ end ## efficient value-based hashing of integers ## -hash(x::Int64, h::UInt) = hash_uint64(bitcast(UInt64, x)) - 3h -hash(x::UInt64, h::UInt) = hash_uint64(x) - 3h +hash(x::Int64, h::UInt) = hash_uint64(bitcast(UInt64, x)) -% (3 *% h) +hash(x::UInt64, h::UInt) = hash_uint64(x) -% (3 *% h) hash(x::Union{Bool,Int8,UInt8,Int16,UInt16,Int32,UInt32}, h::UInt) = hash(Int64(x), h) function hash_integer(n::Integer, h::UInt) @@ -101,11 +101,11 @@ end ## symbol & expression hashing ## if UInt === UInt64 - hash(x::Expr, h::UInt) = hash(x.args, hash(x.head, h + 0x83c7900696d26dc6)) - hash(x::QuoteNode, h::UInt) = hash(x.value, h + 0x2c97bf8b3de87020) + hash(x::Expr, h::UInt) = hash(x.args, hash(x.head, h +% 0x83c7900696d26dc6)) + hash(x::QuoteNode, h::UInt) = hash(x.value, h +% 0x2c97bf8b3de87020) else - hash(x::Expr, h::UInt) = hash(x.args, hash(x.head, h + 0x96d26dc6)) - hash(x::QuoteNode, h::UInt) = hash(x.value, h + 0x469d72af) + hash(x::Expr, h::UInt) = hash(x.args, hash(x.head, h +% 0x96d26dc6)) + hash(x::QuoteNode, h::UInt) = hash(x.value, h +% 0x469d72af) end ## hashing strings ## @@ -114,6 +114,6 @@ const memhash = UInt === UInt64 ? :memhash_seed : :memhash32_seed const memhash_seed = UInt === UInt64 ? 0x71e729fd56419c81 : 0x56419c81 @assume_effects :total function hash(s::String, h::UInt) - h += memhash_seed - ccall(memhash, UInt, (Ptr{UInt8}, Csize_t, UInt32), s, sizeof(s), h % UInt32) + h + h +%= memhash_seed + ccall(memhash, UInt, (Ptr{UInt8}, Csize_t, UInt32), s, sizeof(s), h % UInt32) +% h end diff --git a/base/int.jl b/base/int.jl index 4b2f542bba788..17cba868a0385 100644 --- a/base/int.jl +++ b/base/int.jl @@ -87,6 +87,11 @@ signed(::Type{T}) where {T<:Signed} = T (+)(x::T, y::T) where {T<:BitInteger} = add_int(x, y) (*)(x::T, y::T) where {T<:BitInteger} = mul_int(x, y) +(-%)(x::BitInteger) = neg_int(x) +(-%)(x::T, y::T) where {T<:BitInteger} = sub_int(x, y) +(+%)(x::T, y::T) where {T<:BitInteger} = add_int(x, y) +(*%)(x::T, y::T) where {T<:BitInteger} = mul_int(x, y) + negate(x) = -x negate(x::Unsigned) = -convert(Signed, x) #widenegate(x) = -convert(widen(signed(typeof(x))), x) diff --git a/base/intfuncs.jl b/base/intfuncs.jl index 1b007700f4331..711cf12c22a12 100644 --- a/base/intfuncs.jl +++ b/base/intfuncs.jl @@ -272,7 +272,7 @@ to_power_type(x) = convert(Base._return_type(*, Tuple{typeof(x), typeof(x)}), x) "\nMake x a float matrix by adding a zero decimal ", "(e.g., [2.0 1.0;1.0 0.0]^", p, " instead of [2 1;1 0]^", p, ")", "or write float(x)^", p, " or Rational.(x)^", p, "."))) -@assume_effects :terminates_locally function power_by_squaring(x_, p::Integer) +@assume_effects :terminates_locally function power_by_squaring(*, x_, p::Integer) x = to_power_type(x_) if p == 1 return copy(x) @@ -301,6 +301,7 @@ to_power_type(x) = convert(Base._return_type(*, Tuple{typeof(x), typeof(x)}), x) end return y end +power_by_squaring(x_, p::Integer) = power_by_squaring(*, x_, p) power_by_squaring(x::Bool, p::Unsigned) = ((p==0) | x) function power_by_squaring(x::Bool, p::Integer) p < 0 && !x && throw_domerr_powbysq(x, p) @@ -711,7 +712,7 @@ function bin(x::Unsigned, pad::Int, neg::Bool) i = n @inbounds while i >= 4 b = UInt32((x % UInt8)::UInt8) - d = 0x30303030 + ((b * 0x08040201) >> 0x3) & 0x01010101 + d = 0x30303030 +% ((b *% 0x08040201) >> 0x3) & 0x01010101 a[i-3] = (d >> 0x00) % UInt8 a[i-2] = (d >> 0x08) % UInt8 a[i-1] = (d >> 0x10) % UInt8 @@ -874,7 +875,7 @@ function bitstring(x::T) where {T} i = sz @inbounds while i >= 4 b = UInt32(sizeof(T) == 1 ? bitcast(UInt8, x) : trunc_int(UInt8, x)) - d = 0x30303030 + ((b * 0x08040201) >> 0x3) & 0x01010101 + d = 0x30303030 +% ((b *% 0x08040201) >> 0x3) & 0x01010101 str[i-3] = (d >> 0x00) % UInt8 str[i-2] = (d >> 0x08) % UInt8 str[i-1] = (d >> 0x10) % UInt8 diff --git a/base/io.jl b/base/io.jl index be43a954c621e..ddcb4377f9194 100644 --- a/base/io.jl +++ b/base/io.jl @@ -884,9 +884,10 @@ end function read(io::IO, ::Type{Char}) b0 = read(io, UInt8)::UInt8 - l = 0x08 * (0x04 - UInt8(leading_ones(b0))) + lo = UInt8(leading_ones(b0)) c = UInt32(b0) << 24 - if l ≤ 0x10 + if 0x02 ≤ lo ≤ 0x04 + l = 0x08 * (0x04 - lo) s = 16 while s ≥ l && !eof(io)::Bool peek(io) & 0xc0 == 0x80 || break diff --git a/base/multidimensional.jl b/base/multidimensional.jl index f793df068ec5a..dc8c1e9559f45 100644 --- a/base/multidimensional.jl +++ b/base/multidimensional.jl @@ -138,7 +138,7 @@ module IteratorsMD # hashing const cartindexhash_seed = UInt == UInt64 ? 0xd60ca92f8284b8b0 : 0xf2ea7c2e function Base.hash(ci::CartesianIndex, h::UInt) - h += cartindexhash_seed + h +%= cartindexhash_seed for i in ci.I h = hash(i, h) end diff --git a/base/number.jl b/base/number.jl index 923fc907d4038..c4dfb8997f9f1 100644 --- a/base/number.jl +++ b/base/number.jl @@ -202,7 +202,7 @@ julia> flipsign(5, -3) -5 ``` """ -flipsign(x::Real, y::Real) = ifelse(signbit(y), -x, +x) # the + is for type-stability on Bool +flipsign(x::Real, y::Real) = signbit(y) ? -x : +x # the + is for type-stability on Bool """ copysign(x, y) -> z @@ -218,7 +218,7 @@ julia> copysign(-1, 2) 1 ``` """ -copysign(x::Real, y::Real) = ifelse(signbit(x)!=signbit(y), -x, +x) +copysign(x::Real, y::Real) = signbit(x)!=signbit(y) ? -x : +x conj(x::Real) = x transpose(x::Number) = x diff --git a/base/operators.jl b/base/operators.jl index 3f0f8bc49b164..08af5d4de81c8 100644 --- a/base/operators.jl +++ b/base/operators.jl @@ -579,7 +579,7 @@ function afoldl(op, a, bs...) end setfield!(typeof(afoldl).name.mt, :max_args, 34, :monotonic) -for op in (:+, :*, :&, :|, :xor, :min, :max, :kron) +for op in (:+, :(+%), :*, :(*%), :&, :|, :xor, :min, :max, :kron) @eval begin # note: these definitions must not cause a dispatch loop when +(a,b) is # not defined, and must only try to call 2-argument definitions, so diff --git a/base/pkgid.jl b/base/pkgid.jl index 20d9de559b334..495af7f971e6d 100644 --- a/base/pkgid.jl +++ b/base/pkgid.jl @@ -17,7 +17,7 @@ end ==(a::PkgId, b::PkgId) = a.uuid == b.uuid && a.name == b.name function hash(pkg::PkgId, h::UInt) - h += 0xc9f248583a0ca36c % UInt + h +%= 0xc9f248583a0ca36c % UInt h = hash(pkg.uuid, h) h = hash(pkg.name, h) return h diff --git a/base/pointer.jl b/base/pointer.jl index a47f1e38edb9b..fb8a41c3ae2ab 100644 --- a/base/pointer.jl +++ b/base/pointer.jl @@ -278,6 +278,7 @@ isless(x::Ptr{T}, y::Ptr{T}) where {T} = x < y ==(x::Ptr, y::Ptr) = UInt(x) == UInt(y) <(x::Ptr, y::Ptr) = UInt(x) < UInt(y) -(x::Ptr, y::Ptr) = UInt(x) - UInt(y) +-%(x::Ptr, y::Ptr) = UInt(x) -% UInt(y) +(x::Ptr, y::Integer) = oftype(x, add_ptr(UInt(x), (y % UInt) % UInt)) -(x::Ptr, y::Integer) = oftype(x, sub_ptr(UInt(x), (y % UInt) % UInt)) diff --git a/base/promotion.jl b/base/promotion.jl index 6e32bd7a42efa..aa252aac4fe42 100644 --- a/base/promotion.jl +++ b/base/promotion.jl @@ -424,6 +424,10 @@ end -(x::Number, y::Number) = -(promote(x,y)...) /(x::Number, y::Number) = /(promote(x,y)...) ++%(x::Number, y::Number) = +%(promote(x,y)...) +*%(x::Number, y::Number) = *%(promote(x,y)...) +-%(x::Number, y::Number) = -%(promote(x,y)...) + """ ^(x, y) diff --git a/base/range.jl b/base/range.jl index e8ffe10e2ba7f..2c7f8f82f5e65 100644 --- a/base/range.jl +++ b/base/range.jl @@ -345,7 +345,7 @@ function steprange_last(start, step, stop)::typeof(stop) else # Compute absolute value of difference between `start` and `stop` # (to simplify handling both signed and unsigned T and checking for signed overflow): - absdiff, absstep = stop > start ? (stop - start, step) : (start - stop, -step) + absdiff, absstep = stop > start ? (stop -% start, step) : (start -% stop, -step) # Compute remainder as a nonnegative number: if absdiff isa Signed && absdiff < zero(absdiff) @@ -997,7 +997,7 @@ function getindex(r::AbstractUnitRange, s::AbstractUnitRange{T}) where {T<:Integ f = first(r) start = oftype(f, f + first(s) - firstindex(r)) len = length(s) - stop = oftype(f, start + (len - oneunit(len))) + stop = start + oftype(f, len - oneunit(len)) return range(start, stop) end end diff --git a/base/rational.jl b/base/rational.jl index 5b9ff99ea7a6c..f4e2bc4158834 100644 --- a/base/rational.jl +++ b/base/rational.jl @@ -317,7 +317,16 @@ function +(x::Rational, y::Rational) return xp end xd, yd = divgcd(promote(x.den, y.den)...) - Rational(checked_add(checked_mul(x.num,yd), checked_mul(y.num,xd)), checked_mul(x.den,yd)) + Rational(checked_add(checked_mul(x.num, yd), checked_mul(y.num, xd)), checked_mul(x.den, yd)) +end + +function +%(x::Rational, y::Rational) + xp, yp = promote(x, y)::NTuple{2,Rational} + if isinf(x) && x == y + return xp + end + xd, yd = divgcd(promote(x.den, y.den)...) + Rational(+%(+*(x.num,yd), +*(y.num,xd)), +*(x.den,yd)) end function -(x::Rational, y::Rational) @@ -326,7 +335,16 @@ function -(x::Rational, y::Rational) return xp end xd, yd = divgcd(promote(x.den, y.den)...) - Rational(checked_sub(checked_mul(x.num,yd), checked_mul(y.num,xd)), checked_mul(x.den,yd)) + Rational(checked_sub(checked_mul(x.num, yd), checked_mul(y.num, xd)), checked_mul(x.den, yd)) +end + +function -%(x::Rational, y::Rational) + xp, yp = promote(x, y)::NTuple{2,Rational} + if isinf(x) && x == -y + return xp + end + xd, yd = divgcd(promote(x.den, y.den)...) + Rational(-%(*%(x.num, yd), *%(y.num, xd)), *%(x.den, yd)) end for (op,chop) in ((:rem,:rem), (:mod,:mod)) diff --git a/base/regex.jl b/base/regex.jl index 109724903823f..b9bcba8490b64 100644 --- a/base/regex.jl +++ b/base/regex.jl @@ -417,7 +417,7 @@ function match(re::Regex, str::Union{SubString{String}, String}, idx::Integer, cap = Union{Nothing,SubString{String}}[unsafe_load(p,2i+1) == PCRE.UNSET ? nothing : SubString(str, unsafe_load(p,2i+1)+1, prevind(str, unsafe_load(p,2i+2)+1)) for i=1:n] - off = Int[ unsafe_load(p,2i+1)+1 for i=1:n ] + off = Int[ unsafe_load(p,2i+1) == PCRE.UNSET ? 0 : unsafe_load(p,2i+1)+1 for i=1:n ] result = RegexMatch(mat, cap, unsafe_load(p,1)+1, off, re) PCRE.free_match_data(data) return result diff --git a/base/ryu/shortest.jl b/base/ryu/shortest.jl index aaa62ba33c703..ad945abf0304d 100644 --- a/base/ryu/shortest.jl +++ b/base/ryu/shortest.jl @@ -14,7 +14,7 @@ integer. If a `maxsignif` argument is provided, then `b < maxsignif`. # mf * 2^ef == f mf = (one(U) << significand_bits(T)) | m ef = e - exponent_bias(T) - significand_bits(T) - f_isinteger = mf & ((one(U) << -ef) - one(U)) == 0 + f_isinteger = mf & ((one(U) << -ef) -% one(U)) == 0 if ef > 0 || ef < -Base.significand_bits(T) || !f_isinteger # fixup subnormals @@ -56,7 +56,7 @@ integer. If a `maxsignif` argument is provided, then `b < maxsignif`. end end if q <= qinvbound(T) - if ((v % UInt32) - 5 * div(v, 5)) == 0 + if ((v % UInt32) -% 5 * div(v, 5)) == 0 b_allzero = pow5(v, q) elseif mf_iseven a_allzero = pow5(u, q) @@ -99,9 +99,9 @@ integer. If a `maxsignif` argument is provided, then `b < maxsignif`. if c_div10 <= a_div10 break end - a_mod10 = (a % UInt32) - UInt32(10) * (a_div10 % UInt32) + a_mod10 = (a % UInt32) -% UInt32(10) *% (a_div10 % UInt32) b_div10 = div(b, 10) - b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + b_mod10 = (b % UInt32) -% UInt32(10) *% (b_div10 % UInt32) a_allzero &= a_mod10 == 0 b_allzero &= b_lastdigit == 0 b_lastdigit = b_mod10 % UInt8 @@ -113,13 +113,13 @@ integer. If a `maxsignif` argument is provided, then `b < maxsignif`. if a_allzero while true a_div10 = div(a, 10) - a_mod10 = (a % UInt32) - UInt32(10) * (a_div10 % UInt32) + a_mod10 = (a % UInt32) -% UInt32(10) *% (a_div10 % UInt32) if a_mod10 != 0 && (maxsignif === nothing || b < maxsignif) break end c_div10 = div(c, 10) b_div10 = div(b, 10) - b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + b_mod10 = (b % UInt32) -% UInt32(10) *% (b_div10 % UInt32) b_allzero &= b_lastdigit == 0 b_lastdigit = b_mod10 % UInt8 b = b_div10 @@ -139,7 +139,7 @@ integer. If a `maxsignif` argument is provided, then `b < maxsignif`. a_div100 = div(a, 100) if c_div100 > a_div100 b_div100 = div(b, 100) - b_mod100 = (b % UInt32) - UInt32(100) * (b_div100 % UInt32) + b_mod100 = (b % UInt32) -% UInt32(100) *% (b_div100 % UInt32) roundup = b_mod100 >= 50 b = b_div100 c = c_div100 @@ -153,7 +153,7 @@ integer. If a `maxsignif` argument is provided, then `b < maxsignif`. break end b_div10 = div(b, 10) - b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + b_mod10 = (b % UInt32) -% UInt32(10) *% (b_div10 % UInt32) roundup = b_mod10 >= 5 b = b_div10 c = c_div10 @@ -166,7 +166,7 @@ integer. If a `maxsignif` argument is provided, then `b < maxsignif`. # reduce to max significant digits while true b_div10 = div(b, 10) - b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + b_mod10 = (b % UInt32) -% UInt32(10) *% (b_div10 % UInt32) if b <= maxsignif break end @@ -180,7 +180,7 @@ integer. If a `maxsignif` argument is provided, then `b < maxsignif`. # remove trailing zeros while true b_div10 = div(b, 10) - b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + b_mod10 = (b % UInt32) -% UInt32(10) *% (b_div10 % UInt32) if b_mod10 != 0 break end @@ -200,7 +200,7 @@ integer. If a `maxsignif` argument is provided, then `b < maxsignif`. # reduce to max significant digits while true b_div10 = div(b, 10) - b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + b_mod10 = (b % UInt32) -% UInt32(10) *% (b_div10 % UInt32) if b <= maxsignif break end @@ -213,7 +213,7 @@ integer. If a `maxsignif` argument is provided, then `b < maxsignif`. end while true b_div10 = div(b, 10) - b_mod10 = (b % UInt32) - UInt32(10) * (b_div10 % UInt32) + b_mod10 = (b % UInt32) -% UInt32(10) *% (b_div10 % UInt32) if b_mod10 != 0 break end @@ -353,7 +353,7 @@ function writeshortest(buf::Vector{UInt8}, pos, x::T, ptr2 = pointer(DIGIT_TABLE) if (output >> 32) != 0 q = output ÷ 100000000 - output2 = (output % UInt32) - UInt32(100000000) * (q % UInt32) + output2 = (output % UInt32) -% UInt32(100000000) *% (q % UInt32) output = q c = output2 % UInt32(10000) diff --git a/base/ryu/utils.jl b/base/ryu/utils.jl index f5a88c057e2b3..fd2909e3167bf 100644 --- a/base/ryu/utils.jl +++ b/base/ryu/utils.jl @@ -61,7 +61,23 @@ lengthforindex(idx) = div(((Int64(16 * idx) * 1292913986) >> 32) + 1 + 16 + 8, 9 Return `true` if `5^p` is a divisor of `x`. """ -pow5(x, p) = x % (UInt64(5)^p) == 0 +function pow5(x, p) + # Assumptions: + # 0 < x < (1 << 53) + # 0 <= p <= 308 + wrapped_pow = Base.power_by_squaring(*%, UInt64(5), p) + + # We are using wrapping arithmetic here, which can overflow, but we get the right answer anyway + # by the following argument (#47511): + # 1. False Negative + # No overflow occurs unless big(5)^p > typemax(UInt64), but typemax(UInt64) > (1 << 53), so + # z is never divisible by big(5)^p. QED. + # 2. False Positive + # We ask ∃ m2, p s.t. big(5)^p && m2 % wrapped_pow == 0. + # By exhaustive checking `all(p->UInt64(5)^p > 1<<53, 28:308)`, so there does not exist any such + # `m2` that satiesfies our assumptions above. QED. + x % wrapped_pow == 0 +end """ Ryu.pow2(x, p) @@ -166,6 +182,14 @@ Compute `pHi = (a*b)>>128` where `b = bLo + bHi<<64`. """ umul256_hi(a, bHi, bLo) = umul256(a, bHi, bLo)[2] +function mod1e9(v::UInt128) + # TODO: Fix LLVM to perform this optimization itself on 128 bit integers + # return (v % 10^9) % UInt32 + multiplied = umul256_hi(v, 0x89705F4136B4A597, 0x31680A88F8953031) + shifted = (multiplied >> 29) % UInt32 + return (v % UInt32) -% UInt32(1000000000) *% shifted +end + """ Ryu.mulshiftmod1e9(m, mula, mulb, mulc, j)::UInt32 @@ -178,9 +202,7 @@ function mulshiftmod1e9(m, mula, mulb, mulc, j) mid = b1 + ((b0 >> 64) % UInt64) s1 = b2 + ((mid >> 64) % UInt64) v = s1 >> (j - 128) - multiplied = umul256_hi(v, 0x89705F4136B4A597, 0x31680A88F8953031) - shifted = (multiplied >> 29) % UInt32 - return (v % UInt32) - UInt32(1000000000) * shifted + return mod1e9(v) end function append_sign(x, plus, space, buf, pos) diff --git a/base/special/log.jl b/base/special/log.jl index 5d7f1c8118724..6219e344418f9 100644 --- a/base/special/log.jl +++ b/base/special/log.jl @@ -566,9 +566,9 @@ function _log_ext(xu) # x = 2^k z; where z is in range [0x1.69555p-1,0x1.69555p-0) and exact. # The range is split into N subintervals. # The ith subinterval contains z and c is near the center of the interval. - tmp = reinterpret(Int64, xu - 0x3fe6955500000000) #0x1.69555p-1 + tmp = reinterpret(Int64, xu -% 0x3fe6955500000000) #0x1.69555p-1 i = (tmp >> 45) & 127 - z = reinterpret(Float64, xu - (tmp & 0xfff0000000000000)) + z = reinterpret(Float64, xu -% (tmp & 0xfff0000000000000)) k = Float64(tmp >> 52) # log(x) = k*Ln2 + log(c) + log1p(z/c-1). # getfield instead of getindex to satisfy effect analysis not knowing whether this is inbounds diff --git a/base/special/rem_pio2.jl b/base/special/rem_pio2.jl index de5c4151df2d0..c37f31f88d671 100644 --- a/base/special/rem_pio2.jl +++ b/base/special/rem_pio2.jl @@ -188,9 +188,9 @@ Base.@assume_effects :consistent function paynehanek(x::Float64) # # (i.e. ignoring integer and lowest bit parts of result) - w1 = UInt128(X*a1) << 64 # overflow becomes integer - w2 = widemul(X,a2) - w3 = widemul(X,a3) >> 64 + w1 = UInt128(X *% a1) << 64 # overflow becomes integer + w2 = widemul(X, a2) + w3 = widemul(X, a3) >> 64 w = w1 + w2 + w3 # quotient fraction after division by 2π # adjust for sign of x diff --git a/base/special/trig.jl b/base/special/trig.jl index 6463560caa3e5..23d240327dc64 100644 --- a/base/special/trig.jl +++ b/base/special/trig.jl @@ -634,7 +634,7 @@ function atan(y::T, x::T) where T<:Union{Float32, Float64} ypw = poshighword(y) xpw = poshighword(x) # compute y/x for Float32 - k = reinterpret(Int32, ypw-xpw)>>ATAN2_RATIO_BIT_SHIFT(T) + k = reinterpret(Int32, ypw -% xpw)>>ATAN2_RATIO_BIT_SHIFT(T) if k > ATAN2_RATIO_THRESHOLD(T) # |y/x| > threshold z=T(pi)/2+T(0.5)*ATAN2_PI_LO(T) diff --git a/base/strings/substring.jl b/base/strings/substring.jl index 792925f24b12b..6f062686aa559 100644 --- a/base/strings/substring.jl +++ b/base/strings/substring.jl @@ -127,8 +127,8 @@ pointer(x::SubString{String}) = pointer(x.string) + x.offset pointer(x::SubString{String}, i::Integer) = pointer(x.string) + x.offset + (i-1) function hash(s::SubString{String}, h::UInt) - h += memhash_seed - ccall(memhash, UInt, (Ptr{UInt8}, Csize_t, UInt32), s, sizeof(s), h % UInt32) + h + h +%= memhash_seed + ccall(memhash, UInt, (Ptr{UInt8}, Csize_t, UInt32), s, sizeof(s), h % UInt32) +% h end """ diff --git a/base/tuple.jl b/base/tuple.jl index 59fe2c1e531e1..a1cd53c6d6c85 100644 --- a/base/tuple.jl +++ b/base/tuple.jl @@ -514,10 +514,10 @@ function _eq(t1::Any32, t2::Any32) end const tuplehash_seed = UInt === UInt64 ? 0x77cfa1eef01bca90 : 0xf01bca90 -hash(::Tuple{}, h::UInt) = h + tuplehash_seed +hash(::Tuple{}, h::UInt) = h +% tuplehash_seed hash(t::Tuple, h::UInt) = hash(t[1], hash(tail(t), h)) function hash(t::Any32, h::UInt) - out = h + tuplehash_seed + out = h +% tuplehash_seed for i = length(t):-1:1 out = hash(t[i], out) end diff --git a/src/julia-parser.scm b/src/julia-parser.scm index 210ba8f0ae07b..1f684474ffe6e 100644 --- a/src/julia-parser.scm +++ b/src/julia-parser.scm @@ -4,7 +4,7 @@ (define (add-dots ops) (append! ops (map (lambda (op) (symbol (string "." op))) ops))) (define prec-assignment - (append! (add-dots '(= += -= −= *= /= //= |\\=| ^= ÷= %= <<= >>= >>>= |\|=| &= ⊻= ≔ ⩴ ≕)) + (append! (add-dots '(= += +%= -= −%= *= *%= /= //= |\\=| ^= ÷= %= <<= >>= >>>= |\|=| &= ⊻= ≔ ⩴ ≕)) (add-dots '(~)) '(:= $=))) ;; comma - higher than assignment outside parentheses, lower when inside @@ -20,8 +20,8 @@ (define prec-pipe> '(|.\|>| |\|>|)) (define prec-colon (append! '(: |..|) (add-dots '(… ⁝ ⋮ ⋱ ⋰ ⋯)))) (define prec-plus (append! '($) - (add-dots '(+ - − ¦ |\|| ⊕ ⊖ ⊞ ⊟ |++| ∪ ∨ ⊔ ± ∓ ∔ ∸ ≏ ⊎ ⊻ ⊽ ⋎ ⋓ ⟇ ⧺ ⧻ ⨈ ⨢ ⨣ ⨤ ⨥ ⨦ ⨧ ⨨ ⨩ ⨪ ⨫ ⨬ ⨭ ⨮ ⨹ ⨺ ⩁ ⩂ ⩅ ⩊ ⩌ ⩏ ⩐ ⩒ ⩔ ⩖ ⩗ ⩛ ⩝ ⩡ ⩢ ⩣)))) -(define prec-times (add-dots '(* / ⌿ ÷ % & · · ⋅ ∘ × |\\| ∩ ∧ ⊗ ⊘ ⊙ ⊚ ⊛ ⊠ ⊡ ⊓ ∗ ∙ ∤ ⅋ ≀ ⊼ ⋄ ⋆ ⋇ ⋉ ⋊ ⋋ ⋌ ⋏ ⋒ ⟑ ⦸ ⦼ ⦾ ⦿ ⧶ ⧷ ⨇ ⨰ ⨱ ⨲ ⨳ ⨴ ⨵ ⨶ ⨷ ⨸ ⨻ ⨼ ⨽ ⩀ ⩃ ⩄ ⩋ ⩍ ⩎ ⩑ ⩓ ⩕ ⩘ ⩚ ⩜ ⩞ ⩟ ⩠ ⫛ ⊍ ▷ ⨝ ⟕ ⟖ ⟗ ⨟))) + (add-dots '(+ +% - -% − ¦ |\|| ⊕ ⊖ ⊞ ⊟ |++| ∪ ∨ ⊔ ± ∓ ∔ ∸ ≏ ⊎ ⊻ ⊽ ⋎ ⋓ ⟇ ⧺ ⧻ ⨈ ⨢ ⨣ ⨤ ⨥ ⨦ ⨧ ⨨ ⨩ ⨪ ⨫ ⨬ ⨭ ⨮ ⨹ ⨺ ⩁ ⩂ ⩅ ⩊ ⩌ ⩏ ⩐ ⩒ ⩔ ⩖ ⩗ ⩛ ⩝ ⩡ ⩢ ⩣)))) +(define prec-times (add-dots '(* *% / ⌿ ÷ % & · · ⋅ ∘ × |\\| ∩ ∧ ⊗ ⊘ ⊙ ⊚ ⊛ ⊠ ⊡ ⊓ ∗ ∙ ∤ ⅋ ≀ ⊼ ⋄ ⋆ ⋇ ⋉ ⋊ ⋋ ⋌ ⋏ ⋒ ⟑ ⦸ ⦼ ⦾ ⦿ ⧶ ⧷ ⨇ ⨰ ⨱ ⨲ ⨳ ⨴ ⨵ ⨶ ⨷ ⨸ ⨻ ⨼ ⨽ ⩀ ⩃ ⩄ ⩋ ⩍ ⩎ ⩑ ⩓ ⩕ ⩘ ⩚ ⩜ ⩞ ⩟ ⩠ ⫛ ⊍ ▷ ⨝ ⟕ ⟖ ⟗ ⨟))) (define prec-rational (add-dots '(//))) (define prec-bitshift (add-dots '(<< >> >>>))) ;; `where` @@ -927,8 +927,8 @@ (else (loop (list 'call t ex (down s)))))))))) -(define (parse-expr s) (parse-with-chains s parse-term is-prec-plus? '(+ ++))) -(define (parse-term s) (parse-with-chains s parse-rational is-prec-times? '(*))) +(define (parse-expr s) (parse-with-chains s parse-term is-prec-plus? '(+ +% ++))) +(define (parse-term s) (parse-with-chains s parse-rational is-prec-times? '(* *%))) (define (parse-rational s) (parse-LtoR s parse-shift is-prec-rational?)) (define (parse-shift s) (parse-LtoR s parse-unary-subtype is-prec-bitshift?)) diff --git a/src/julia-syntax.scm b/src/julia-syntax.scm index 59f0662e61018..feaf585cceec9 100644 --- a/src/julia-syntax.scm +++ b/src/julia-syntax.scm @@ -2749,8 +2749,11 @@ '& (lambda (e) (error (string "invalid syntax " (deparse e)))) '+= lower-update-op + '+%= lower-update-op '-= lower-update-op + '-%= lower-update-op '*= lower-update-op + '*%= lower-update-op '.*= lower-update-op '/= lower-update-op './= lower-update-op diff --git a/stdlib/FileWatching/test/pidfile.jl b/stdlib/FileWatching/test/pidfile.jl index c2cb0c88a1b1e..31662e7f72b20 100644 --- a/stdlib/FileWatching/test/pidfile.jl +++ b/stdlib/FileWatching/test/pidfile.jl @@ -43,7 +43,7 @@ try if !iswindows() @test isvalidpid("", 1 % Cuint) @test !isvalidpid("", -1 % Cuint) - @test !isvalidpid("", -mypid) + @test !isvalidpid("", -getpid() % Cuint) end end diff --git a/stdlib/Printf/src/Printf.jl b/stdlib/Printf/src/Printf.jl index cb336a8d9c18b..ba72cf2e8330f 100644 --- a/stdlib/Printf/src/Printf.jl +++ b/stdlib/Printf/src/Printf.jl @@ -184,8 +184,8 @@ function Format(f::AbstractString) b = bytes[pos] pos += 1 else - while b - UInt8('0') < 0x0a - width = 10 * width + (b - UInt8('0')) + while UInt8('0') <= b <= UInt8('9') + width = 10 * width + (b - UInt8('0')) b = bytes[pos] pos += 1 pos > len && break @@ -208,7 +208,7 @@ function Format(f::AbstractString) pos += 1 else precision = 0 - while b - UInt8('0') < 0x0a + while UInt8('0') <= b <= UInt8('9') precision = 10precision + (b - UInt8('0')) b = bytes[pos] pos += 1 diff --git a/stdlib/Random/src/Xoshiro.jl b/stdlib/Random/src/Xoshiro.jl index 3be276ad23754..b5820ae59e3af 100644 --- a/stdlib/Random/src/Xoshiro.jl +++ b/stdlib/Random/src/Xoshiro.jl @@ -116,7 +116,7 @@ rng_native_52(::TaskLocalRNG) = UInt64 function setstate!( x::TaskLocalRNG, s0::UInt64, s1::UInt64, s2::UInt64, s3::UInt64, # xoshiro256 state - s4::UInt64 = 1s0 + 3s1 + 5s2 + 7s3, # internal splitmix state + s4::UInt64 = s0 +% (3 *% s1) +% (5 *% s2) +% (7 *% s3), # internal splitmix state ) t = current_task() t.rngState0 = s0 @@ -130,8 +130,8 @@ end @inline function rand(::TaskLocalRNG, ::SamplerType{UInt64}) task = current_task() s0, s1, s2, s3 = task.rngState0, task.rngState1, task.rngState2, task.rngState3 - tmp = s0 + s3 - res = ((tmp << 23) | (tmp >> 41)) + s0 + tmp = s0 +% s3 + res = ((tmp << 23) | (tmp >> 41)) +% s0 t = s1 << 17 s2 ⊻= s0 s3 ⊻= s1 diff --git a/stdlib/Random/src/XoshiroSimd.jl b/stdlib/Random/src/XoshiroSimd.jl index 1a16baa4bce28..9980c8dfef698 100644 --- a/stdlib/Random/src/XoshiroSimd.jl +++ b/stdlib/Random/src/XoshiroSimd.jl @@ -17,7 +17,7 @@ simdThreshold(::Type{Bool}) = 640 @inline _rotl45(x::UInt64) = (x<<45)|(x>>19) @inline _shl17(x::UInt64) = x<<17 @inline _rotl23(x::UInt64) = (x<<23)|(x>>41) -@inline _plus(x::UInt64,y::UInt64) = x+y +@inline _plus(x::UInt64,y::UInt64) = x +% y @inline _xor(x::UInt64,y::UInt64) = xor(x,y) @inline _and(x::UInt64, y::UInt64) = x & y @inline _or(x::UInt64, y::UInt64) = x | y @@ -128,10 +128,10 @@ function forkRand(rng::Union{TaskLocalRNG, Xoshiro}, ::Val{N}) where N # 0x5a94851fb48a6e05 == hash(UInt(2))|0x01 # 0x3688cf5d48899fa7 == hash(UInt(3))|0x01 # 0x867b4bb4c42e5661 == hash(UInt(4))|0x01 - s0 = ntuple(i->VecElement(0x02011ce34bce797f * rand(rng, UInt64)), Val(N)) - s1 = ntuple(i->VecElement(0x5a94851fb48a6e05 * rand(rng, UInt64)), Val(N)) - s2 = ntuple(i->VecElement(0x3688cf5d48899fa7 * rand(rng, UInt64)), Val(N)) - s3 = ntuple(i->VecElement(0x867b4bb4c42e5661 * rand(rng, UInt64)), Val(N)) + s0 = ntuple(i->VecElement(0x02011ce34bce797f *% rand(rng, UInt64)), Val(N)) + s1 = ntuple(i->VecElement(0x5a94851fb48a6e05 *% rand(rng, UInt64)), Val(N)) + s2 = ntuple(i->VecElement(0x3688cf5d48899fa7 *% rand(rng, UInt64)), Val(N)) + s3 = ntuple(i->VecElement(0x867b4bb4c42e5661 *% rand(rng, UInt64)), Val(N)) (s0, s1, s2, s3) end diff --git a/stdlib/Random/src/generation.jl b/stdlib/Random/src/generation.jl index cc9840f678413..7c4ce40616c28 100644 --- a/stdlib/Random/src/generation.jl +++ b/stdlib/Random/src/generation.jl @@ -345,7 +345,7 @@ function rand(rng::AbstractRNG, sp::SamplerRangeNDL{U,T}) where {U,T} m = x * s l = m % U if l < s - t = mod(-s, s) # as s is unsigned, -s is equal to 2^L - s in the paper + t = mod(-%(s), s) # as s is unsigned, -s is equal to 2^L - s in the paper while l < t x = widen(rand(rng, U)) m = x * s diff --git a/stdlib/SHA.version b/stdlib/SHA.version index f2242a336c6fe..56b5e12f11cbd 100644 --- a/stdlib/SHA.version +++ b/stdlib/SHA.version @@ -1,4 +1,4 @@ SHA_BRANCH = master -SHA_SHA1 = 2d1f84e6f8417a1a368de48318640d948b023e7a +SHA_SHA1 = d44b32222edc233f3f4f2bf30ef43275841356b5 SHA_GIT_URL := https://github.com/JuliaCrypto/SHA.jl.git SHA_TAR_URL = https://api.github.com/repos/JuliaCrypto/SHA.jl/tarball/$1 diff --git a/test/arrayops.jl b/test/arrayops.jl index 770cec3705038..d80c88ce3f59d 100644 --- a/test/arrayops.jl +++ b/test/arrayops.jl @@ -2533,7 +2533,7 @@ end @test cumsum(Any[1, 2.3]) == [1, 3.3] == cumsum(Real[1, 2.3])::Vector{Real} @test cumsum([true,true,true]) == [1,2,3] @test cumsum(0x00:0xff)[end] === UInt(255*(255+1)÷2) # no overflow - @test accumulate(+, 0x00:0xff)[end] === 0x80 # overflow + @test accumulate(+%, 0x00:0xff)[end] === 0x80 # overflow @test_throws InexactError cumsum!(similar(0x00:0xff), 0x00:0xff) # overflow @test cumsum([[true], [true], [false]])::Vector{Vector{Int}} == [[1], [2], [2]] diff --git a/test/intfuncs.jl b/test/intfuncs.jl index ceaac235a3da9..1f31ed240c7a0 100644 --- a/test/intfuncs.jl +++ b/test/intfuncs.jl @@ -53,14 +53,15 @@ is_effect_free(args...) = Core.Compiler.is_effect_free(Base.infer_effects(args.. @test_throws OverflowError gcd(typemin(T), T(0)) @test_throws OverflowError gcd(T(0), typemin(T)) else + negtmax = one(T) # For Unsigned Integer types, -typemax(T) == 1. - @test gcd(-typemax(T), T(1)) === T(1) - @test gcd(T(1), -typemax(T)) === T(1) - @test gcd(-typemax(T), T(0)) === T(1) - @test gcd(T(0), -typemax(T)) === T(1) - @test gcd(-typemax(T), -typemax(T)) === T(1) - @test gcd(-typemax(T), typemax(T)) === T(1) - @test gcd(typemax(T), -typemax(T)) === T(1) + @test gcd(negtmax, T(1)) === T(1) + @test gcd(T(1), negtmax) === T(1) + @test gcd(negtmax, T(0)) === T(1) + @test gcd(T(0), negtmax) === T(1) + @test gcd(negtmax, negtmax) === T(1) + @test gcd(negtmax, typemax(T)) === T(1) + @test gcd(typemax(T), negtmax) === T(1) # For Unsigned Integer types, typemin(T) == 0. @test gcd(typemin(T), T(1)) === T(1) @@ -126,14 +127,15 @@ is_effect_free(args...) = Core.Compiler.is_effect_free(Base.infer_effects(args.. @test_throws OverflowError lcm(typemin(T), typemin(T)+T(1)) # lcm(n, n+1) = n*(n+1). @test_throws OverflowError lcm(typemin(T), typemin(T)) else + negtmax = one(T) # For Unsigned Integer types, -typemax(T) == 1. - @test lcm(-typemax(T), T(1)) === T(1) - @test lcm(T(1), -typemax(T)) === T(1) - @test lcm(-typemax(T), T(0)) === T(0) - @test lcm(T(0), -typemax(T)) === T(0) - @test lcm(-typemax(T), -typemax(T)) === T(1) - @test lcm(-typemax(T), typemax(T)) === typemax(T) - @test lcm(typemax(T), -typemax(T)) === typemax(T) + @test lcm(negtmax, T(1)) === T(1) + @test lcm(T(1), negtmax) === T(1) + @test lcm(negtmax, T(0)) === T(0) + @test lcm(T(0), negtmax) === T(0) + @test lcm(negtmax, negtmax) === T(1) + @test lcm(negtmax, typemax(T)) === typemax(T) + @test lcm(typemax(T), negtmax) === typemax(T) # For Unsigned Integer types, typemin(T) == 0. @test lcm(typemin(T), T(1)) === lcm(T(0), T(1)) === T(0) diff --git a/test/ryu.jl b/test/ryu.jl index 0b10bd7e49ba5..031c98a670df1 100644 --- a/test/ryu.jl +++ b/test/ryu.jl @@ -52,7 +52,7 @@ end @test "2.305843009213694e40" == Ryu.writeshortest(Core.bitcast(Float64, 0x4850F0CF064DD592)) end -@testset "pow5 overflow (#47464)" begin +@testset "pow5 overflow (#46764/#47511)" begin @test "4.6458339e+63" == Ryu.writeexp(4.645833859177319e63, 7) @test "4.190673780e+40" == Ryu.writeexp(4.190673779576499e40, 9) end diff --git a/test/testhelpers/Furlongs.jl b/test/testhelpers/Furlongs.jl index f63b5460c7c16..6f8ff255f64e4 100644 --- a/test/testhelpers/Furlongs.jl +++ b/test/testhelpers/Furlongs.jl @@ -66,8 +66,8 @@ for f in (:real,:imag,:complex,:+,:-) @eval Base.$f(x::Furlong{p}) where {p} = Furlong{p}($f(x.val)) end -import Base: +, -, ==, !=, <, <=, isless, isequal, *, /, //, div, rem, mod, ^ -for op in (:+, :-) +import Base: +, -, +%, -%, ==, !=, <, <=, isless, isequal, *, /, //, div, rem, mod, ^ +for op in (:+, :-, :(+%), :(-%)) @eval function $op(x::Furlong{p}, y::Furlong{p}) where {p} v = $op(x.val, y.val) Furlong{p}(v)