-
-
Notifications
You must be signed in to change notification settings - Fork 5.7k
Labels
bugIndicates an unexpected problem or unintended behaviorIndicates an unexpected problem or unintended behaviorcorrectness bug ⚠Bugs that are likely to lead to incorrect results in user code without throwingBugs that are likely to lead to incorrect results in user code without throwingtypes and dispatchTypes, subtyping and method dispatchTypes, subtyping and method dispatch
Milestone
Description
The bug:
- does not appear on v1.9
- manifests on v1.10 as a crash, like so:
ERROR: LoadError: fatal error in type inference (type bound) Stacktrace: [1] from_base_rational_checked @ ~/reproducer/reproducer.jl:406 [inlined] [2] Main.LazyMinus.TypeDomainRational(x::Rational{Int64}) @ Main.BaseOverloads ~/reproducer/reproducer.jl:409 [3] top-level scope @ ~/reproducer/reproducer.jl:413
- manifests on v1.11 and up as a silently wrong result
For all Julia versions the correct behavior is obtained with --compile=min
.
Reproducer:
module NonnegativeIntegers
export
NonnegativeInteger, type_assert_nonnegative_integer,
new_nonnegative_integer, NaturalNumberTuple
const NaturalNumberTuple = Tuple{Vararg{Nothing,N}} where {N}
struct NonnegativeInteger{
N,
NaturalNumberTuple{N} <: Tup <: NaturalNumberTuple,
} <: Integer
global function new_nonnegative_integer(::Type{NonnegativeInteger{N}}) where {N}
Tup = NaturalNumberTuple{N}
new{N,Tup}()
end
end
function new_nonnegative_integer(n::Int)
new_nonnegative_integer(NonnegativeInteger{n})
end
function type_assert_nonnegative_integer(@nospecialize x::NonnegativeInteger)
x
end
end
module PositiveIntegers
using ..NonnegativeIntegers
export
PositiveInteger, type_assert_positive_integer,
NonnegativeInteger, new_nonnegative_integer,
natural_successor, natural_predecessor,
PositiveIntegerUpperBound, NonnegativeIntegerUpperBound
const PositiveInteger = NonnegativeInteger{N,Tup} where {
N,
NaturalNumberTuple{N} <: Tup <: Tuple{Nothing,Vararg{Nothing}},
}
function type_assert_positive_integer(@nospecialize x::PositiveInteger)
x
end
function natural_successor(::NonnegativeInteger{N}) where {N}
new_nonnegative_integer(N + 1)
end
function natural_predecessor(::PositiveInteger{N}) where {N}
new_nonnegative_integer(N - 1)
end
const PositiveIntegerUpperBound = PositiveInteger
const NonnegativeIntegerUpperBound = NonnegativeInteger
end
module IntegersGreaterThanOne
using ..NonnegativeIntegers, ..PositiveIntegers
export
IntegerGreaterThanOne, IntegerGreaterThanOneUpperBound,
type_assert_integer_greater_than_one
const IntegerGreaterThanOne = NonnegativeInteger{N,Tup} where {
N,
NaturalNumberTuple{N} <: Tup <: Tuple{Nothing,Nothing,Vararg{Nothing}},
}
const IntegerGreaterThanOneUpperBound = IntegerGreaterThanOne
function type_assert_integer_greater_than_one(@nospecialize x::IntegerGreaterThanOne)
x
end
end
module NonintegralRationalsGreaterThanOne
module RecursiveStep
using ....IntegersGreaterThanOne
export recursive_step
function recursive_step(@nospecialize t::Type)
Union{IntegerGreaterThanOne,t}
end
end
const NonintegralRationalGreaterThanOneUpperBound = Real
const NonintegralRationalGreaterThanOneUpperBoundTighter = Real
using .RecursiveStep, ..PositiveIntegers, ..IntegersGreaterThanOne
export
NonintegralRationalGreaterThanOne, NonintegralRationalGreaterThanOneUpperBound,
new_nonintegral_rational_greater_than_one,
RationalGreaterThanOne, RationalGreaterThanOneUpperBound,
type_assert_rational_greater_than_one
struct NonintegralRationalGreaterThanOne{ # regular continued fraction with positive integer part XXX
IntegerPart<:PositiveInteger,
FractionalPartDenominator<:recursive_step(NonintegralRationalGreaterThanOneUpperBoundTighter),
} <: NonintegralRationalGreaterThanOneUpperBoundTighter
integer_part::IntegerPart
fractional_part_denominator::FractionalPartDenominator
global function new_nonintegral_rational_greater_than_one(i::I, f::F) where {
I<:PositiveInteger,
F<:recursive_step(NonintegralRationalGreaterThanOne),
}
r = new{I,F}(i, f)
type_assert_nonintegral_rational_greater_than_one(r)
end
end
function type_assert_nonintegral_rational_greater_than_one(@nospecialize x::NonintegralRationalGreaterThanOne)
x
end
const RationalGreaterThanOne = Union{IntegerGreaterThanOne,NonintegralRationalGreaterThanOne}
const RationalGreaterThanOneUpperBound = Union{IntegerGreaterThanOneUpperBound,NonintegralRationalGreaterThanOneUpperBound}
function type_assert_rational_greater_than_one(@nospecialize x::RationalGreaterThanOne)
x
end
end
module NonintegralRationalsBetweenZeroAndOne
using ..IntegersGreaterThanOne, ..NonintegralRationalsGreaterThanOne
export
NonintegralRationalBetweenZeroAndOne, NonintegralRationalBetweenZeroAndOneUpperBound,
new_nonintegral_rational_between_zero_and_one
struct NonintegralRationalBetweenZeroAndOne{ # regular continued fraction with zero integer part XXX
Denominator<:RationalGreaterThanOne,
} <: Real
denominator::Denominator
global function new_nonintegral_rational_between_zero_and_one(d::D) where {
D<:RationalGreaterThanOne,
}
new{D}(d)
end
end
const NonintegralRationalBetweenZeroAndOneUpperBound = NonintegralRationalBetweenZeroAndOne{<:Any}
end
module PositiveNonintegralRationals
using ..PositiveIntegers, ..NonintegralRationalsGreaterThanOne, ..NonintegralRationalsBetweenZeroAndOne
export
PositiveNonintegralRational, PositiveNonintegralRationalUpperBound, NonnegativeRational,
PositiveRational, PositiveRationalUpperBound
const PositiveNonintegralRational = Union{NonintegralRationalBetweenZeroAndOne,NonintegralRationalGreaterThanOne}
const PositiveNonintegralRationalUpperBound = Union{NonintegralRationalBetweenZeroAndOneUpperBound,NonintegralRationalGreaterThanOneUpperBound}
const NonnegativeRational = Union{NonnegativeInteger,PositiveNonintegralRational}
const PositiveRational = Union{PositiveInteger,PositiveNonintegralRational}
const PositiveRationalUpperBound = Union{PositiveIntegerUpperBound,PositiveNonintegralRationalUpperBound}
end
module FractionalPartDenominator
using
..NonintegralRationalsGreaterThanOne,
..NonintegralRationalsBetweenZeroAndOne, ..PositiveNonintegralRationals
export fractional_part_denominator
function fractional_part_denominator_gt1(x::NonintegralRationalGreaterThanOne)
type_assert_rational_greater_than_one(getfield(x, :fractional_part_denominator))
end
function fractional_part_denominator(x::PositiveNonintegralRational)
if x isa NonintegralRationalBetweenZeroAndOne
getfield(x, :denominator)
else
fractional_part_denominator_gt1(x)
end
end
end
module LazyMinus
using ..PositiveIntegers, ..PositiveNonintegralRationals
export
NegativeInteger, NegativeIntegerUpperBound,
TypeDomainInteger, TypeDomainIntegerUpperBound,
NonzeroInteger, NonzeroIntegerUpperBound,
NegativeNonintegralRational, NegativeNonintegralRationalUpperBound,
NegativeRational, NegativeRationalUpperBound,
NonintegralRational, NonintegralRationalUpperBound,
TypeDomainRational, TypeDomainRationalUpperBound,
NonzeroRational, NonzeroRationalUpperBound,
negated, absolute_value_of
struct NegativeInteger{X<:PositiveInteger} <: Integer
x::X
global function new_negative_integer(x::X) where {X<:PositiveInteger}
new{X}(x)
end
end
const NegativeIntegerUpperBound = NegativeInteger{<:Any}
struct NegativeNonintegralRational{X<:PositiveNonintegralRational} <: Real
x::X
global function new_negative_nonintegral_rational(x::X) where {X<:PositiveNonintegralRational}
new{X}(x)
end
end
const NegativeNonintegralRationalUpperBound = NegativeNonintegralRational{<:Any}
const NegativeRational = Union{NegativeInteger,NegativeNonintegralRational}
const NegativeRationalUpperBound = Union{NegativeIntegerUpperBound,NegativeNonintegralRationalUpperBound}
const NonintegralRational = Union{NegativeNonintegralRational,PositiveNonintegralRational}
const NonintegralRationalUpperBound = Union{NegativeNonintegralRationalUpperBound,PositiveNonintegralRationalUpperBound}
const TypeDomainInteger = Union{NonnegativeInteger,NegativeInteger}
const TypeDomainIntegerUpperBound = Union{NonnegativeIntegerUpperBound,NegativeIntegerUpperBound}
const TypeDomainRational = Union{TypeDomainInteger,NonintegralRational}
const TypeDomainRationalUpperBound = Union{TypeDomainIntegerUpperBound,NonintegralRationalUpperBound}
const NonzeroInteger = Union{NegativeInteger,PositiveInteger}
const NonzeroIntegerUpperBound = Union{NegativeIntegerUpperBound,PositiveIntegerUpperBound}
const NonzeroRational = Union{NonzeroInteger,NonintegralRational}
const NonzeroRationalUpperBound = Union{NonzeroIntegerUpperBound,NonintegralRationalUpperBound}
function new_negative(x::PositiveRational)
if x isa Integer
new_negative_integer(x)
else
new_negative_nonintegral_rational(x)
end
end
function negated_nonnegative(n::NonnegativeRational)
if n isa PositiveRationalUpperBound
new_negative(n)
else
new_nonnegative_integer(0)
end
end
function negated(n::TypeDomainRational)
if n isa NegativeRational
getfield(n, :x)
else
negated_nonnegative(n)
end
end
function absolute_value_of(n::TypeDomainRational)
if n isa NegativeRational
getfield(n, :x)
else
n
end
end
end
module MultiplicativeInverse
using
..PositiveIntegers, ..NonintegralRationalsGreaterThanOne,
..NonintegralRationalsBetweenZeroAndOne, ..PositiveNonintegralRationals,
..LazyMinus, ..FractionalPartDenominator
export multiplicative_inverse
function multiplicative_inverse_positive_integer(x::PositiveInteger)
p = natural_predecessor(x)
if p isa PositiveIntegerUpperBound
new_nonintegral_rational_between_zero_and_one(x)
else
new_nonnegative_integer(1)
end
end
function multiplicative_inverse_positive_noninteger(x::PositiveNonintegralRational)
if x isa NonintegralRationalBetweenZeroAndOne
fractional_part_denominator(x)
else
new_nonintegral_rational_between_zero_and_one(x)
end
end
function multiplicative_inverse_positive(x::PositiveRational)
if x isa Integer
multiplicative_inverse_positive_integer(x)
else
multiplicative_inverse_positive_noninteger(x)
end
end
function multiplicative_inverse(x::NonzeroRational)
a = absolute_value_of(x)
i = multiplicative_inverse_positive(a)
if x isa NegativeRational
negated(i)
else
i
end
end
end
module RecursiveAlgorithms
using ..PositiveIntegers, ..IntegersGreaterThanOne
export
added, from_abs_int, is_even_nonnegative, multiplied, is_less_nonnegative,
subtracted_nonnegative,
euclidean_division_quotient, euclidean_division_remainder
function is_less_nonnegative(::NonnegativeInteger{L}, ::NonnegativeInteger{R}) where {L,R}
L < R
end
function from_abs_int(n::Int)
new_nonnegative_integer(abs(n))
end
function subtracted_nonnegative(::NonnegativeInteger{L}, ::NonnegativeInteger{R}) where {L, R}
new_nonnegative_integer(L - R)
end
Base.@assume_effects :foldable function euclidean_division_quotient(num::NonnegativeInteger, den::PositiveInteger)
if is_less_nonnegative(num, den)
new_nonnegative_integer(0)
else
let n = subtracted_nonnegative(num, den), quo = euclidean_division_quotient(n, den)
natural_successor(quo)
end
end
end
function euclidean_division_remainder(::NonnegativeInteger{L}, ::PositiveInteger{R}) where {L, R}
new_nonnegative_integer(L % R)
end
end
module DivisionForIntegers
using
..PositiveIntegers, ..IntegersGreaterThanOne, ..NonintegralRationalsGreaterThanOne,
..LazyMinus, ..MultiplicativeInverse, ..RecursiveAlgorithms
export division_for_integers
Base.@assume_effects :foldable function division_gt1(num::PositiveInteger, den::PositiveInteger)
quo = euclidean_division_quotient(num, den)
quo = type_assert_positive_integer(quo)
rem = euclidean_division_remainder(num, den)
if rem isa PositiveIntegerUpperBound
let fdp = division_gt1(den, rem)
new_nonintegral_rational_greater_than_one(quo, fdp)
end
else
type_assert_integer_greater_than_one(quo)
end
end
function division_between_0_and_1(num::PositiveInteger, den::PositiveInteger)
i = division_gt1(den, num)
multiplicative_inverse(i)
end
function division_nonnegative(num::NonnegativeInteger, den::PositiveInteger)
z = new_nonnegative_integer(0)
if num isa PositiveIntegerUpperBound
if num === den
natural_successor(z)
else
if is_less_nonnegative(num, den)
division_between_0_and_1(num, den)
else
division_gt1(num, den)
end
end
else
z
end
end
function division_for_integers(num::TypeDomainInteger, den::NonzeroInteger)
anum = absolute_value_of(num)
aden = absolute_value_of(den)
cf = division_nonnegative(anum, aden)
if (num isa NegativeInteger) === (den isa NegativeInteger)
cf
else
negated(cf)
end
end
end
module Conversion
using ..PositiveIntegers, ..LazyMinus, ..RecursiveAlgorithms
export
tdi_to_int, tdi_from_int,
tdi_to_x, tdi_from_x,
tdi_to_x_with_extra
function tdi_from_int(i::Int)
n = from_abs_int(i)
if signbit(i)
negated(n)
else
n
end
end
function tdi_from_bool(i::Bool)
z = new_nonnegative_integer(0)
if i
natural_successor(z)
else
z
end
end
function tdi_to_x(x::Type, n::TypeDomainInteger)
i = tdi_to_int(n)
x(i)
end
function tdi_to_x_with_extra(x::Type, n::TypeDomainInteger, e)
i = tdi_to_int(n)
x(i, e)
end
function x_to_int(x)
t = Int # presumably wide enough for any type domain integer
t(x)::t
end
function tdi_from_x(x)
if x isa Bool
tdi_from_bool(x)
else
let i = x_to_int(x)
tdi_from_int(i)
end
end
end
end
module BaseOverloads
using
..PositiveIntegers, ..RecursiveAlgorithms, ..LazyMinus, ..MultiplicativeInverse,
..NonintegralRationalsGreaterThanOne,
..NonintegralRationalsBetweenZeroAndOne, ..DivisionForIntegers, ..Conversion
function identity_conversion(::Type{T}, x::TypeDomainRational) where {T<:TypeDomainRational}
if x isa T
x
else
throw(InexactError(:convert, T, x))
end
end
function to_tdi(x::Number)
if !isinteger(x)
throw(InexactError(:convert, TypeDomainInteger, x))
end
tdi_from_x(x)
end
function from_base_rational(x::Rational)
num = numerator(x)
den = denominator(x)
num_tdi = to_tdi(num)
den_tdi = to_tdi(den)
division_for_integers(num_tdi, den_tdi)
end
function from_base_rational_checked(::Type{T}, x::Rational) where {T<:TypeDomainRational}
q = from_base_rational(x)
identity_conversion(T, q)
end
function (::Type{T})(x::Rational) where {T<:TypeDomainRational}
from_base_rational_checked(T, x)
end
end
n = LazyMinus.TypeDomainRational(5//1)
display(n)
Metadata
Metadata
Assignees
Labels
bugIndicates an unexpected problem or unintended behaviorIndicates an unexpected problem or unintended behaviorcorrectness bug ⚠Bugs that are likely to lead to incorrect results in user code without throwingBugs that are likely to lead to incorrect results in user code without throwingtypes and dispatchTypes, subtyping and method dispatchTypes, subtyping and method dispatch