diff --git a/spec/std/big/big_int_spec.cr b/spec/std/big/big_int_spec.cr
index 07575892bc08..8a86939efbac 100644
--- a/spec/std/big/big_int_spec.cr
+++ b/spec/std/big/big_int_spec.cr
@@ -198,7 +198,7 @@ describe "BigInt" do
it "raises if factorial of 2^64" do
expect_raises ArgumentError do
- (LibGMP::ULong::MAX.to_big_i + 1).factorial
+ (LibGMP::UI::MAX.to_big_i + 1).factorial
end
end
diff --git a/spec/std/int_spec.cr b/spec/std/int_spec.cr
index 72a83bd64061..88ef12553c38 100644
--- a/spec/std/int_spec.cr
+++ b/spec/std/int_spec.cr
@@ -146,6 +146,102 @@ describe "Int" do
end
end
+ describe "#to_signed" do
+ {% for n in [8, 16, 32, 64, 128] %}
+ it "does for Int{{n}}" do
+ x = Int{{n}}.new(123).to_signed
+ x.should be_a(Int{{n}})
+ x.should eq(123)
+
+ Int{{n}}.new(-123).to_signed.should eq(-123)
+ Int{{n}}::MIN.to_signed.should eq(Int{{n}}::MIN)
+ Int{{n}}::MAX.to_signed.should eq(Int{{n}}::MAX)
+ end
+
+ it "does for UInt{{n}}" do
+ x = UInt{{n}}.new(123).to_signed
+ x.should be_a(Int{{n}})
+ x.should eq(123)
+
+ UInt{{n}}::MIN.to_signed.should eq(0)
+ expect_raises(OverflowError) { UInt{{n}}::MAX.to_signed }
+ expect_raises(OverflowError) { (UInt{{n}}.new(Int{{n}}::MAX) + 1).to_signed }
+ end
+ {% end %}
+ end
+
+ describe "#to_signed!" do
+ {% for n in [8, 16, 32, 64, 128] %}
+ it "does for Int{{n}}" do
+ x = Int{{n}}.new(123).to_signed!
+ x.should be_a(Int{{n}})
+ x.should eq(123)
+
+ Int{{n}}.new(-123).to_signed!.should eq(-123)
+ Int{{n}}::MIN.to_signed!.should eq(Int{{n}}::MIN)
+ Int{{n}}::MAX.to_signed!.should eq(Int{{n}}::MAX)
+ end
+
+ it "does for UInt{{n}}" do
+ x = UInt{{n}}.new(123).to_signed!
+ x.should be_a(Int{{n}})
+ x.should eq(123)
+
+ UInt{{n}}::MIN.to_signed!.should eq(0)
+ UInt{{n}}::MAX.to_signed!.should eq(-1)
+ (UInt{{n}}::MAX - 122).to_signed!.should eq(-123)
+ (UInt{{n}}.new(Int{{n}}::MAX) + 1).to_signed!.should eq(Int{{n}}::MIN)
+ end
+ {% end %}
+ end
+
+ describe "#to_unsigned" do
+ {% for n in [8, 16, 32, 64, 128] %}
+ it "does for Int{{n}}" do
+ x = Int{{n}}.new(123).to_unsigned
+ x.should be_a(UInt{{n}})
+ x.should eq(123)
+
+ Int{{n}}.zero.to_unsigned.should eq(UInt{{n}}::MIN)
+ Int{{n}}::MAX.to_unsigned.should eq(UInt{{n}}.new(Int{{n}}::MAX))
+ expect_raises(OverflowError) { Int{{n}}::MIN.to_unsigned }
+ end
+
+ it "does for UInt{{n}}" do
+ x = UInt{{n}}.new(123).to_unsigned
+ x.should be_a(UInt{{n}})
+ x.should eq(123)
+
+ UInt{{n}}::MIN.to_unsigned.should eq(UInt{{n}}::MIN)
+ UInt{{n}}::MAX.to_unsigned.should eq(UInt{{n}}::MAX)
+ end
+ {% end %}
+ end
+
+ describe "#to_unsigned!" do
+ {% for n in [8, 16, 32, 64, 128] %}
+ it "does for Int{{n}}" do
+ x = Int{{n}}.new(123).to_unsigned!
+ x.should be_a(UInt{{n}})
+ x.should eq(123)
+
+ Int{{n}}.new(-123).to_unsigned!.should eq(UInt{{n}}::MAX - 122)
+ Int{{n}}::MIN.to_unsigned!.should eq(UInt{{n}}::MAX // 2 + 1)
+ Int{{n}}::MAX.to_unsigned!.should eq(UInt{{n}}::MAX // 2)
+ Int{{n}}.new(-1).to_unsigned!.should eq(UInt{{n}}::MAX)
+ end
+
+ it "does for UInt{{n}}" do
+ x = UInt{{n}}.new(123).to_unsigned!
+ x.should be_a(UInt{{n}})
+ x.should eq(123)
+
+ UInt{{n}}::MIN.to_unsigned!.should eq(UInt{{n}}::MIN)
+ UInt{{n}}::MAX.to_unsigned!.should eq(UInt{{n}}::MAX)
+ end
+ {% end %}
+ end
+
describe "#abs_unsigned" do
{% for int in Int::Signed.union_types %}
it "does for {{ int }}" do
diff --git a/src/array.cr b/src/array.cr
index df31a2699251..5ce9f2e6148b 100644
--- a/src/array.cr
+++ b/src/array.cr
@@ -127,12 +127,21 @@ class Array(T)
#
# ```
# Array.new(3, 'a') # => ['a', 'a', 'a']
+ # ```
+ #
+ # WARNING: The initial value is filled into the array as-is. It gets neither
+ # duplicated nor cloned. For types with reference semantics this means every
+ # item will point to the *same* object.
#
+ # ```
# ary = Array.new(3, [1])
# ary # => [[1], [1], [1]]
# ary[0][0] = 2
# ary # => [[2], [2], [2]]
# ```
+ #
+ # * `.new(Int, & : Int32 -> T)` is an alternative that allows using a
+ # different initial value for each position.
def initialize(size : Int, value : T)
if size < 0
raise ArgumentError.new("Negative array size: #{size}")
diff --git a/src/big/big_float.cr b/src/big/big_float.cr
index 5464d948931b..7377afda34b3 100644
--- a/src/big/big_float.cr
+++ b/src/big/big_float.cr
@@ -40,29 +40,16 @@ struct BigFloat < Float
LibGMP.mpf_set(self, num)
end
- def initialize(num : Int8 | Int16 | Int32)
- LibGMP.mpf_init_set_si(out @mpf, num)
- end
-
- def initialize(num : UInt8 | UInt16 | UInt32)
- LibGMP.mpf_init_set_ui(out @mpf, num)
- end
-
- def initialize(num : Int64)
- if LibGMP::Long == Int64
- LibGMP.mpf_init_set_si(out @mpf, num)
- else
- LibGMP.mpf_init(out @mpf)
- LibGMP.mpf_set_z(self, num.to_big_i)
- end
- end
-
- def initialize(num : UInt64)
- if LibGMP::ULong == UInt64
- LibGMP.mpf_init_set_ui(out @mpf, num)
- else
- LibGMP.mpf_init(out @mpf)
- LibGMP.mpf_set_z(self, num.to_big_i)
+ def initialize(num : Int)
+ Int.primitive_si_ui_check(num) do |si, ui, big_i|
+ {
+ si: LibGMP.mpf_init_set_si(out @mpf, {{ si }}),
+ ui: LibGMP.mpf_init_set_ui(out @mpf, {{ ui }}),
+ big_i: begin
+ LibGMP.mpf_init(out @mpf)
+ LibGMP.mpf_set_z(self, {{ big_i }})
+ end,
+ }
end
end
@@ -113,16 +100,20 @@ struct BigFloat < Float
LibGMP.mpf_cmp_d(self, other) unless other.nan?
end
- def <=>(other : Number)
- if other.is_a?(Int8 | Int16 | Int32) || (LibGMP::Long == Int64 && other.is_a?(Int64))
- LibGMP.mpf_cmp_si(self, other)
- elsif other.is_a?(UInt8 | UInt16 | UInt32) || (LibGMP::ULong == UInt64 && other.is_a?(UInt64))
- LibGMP.mpf_cmp_ui(self, other)
- else
- LibGMP.mpf_cmp(self, other.to_big_f)
+ def <=>(other : Int)
+ Int.primitive_si_ui_check(other) do |si, ui, big_i|
+ {
+ si: LibGMP.mpf_cmp_si(self, {{ si }}),
+ ui: LibGMP.mpf_cmp_ui(self, {{ ui }}),
+ big_i: self <=> {{ big_i }},
+ }
end
end
+ def <=>(other : Number)
+ LibGMP.mpf_cmp(self, other.to_big_f)
+ end
+
def - : BigFloat
BigFloat.new { |mpf| LibGMP.mpf_neg(mpf, self) }
end
diff --git a/src/big/big_int.cr b/src/big/big_int.cr
index 9cc3f15e5890..decf7a0828ff 100644
--- a/src/big/big_int.cr
+++ b/src/big/big_int.cr
@@ -48,29 +48,33 @@ struct BigInt < Int
# Creates a `BigInt` from the given *num*.
def self.new(num : Int::Primitive)
- if LibGMP::SI::MIN <= num <= LibGMP::UI::MAX
- if num <= LibGMP::SI::MAX
- LibGMP.init_set_si(out mpz1, LibGMP::SI.new!(num))
- new(mpz1)
- else
- LibGMP.init_set_ui(out mpz2, LibGMP::UI.new!(num))
- new(mpz2)
- end
- else
- negative = num < 0
- num = num.abs_unsigned
- capacity = (num.bit_length - 1) // (sizeof(LibGMP::MpLimb) * 8) + 1
-
- # This assumes GMP wasn't built with its experimental nails support:
- # https://gmplib.org/manual/Low_002dlevel-Functions
- unsafe_build(capacity) do |limbs|
- appender = limbs.to_unsafe.appender
- limbs.size.times do
- appender << LibGMP::MpLimb.new!(num)
- num = num.unsafe_shr(sizeof(LibGMP::MpLimb) * 8)
- end
- {capacity, negative}
- end
+ Int.primitive_si_ui_check(num) do |si, ui, _|
+ {
+ si: begin
+ LibGMP.init_set_si(out mpz1, {{ si }})
+ new(mpz1)
+ end,
+ ui: begin
+ LibGMP.init_set_ui(out mpz2, {{ ui }})
+ new(mpz2)
+ end,
+ big_i: begin
+ negative = num < 0
+ num = num.abs_unsigned
+ capacity = (num.bit_length - 1) // (sizeof(LibGMP::MpLimb) * 8) + 1
+
+ # This assumes GMP wasn't built with its experimental nails support:
+ # https://gmplib.org/manual/Low_002dlevel-Functions
+ unsafe_build(capacity) do |limbs|
+ appender = limbs.to_unsafe.appender
+ limbs.size.times do
+ appender << LibGMP::MpLimb.new!(num)
+ num = num.unsafe_shr(sizeof(LibGMP::MpLimb) * 8)
+ end
+ {capacity, negative}
+ end
+ end,
+ }
end
end
@@ -141,19 +145,13 @@ struct BigInt < Int
LibGMP.cmp(mpz, other)
end
- def <=>(other : Int::Signed)
- if LibC::Long::MIN <= other <= LibC::Long::MAX
- LibGMP.cmp_si(mpz, other)
- else
- self <=> BigInt.new(other)
- end
- end
-
- def <=>(other : Int::Unsigned)
- if other <= LibC::ULong::MAX
- LibGMP.cmp_ui(mpz, other)
- else
- self <=> BigInt.new(other)
+ def <=>(other : Int)
+ Int.primitive_si_ui_check(other) do |si, ui, big_i|
+ {
+ si: LibGMP.cmp_si(self, {{ si }}),
+ ui: LibGMP.cmp_ui(self, {{ ui }}),
+ big_i: self <=> {{ big_i }},
+ }
end
end
@@ -166,12 +164,12 @@ struct BigInt < Int
end
def +(other : Int) : BigInt
- if other < 0
- self - other.abs
- elsif other <= LibGMP::ULong::MAX
- BigInt.new { |mpz| LibGMP.add_ui(mpz, self, other) }
- else
- self + other.to_big_i
+ Int.primitive_ui_check(other) do |ui, neg_ui, big_i|
+ {
+ ui: BigInt.new { |mpz| LibGMP.add_ui(mpz, self, {{ ui }}) },
+ neg_ui: BigInt.new { |mpz| LibGMP.sub_ui(mpz, self, {{ neg_ui }}) },
+ big_i: self + {{ big_i }},
+ }
end
end
@@ -184,12 +182,12 @@ struct BigInt < Int
end
def -(other : Int) : BigInt
- if other < 0
- self + other.abs
- elsif other <= LibGMP::ULong::MAX
- BigInt.new { |mpz| LibGMP.sub_ui(mpz, self, other) }
- else
- self - other.to_big_i
+ Int.primitive_ui_check(other) do |ui, neg_ui, big_i|
+ {
+ ui: BigInt.new { |mpz| LibGMP.sub_ui(mpz, self, {{ ui }}) },
+ neg_ui: BigInt.new { |mpz| LibGMP.add_ui(mpz, self, {{ neg_ui }}) },
+ big_i: self - {{ big_i }},
+ }
end
end
@@ -208,26 +206,24 @@ struct BigInt < Int
def factorial : BigInt
if self < 0
raise ArgumentError.new("Factorial not defined for negative values")
- elsif self > LibGMP::ULong::MAX
- raise ArgumentError.new("Factorial not supported for numbers bigger than 2^64")
+ elsif self > LibGMP::UI::MAX
+ raise ArgumentError.new("Factorial not supported for numbers bigger than #{LibGMP::UI::MAX}")
end
- BigInt.new { |mpz| LibGMP.fac_ui(mpz, self) }
+ BigInt.new { |mpz| LibGMP.fac_ui(mpz, LibGMP::UI.new!(self)) }
end
def *(other : BigInt) : BigInt
BigInt.new { |mpz| LibGMP.mul(mpz, self, other) }
end
- def *(other : LibGMP::IntPrimitiveSigned) : BigInt
- BigInt.new { |mpz| LibGMP.mul_si(mpz, self, other) }
- end
-
- def *(other : LibGMP::IntPrimitiveUnsigned) : BigInt
- BigInt.new { |mpz| LibGMP.mul_ui(mpz, self, other) }
- end
-
def *(other : Int) : BigInt
- self * other.to_big_i
+ Int.primitive_si_ui_check(other) do |si, ui, big_i|
+ {
+ si: BigInt.new { |mpz| LibGMP.mul_si(mpz, self, {{ si }}) },
+ ui: BigInt.new { |mpz| LibGMP.mul_ui(mpz, self, {{ ui }}) },
+ big_i: self * {{ big_i }},
+ }
+ end
end
def &*(other) : BigInt
@@ -238,19 +234,10 @@ struct BigInt < Int
Number.expand_div [BigDecimal], BigDecimal
Number.expand_div [BigRational], BigRational
- def //(other : Int::Unsigned) : BigInt
- check_division_by_zero other
- unsafe_floored_div(other)
- end
-
def //(other : Int) : BigInt
check_division_by_zero other
- if other < 0
- (-self).unsafe_floored_div(-other)
- else
- unsafe_floored_div(other)
- end
+ unsafe_floored_div(other)
end
def tdiv(other : Int) : BigInt
@@ -264,12 +251,12 @@ struct BigInt < Int
end
def unsafe_floored_div(other : Int) : BigInt
- if LibGMP::ULong == UInt32 && (other < Int32::MIN || other > UInt32::MAX)
- unsafe_floored_div(other.to_big_i)
- elsif other < 0
- -BigInt.new { |mpz| LibGMP.fdiv_q_ui(mpz, self, other.abs) }
- else
- BigInt.new { |mpz| LibGMP.fdiv_q_ui(mpz, self, other) }
+ Int.primitive_ui_check(other) do |ui, neg_ui, big_i|
+ {
+ ui: BigInt.new { |mpz| LibGMP.fdiv_q_ui(mpz, self, {{ ui }}) },
+ neg_ui: BigInt.new { |mpz| LibGMP.fdiv_q_ui(mpz, -self, {{ neg_ui }}) },
+ big_i: unsafe_floored_div({{ big_i }}),
+ }
end
end
@@ -278,23 +265,19 @@ struct BigInt < Int
end
def unsafe_truncated_div(other : Int) : BigInt
- if LibGMP::ULong == UInt32 && (other < Int32::MIN || other > UInt32::MAX)
- unsafe_truncated_div(other.to_big_i)
- elsif other < 0
- -BigInt.new { |mpz| LibGMP.tdiv_q_ui(mpz, self, other.abs) }
- else
- BigInt.new { |mpz| LibGMP.tdiv_q_ui(mpz, self, other) }
+ Int.primitive_ui_check(other) do |ui, neg_ui, big_i|
+ {
+ ui: BigInt.new { |mpz| LibGMP.tdiv_q_ui(mpz, self, {{ ui }}) },
+ neg_ui: BigInt.new { |mpz| LibGMP.tdiv_q_ui(mpz, self, {{ neg_ui }}); LibGMP.neg(mpz, mpz) },
+ big_i: unsafe_truncated_div({{ big_i }}),
+ }
end
end
def %(other : Int) : BigInt
check_division_by_zero other
- if other < 0
- -(-self).unsafe_floored_mod(other.abs)
- else
- unsafe_floored_mod(other)
- end
+ unsafe_floored_mod(other)
end
def remainder(other : Int) : BigInt
@@ -303,46 +286,23 @@ struct BigInt < Int
unsafe_truncated_mod(other)
end
- def divmod(number : BigInt) : {BigInt, BigInt}
+ def divmod(number : Int) : {BigInt, BigInt}
check_division_by_zero number
unsafe_floored_divmod(number)
end
- def divmod(number : LibGMP::ULong)
- check_division_by_zero number
- unsafe_floored_divmod(number)
- end
-
- def divmod(number : Int::Signed) : {BigInt, BigInt}
- check_division_by_zero number
- if number > 0 && number <= LibC::Long::MAX
- unsafe_floored_divmod(LibGMP::ULong.new(number))
- else
- divmod(number.to_big_i)
- end
- end
-
- def divmod(number : Int::Unsigned)
- check_division_by_zero number
- if number <= LibC::ULong::MAX
- unsafe_floored_divmod(LibGMP::ULong.new(number))
- else
- divmod(number.to_big_i)
- end
- end
-
def unsafe_floored_mod(other : BigInt) : BigInt
BigInt.new { |mpz| LibGMP.fdiv_r(mpz, self, other) }
end
def unsafe_floored_mod(other : Int) : BigInt
- if (other < LibGMP::Long::MIN || other > LibGMP::ULong::MAX)
- unsafe_floored_mod(other.to_big_i)
- elsif other < 0
- -BigInt.new { |mpz| LibGMP.fdiv_r_ui(mpz, self, other.abs) }
- else
- BigInt.new { |mpz| LibGMP.fdiv_r_ui(mpz, self, other) }
+ Int.primitive_ui_check(other) do |ui, neg_ui, big_i|
+ {
+ ui: BigInt.new { |mpz| LibGMP.fdiv_r_ui(mpz, self, {{ ui }}) },
+ neg_ui: BigInt.new { |mpz| LibGMP.fdiv_r_ui(mpz, self, {{ neg_ui }}); LibGMP.neg(mpz, mpz) },
+ big_i: unsafe_floored_mod({{ big_i }}),
+ }
end
end
@@ -350,12 +310,14 @@ struct BigInt < Int
BigInt.new { |mpz| LibGMP.tdiv_r(mpz, self, other) }
end
- def unsafe_truncated_mod(other : LibGMP::IntPrimitive) : BigInt
- BigInt.new { |mpz| LibGMP.tdiv_r_ui(mpz, self, other.abs) }
- end
-
def unsafe_truncated_mod(other : Int) : BigInt
- BigInt.new { |mpz| LibGMP.tdiv_r_ui(mpz, self, other.abs.to_big_i) }
+ Int.primitive_ui_check(other) do |ui, neg_ui, big_i|
+ {
+ ui: BigInt.new { |mpz| LibGMP.tdiv_r_ui(mpz, self, {{ ui }}) },
+ neg_ui: BigInt.new { |mpz| LibGMP.tdiv_r_ui(mpz, self, {{ neg_ui }}) },
+ big_i: unsafe_truncated_mod({{ big_i }}),
+ }
+ end
end
def unsafe_floored_divmod(number : BigInt) : {BigInt, BigInt}
@@ -364,9 +326,15 @@ struct BigInt < Int
{the_q, the_r}
end
- def unsafe_floored_divmod(number : LibGMP::ULong) : {BigInt, BigInt}
+ def unsafe_floored_divmod(number : Int) : {BigInt, BigInt}
the_q = BigInt.new
- the_r = BigInt.new { |r| LibGMP.fdiv_qr_ui(the_q, r, self, number) }
+ the_r = Int.primitive_ui_check(number) do |ui, neg_ui, big_i|
+ {
+ ui: BigInt.new { |r| LibGMP.fdiv_qr_ui(the_q, r, self, {{ ui }}) },
+ neg_ui: BigInt.new { |r| LibGMP.fdiv_qr_ui(the_q, r, -self, {{ neg_ui }}); LibGMP.neg(r, r) },
+ big_i: BigInt.new { |r| LibGMP.fdiv_qr(the_q, r, self, {{ big_i }}) },
+ }
+ end
{the_q, the_r}
end
@@ -376,9 +344,15 @@ struct BigInt < Int
{the_q, the_r}
end
- def unsafe_truncated_divmod(number : LibGMP::ULong)
+ def unsafe_truncated_divmod(number : Int)
the_q = BigInt.new
- the_r = BigInt.new { |r| LibGMP.tdiv_qr_ui(the_q, r, self, number) }
+ the_r = Int.primitive_ui_check(number) do |ui, neg_ui, big_i|
+ {
+ ui: BigInt.new { |r| LibGMP.tdiv_qr_ui(the_q, r, self, {{ ui }}) },
+ neg_ui: BigInt.new { |r| LibGMP.tdiv_qr_ui(the_q, r, self, {{ neg_ui }}); LibGMP.neg(the_q, the_q) },
+ big_i: BigInt.new { |r| LibGMP.tdiv_qr(the_q, r, self, {{ big_i }}) },
+ }
+ end
{the_q, the_r}
end
@@ -386,17 +360,13 @@ struct BigInt < Int
LibGMP.divisible_p(self, number) != 0
end
- def divisible_by?(number : LibGMP::ULong) : Bool
- LibGMP.divisible_ui_p(self, number) != 0
- end
-
def divisible_by?(number : Int) : Bool
- if 0 <= number <= LibGMP::ULong::MAX
- LibGMP.divisible_ui_p(self, number) != 0
- elsif LibGMP::Long::MIN < number < 0
- LibGMP.divisible_ui_p(self, number.abs) != 0
- else
- divisible_by?(number.to_big_i)
+ Int.primitive_ui_check(number) do |ui, neg_ui, big_i|
+ {
+ ui: LibGMP.divisible_ui_p(self, {{ ui }}) != 0,
+ neg_ui: LibGMP.divisible_ui_p(self, {{ neg_ui }}) != 0,
+ big_i: divisible_by?({{ big_i }}),
+ }
end
end
@@ -459,8 +429,19 @@ struct BigInt < Int
# :ditto:
def gcd(other : Int) : Int
- result = LibGMP.gcd_ui(nil, self, other.abs.to_u64)
- result == 0 ? self : result
+ Int.primitive_ui_check(other) do |ui, neg_ui, big_i|
+ {
+ ui: begin
+ result = LibGMP.gcd_ui(nil, self, {{ ui }})
+ result == 0 ? self : result
+ end,
+ neg_ui: begin
+ result = LibGMP.gcd_ui(nil, self, {{ neg_ui }})
+ result == 0 ? self : result
+ end,
+ big_i: gcd({{ big_i }}),
+ }
+ end
end
# Returns the least common multiple of `self` and *other*.
@@ -470,7 +451,13 @@ struct BigInt < Int
# :ditto:
def lcm(other : Int) : BigInt
- BigInt.new { |mpz| LibGMP.lcm_ui(mpz, self, other.abs.to_u64) }
+ Int.primitive_ui_check(other) do |ui, neg_ui, big_i|
+ {
+ ui: BigInt.new { |mpz| LibGMP.lcm_ui(mpz, self, {{ ui }}) },
+ neg_ui: BigInt.new { |mpz| LibGMP.lcm_ui(mpz, self, {{ neg_ui }}) },
+ big_i: lcm({{ big_i }}),
+ }
+ end
end
def bit_length : Int32
@@ -819,15 +806,12 @@ struct Int
end
def -(other : BigInt) : BigInt
- if self < 0
- -(abs + other)
- else
- # The line below segfault on linux 32 bits for a (yet) unknown reason:
- #
- # BigInt.new { |mpz| LibGMP.ui_sub(mpz, self.to_u64, other) }
- #
- # So for now we do it a bit slower.
- to_big_i - other
+ Int.primitive_ui_check(self) do |ui, neg_ui, big_i|
+ {
+ ui: BigInt.new { |mpz| LibGMP.neg(mpz, other); LibGMP.add_ui(mpz, mpz, {{ ui }}) },
+ neg_ui: BigInt.new { |mpz| LibGMP.neg(mpz, other); LibGMP.sub_ui(mpz, mpz, {{ neg_ui }}) },
+ big_i: {{ big_i }} - other,
+ }
end
end
@@ -982,7 +966,7 @@ struct Crystal::Hasher
def int(value : BigInt)
# it should calculate `remainder(HASH_MODULUS)`
- if LibGMP::ULong == UInt64
+ if LibGMP::UI == UInt64
v = LibGMP.tdiv_ui(value, HASH_MODULUS).to_i64
value < 0 ? -v : v
elsif value >= HASH_MODULUS_INT_P || value <= HASH_MODULUS_INT_N
diff --git a/src/big/big_rational.cr b/src/big/big_rational.cr
index 4b81db43f46b..be510e6033bb 100644
--- a/src/big/big_rational.cr
+++ b/src/big/big_rational.cr
@@ -102,15 +102,13 @@ struct BigRational < Number
self <=> other.to_big_r
end
- def <=>(other : Int::Primitive)
- if LibGMP::SI::MIN <= other <= LibGMP::UI::MAX
- if other <= LibGMP::SI::MAX
- LibGMP.mpq_cmp_si(self, LibGMP::SI.new!(other), 1)
- else
- LibGMP.mpq_cmp_ui(self, LibGMP::UI.new!(other), 1)
- end
- else
- self <=> other.to_big_i
+ def <=>(other : Int)
+ Int.primitive_si_ui_check(other) do |si, ui, big_i|
+ {
+ si: LibGMP.mpq_cmp_si(self, {{ si }}, 1),
+ ui: LibGMP.mpq_cmp_ui(self, {{ ui }}, 1),
+ big_i: self <=> {{ big_i }},
+ }
end
end
diff --git a/src/big/lib_gmp.cr b/src/big/lib_gmp.cr
index 52eecdf945fb..9caf408f1494 100644
--- a/src/big/lib_gmp.cr
+++ b/src/big/lib_gmp.cr
@@ -23,10 +23,6 @@ lib LibGMP
alias Double = LibC::Double
alias BitcntT = UI
- alias IntPrimitiveSigned = Int8 | Int16 | Int32 | LibC::Long
- alias IntPrimitiveUnsigned = UInt8 | UInt16 | UInt32 | LibC::ULong
- alias IntPrimitive = IntPrimitiveSigned | IntPrimitiveUnsigned
-
{% if flag?(:win32) && flag?(:bits64) %}
alias MpExp = LibC::Long
alias MpSize = LibC::LongLong
diff --git a/src/big/number.cr b/src/big/number.cr
index 8cdacf5413e5..1251e8113db3 100644
--- a/src/big/number.cr
+++ b/src/big/number.cr
@@ -8,17 +8,18 @@ struct BigFloat
self.class.new(self / other)
end
- def /(other : UInt8 | UInt16 | UInt32 | UInt64) : BigFloat
+ def /(other : Int::Primitive) : BigFloat
# Division by 0 in BigFloat is not allowed, there is no BigFloat::Infinity
raise DivisionByZeroError.new if other == 0
- if other.is_a?(UInt8 | UInt16 | UInt32) || (LibGMP::ULong == UInt64 && other.is_a?(UInt64))
- BigFloat.new { |mpf| LibGMP.mpf_div_ui(mpf, self, other) }
- else
- BigFloat.new { |mpf| LibGMP.mpf_div(mpf, self, other.to_big_f) }
+ Int.primitive_ui_check(other) do |ui, neg_ui, _|
+ {
+ ui: BigFloat.new { |mpf| LibGMP.mpf_div_ui(mpf, self, {{ ui }}) },
+ neg_ui: BigFloat.new { |mpf| LibGMP.mpf_div_ui(mpf, self, {{ neg_ui }}); LibGMP.mpf_neg(mpf, mpf) },
+ big_i: BigFloat.new { |mpf| LibGMP.mpf_div(mpf, self, BigFloat.new(other)) },
+ }
end
end
- Number.expand_div [Int8, Int16, Int32, Int64, Int128, UInt128], BigFloat
Number.expand_div [Float32, Float64], BigFloat
end
@@ -32,6 +33,62 @@ struct BigRational
Number.expand_div [Float32, Float64], BigRational
end
+struct Int
+ # :nodoc:
+ # Yields 3 expressions: `Call`s nodes that convert *var* into a `LibGMP::SI`,
+ # a `LibGMP::UI`, and a `BigInt` respectively. These expressions are not
+ # evaluated unless they are interpolated in *block*.
+ #
+ # *block* should return a named tuple: the value for `:si` is returned by the
+ # macro if *var* fits into a `LibGMP::SI`, the value for `:ui` returned if
+ # *var* fits into a `LibGMP::UI`, and the value for `:big_i` otherwise.
+ macro primitive_si_ui_check(var, &block)
+ {%
+ exps = yield(
+ "::LibGMP::SI.new!(#{var.id})".id,
+ "::LibGMP::UI.new!(#{var.id})".id,
+ "::BigInt.new(#{var.id})".id,
+ )
+ %}
+ if ::LibGMP::SI::MIN <= {{ var }} <= ::LibGMP::UI::MAX
+ if {{ var }} <= ::LibGMP::SI::MAX
+ {{ exps[:si] }}
+ else
+ {{ exps[:ui] }}
+ end
+ else
+ {{ exps[:big_i] }}
+ end
+ end
+
+ # :nodoc:
+ # Yields 3 expressions: `Call`s nodes that convert *var* into a `LibGMP::UI`,
+ # the negative of *var* into a `LibGMP::UI`, and *var* into a `BigInt`,
+ # respectively. These expressions are not evaluated unless they are
+ # interpolated in *block*.
+ #
+ # *block* should return a named tuple: the value for `:ui` is returned by the
+ # macro if *var* fits into a `LibGMP::UI`, the value for `:neg_ui` returned if
+ # the negative of *var* fits into a `LibGMP::UI`, and the value for `:big_i`
+ # otherwise.
+ macro primitive_ui_check(var, &block)
+ {%
+ exps = yield(
+ "::LibGMP::UI.new!(#{var.id})".id,
+ "::LibGMP::UI.new!((#{var.id}).abs_unsigned)".id,
+ "::BigInt.new(#{var.id})".id,
+ )
+ %}
+ if ::LibGMP::UI::MIN <= {{ var }} <= ::LibGMP::UI::MAX
+ {{ exps[:ui] }}
+ elsif {{ var }}.responds_to?(:abs_unsigned) && {{ var }}.abs_unsigned <= ::LibGMP::UI::MAX
+ {{ exps[:neg_ui] }}
+ else
+ {{ exps[:big_i] }}
+ end
+ end
+end
+
struct Int8
Number.expand_div [BigInt], BigFloat
Number.expand_div [BigFloat], BigFloat
diff --git a/src/compiler/crystal/codegen/target.cr b/src/compiler/crystal/codegen/target.cr
index 1e731d801b58..8817221c18b9 100644
--- a/src/compiler/crystal/codegen/target.cr
+++ b/src/compiler/crystal/codegen/target.cr
@@ -195,7 +195,7 @@ class Crystal::Codegen::Target
target = LLVM::Target.from_triple(self.to_s)
machine = target.create_target_machine(self.to_s, cpu: cpu, features: features, opt_level: opt_level, code_model: code_model).not_nil!
- # We need to disable global isel until https://reviews.llvm.org/D80898 is released,
+ # FIXME: We need to disable global isel until https://reviews.llvm.org/D80898 is released,
# or we fixed generating values for 0 sized types.
# When removing this, also remove it from the ABI specs and jit compiler.
# See https://github.com/crystal-lang/crystal/issues/9297#issuecomment-636512270
diff --git a/src/compiler/crystal/tools/doc/html/_macros_inherited.html b/src/compiler/crystal/tools/doc/html/_macros_inherited.html
new file mode 100644
index 000000000000..3ef7f287ca43
--- /dev/null
+++ b/src/compiler/crystal/tools/doc/html/_macros_inherited.html
@@ -0,0 +1,11 @@
+<% macro_groups = macros.group_by { |m| m.name } %>
+<% unless macro_groups.empty? %>
+
Macros inherited from <%= ancestor.kind %> <%= ancestor.link_from(type) %>
+ <% i = 0 %>
+ <% macro_groups.each do |k, v| %>
+
+ <%= v.map { |m| m.name + m.args_to_html(:highlight) } .join("
") %>
+ <%= k %><%= ", " if i != macro_groups.size - 1 %>
+ <% i += 1 %>
+ <% end %>
+<% end %>
diff --git a/src/compiler/crystal/tools/doc/html/type.html b/src/compiler/crystal/tools/doc/html/type.html
index aaa3d338df9e..cd8115e74f5c 100644
--- a/src/compiler/crystal/tools/doc/html/type.html
+++ b/src/compiler/crystal/tools/doc/html/type.html
@@ -103,6 +103,7 @@
<%= MethodsInheritedTemplate.new(type, ancestor, ancestor.instance_methods, "Instance") %>
<%= MethodsInheritedTemplate.new(type, ancestor, ancestor.constructors, "Constructor") %>
<%= MethodsInheritedTemplate.new(type, ancestor, ancestor.class_methods, "Class") %>
+ <%= MacrosInheritedTemplate.new(type, ancestor, ancestor.macros) %>
<% end %>
diff --git a/src/compiler/crystal/tools/doc/templates.cr b/src/compiler/crystal/tools/doc/templates.cr
index 6463ea637479..91ad32e1d0d1 100644
--- a/src/compiler/crystal/tools/doc/templates.cr
+++ b/src/compiler/crystal/tools/doc/templates.cr
@@ -49,6 +49,10 @@ module Crystal::Doc
ECR.def_to_s "#{__DIR__}/html/_methods_inherited.html"
end
+ record MacrosInheritedTemplate, type : Type, ancestor : Type, macros : Array(Macro) do
+ ECR.def_to_s "#{__DIR__}/html/_macros_inherited.html"
+ end
+
record OtherTypesTemplate, title : String, type : Type, other_types : Array(Type) do
ECR.def_to_s "#{__DIR__}/html/_other_types.html"
end
diff --git a/src/compiler/crystal/tools/unreachable.cr b/src/compiler/crystal/tools/unreachable.cr
index e03d64f1becc..f89f90e4c37d 100644
--- a/src/compiler/crystal/tools/unreachable.cr
+++ b/src/compiler/crystal/tools/unreachable.cr
@@ -1,11 +1,12 @@
require "../syntax/ast"
require "../compiler"
require "json"
+require "csv"
module Crystal
class Command
private def unreachable
- config, result = compile_no_codegen "tool unreachable", path_filter: true, unreachable_command: true
+ config, result = compile_no_codegen "tool unreachable", path_filter: true, unreachable_command: true, allowed_formats: %w[text json csv]
unreachable = UnreachableVisitor.new
@@ -39,6 +40,8 @@ module Crystal
case format
when "json"
to_json(STDOUT)
+ when "csv"
+ to_csv(STDOUT)
else
to_text(STDOUT)
end
@@ -83,6 +86,22 @@ module Crystal
end
end
end
+
+ def to_csv(io)
+ CSV.build(io) do |builder|
+ builder.row %w[name file line column length annotations]
+ each do |a_def, location|
+ builder.row do |row|
+ row << a_def.short_reference
+ row << location.filename
+ row << location.line_number
+ row << location.column_number
+ row << a_def.length
+ row << a_def.all_annotations.try(&.join(" "))
+ end
+ end
+ end
+ end
end
# This visitor walks the entire reachable code tree and collect locations
diff --git a/src/crystal/system/thread.cr b/src/crystal/system/thread.cr
index 9e58962fd49b..878be639a7a3 100644
--- a/src/crystal/system/thread.cr
+++ b/src/crystal/system/thread.cr
@@ -1,26 +1,127 @@
+# :nodoc:
+module Crystal::System::Thread
+ # alias Handle
+
+ # def self.new_handle(thread_obj : ::Thread) : Handle
+
+ # def self.current_handle : Handle
+
+ # def self.yield_current : Nil
+
+ # def self.current_thread : ::Thread
+
+ # def self.current_thread=(thread : ::Thread)
+
+ # private def system_join : Exception?
+
+ # private def system_close
+
+ # private def stack_address : Void*
+end
+
+{% if flag?(:wasi) %}
+ require "./wasi/thread"
+{% elsif flag?(:unix) %}
+ require "./unix/pthread"
+{% elsif flag?(:win32) %}
+ require "./win32/thread"
+{% else %}
+ {% raise "Thread not supported" %}
+{% end %}
+
# :nodoc:
class Thread
+ include Crystal::System::Thread
+
+ # all thread objects, so the GC can see them (it doesn't scan thread locals)
+ protected class_getter(threads) { Thread::LinkedList(Thread).new }
+
+ @system_handle : Crystal::System::Thread::Handle
+ @exception : Exception?
+ @detached = Atomic::Flag.new
+
+ # Returns the Fiber representing the thread's main stack.
+ getter! main_fiber : Fiber
+
+ # :nodoc:
+ property next : Thread?
+
+ # :nodoc:
+ property previous : Thread?
+
+ def self.unsafe_each(&)
+ threads.unsafe_each { |thread| yield thread }
+ end
+
# Creates and starts a new system thread.
- # def initialize(&proc : ->)
+ def initialize(&@func : ->)
+ @system_handle = uninitialized Crystal::System::Thread::Handle
+ init_handle
+ end
# Used once to initialize the thread object representing the main thread of
# the process (that already exists).
- # def initialize
+ def initialize
+ @func = ->{}
+ @system_handle = Crystal::System::Thread.current_handle
+ @main_fiber = Fiber.new(stack_address, self)
- # Suspends the current thread until this thread terminates.
- # def join : Nil
+ Thread.threads.push(self)
+ end
- # Returns the Fiber representing the thread's main stack.
- # def main_fiber
+ private def detach(&)
+ if @detached.test_and_set
+ yield
+ end
+ end
+
+ # Suspends the current thread until this thread terminates.
+ def join : Nil
+ detach do
+ if ex = system_join
+ @exception ||= ex
+ end
+ end
- # Yields the thread.
- # def self.yield : Nil
+ if exception = @exception
+ raise exception
+ end
+ end
# Returns the Thread object associated to the running system thread.
- # def self.current : Thread
+ def self.current : Thread
+ Crystal::System::Thread.current_thread
+ end
# Associates the Thread object to the running system thread.
- # def self.current=(thread : Thread)
+ protected def self.current=(current : Thread) : Thread
+ Crystal::System::Thread.current_thread = current
+ current
+ end
+
+ # Yields the currently running thread.
+ def self.yield : Nil
+ Crystal::System::Thread.yield_current
+ end
+
+ # :nodoc:
+ getter scheduler : Crystal::Scheduler { Crystal::Scheduler.new(main_fiber) }
+
+ protected def start
+ Thread.threads.push(self)
+ Thread.current = self
+ @main_fiber = fiber = Fiber.new(stack_address, self)
+
+ begin
+ @func.call
+ rescue ex
+ @exception = ex
+ ensure
+ Thread.threads.delete(self)
+ Fiber.inactive(fiber)
+ detach { system_close }
+ end
+ end
# Holds the GC thread handler
property gc_thread_handler : Void* = Pointer(Void).null
@@ -28,13 +129,3 @@ end
require "./thread_linked_list"
require "./thread_condition_variable"
-
-{% if flag?(:wasi) %}
- require "./wasi/thread"
-{% elsif flag?(:unix) %}
- require "./unix/pthread"
-{% elsif flag?(:win32) %}
- require "./win32/thread"
-{% else %}
- {% raise "Thread not supported" %}
-{% end %}
diff --git a/src/crystal/system/unix/pthread.cr b/src/crystal/system/unix/pthread.cr
index bde5cc7b4333..44076dedcc43 100644
--- a/src/crystal/system/unix/pthread.cr
+++ b/src/crystal/system/unix/pthread.cr
@@ -1,62 +1,34 @@
require "c/pthread"
require "c/sched"
-class Thread
- # all thread objects, so the GC can see them (it doesn't scan thread locals)
- protected class_getter(threads) { Thread::LinkedList(Thread).new }
+module Crystal::System::Thread
+ alias Handle = LibC::PthreadT
- @th : LibC::PthreadT
- @exception : Exception?
- @detached = Atomic(UInt8).new(0)
- @main_fiber : Fiber?
-
- # :nodoc:
- property next : Thread?
-
- # :nodoc:
- property previous : Thread?
-
- def self.unsafe_each(&)
- threads.unsafe_each { |thread| yield thread }
- end
-
- # Starts a new system thread.
- def initialize(&@func : ->)
- @th = uninitialized LibC::PthreadT
-
- ret = GC.pthread_create(pointerof(@th), Pointer(LibC::PthreadAttrT).null, ->(data : Void*) {
- (data.as(Thread)).start
- Pointer(Void).null
- }, self.as(Void*))
-
- if ret != 0
- raise RuntimeError.from_os_error("pthread_create", Errno.new(ret))
- end
+ def to_unsafe
+ @system_handle
end
- # Used once to initialize the thread object representing the main thread of
- # the process (that already exists).
- def initialize
- @func = ->{}
- @th = LibC.pthread_self
- @main_fiber = Fiber.new(stack_address, self)
-
- Thread.threads.push(self)
+ private def init_handle
+ # NOTE: the thread may start before `pthread_create` returns, so
+ # `@system_handle` must be set as soon as possible; we cannot use a separate
+ # handle and assign it to `@system_handle`, which would have been too late
+ ret = GC.pthread_create(
+ thread: pointerof(@system_handle),
+ attr: Pointer(LibC::PthreadAttrT).null,
+ start: ->(data : Void*) { data.as(::Thread).start; Pointer(Void).null },
+ arg: self.as(Void*),
+ )
+
+ raise RuntimeError.from_os_error("pthread_create", Errno.new(ret)) unless ret == 0
end
- private def detach(&)
- if @detached.compare_and_set(0, 1).last
- yield
- end
+ def self.current_handle : Handle
+ LibC.pthread_self
end
- # Suspends the current thread until this thread terminates.
- def join : Nil
- detach { GC.pthread_join(@th) }
-
- if exception = @exception
- raise exception
- end
+ def self.yield_current : Nil
+ ret = LibC.sched_yield
+ raise RuntimeError.from_errno("sched_yield") unless ret == 0
end
{% if flag?(:openbsd) %}
@@ -70,68 +42,33 @@ class Thread
current_key
end
- # Returns the Thread object associated to the running system thread.
- def self.current : Thread
+ def self.current_thread : ::Thread
if ptr = LibC.pthread_getspecific(@@current_key)
- ptr.as(Thread)
+ ptr.as(::Thread)
else
- # Thread#start sets @@current as soon it starts. Thus we know
- # that if @@current is not set then we are in the main thread
- self.current = new
+ # Thread#start sets `Thread.current` as soon it starts. Thus we know
+ # that if `Thread.current` is not set then we are in the main thread
+ self.current_thread = ::Thread.new
end
end
- # Associates the Thread object to the running system thread.
- protected def self.current=(thread : Thread) : Thread
+ def self.current_thread=(thread : ::Thread)
ret = LibC.pthread_setspecific(@@current_key, thread.as(Void*))
raise RuntimeError.from_os_error("pthread_setspecific", Errno.new(ret)) unless ret == 0
thread
end
{% else %}
@[ThreadLocal]
- @@current : Thread?
-
- # Returns the Thread object associated to the running system thread.
- def self.current : Thread
- # Thread#start sets @@current as soon it starts. Thus we know
- # that if @@current is not set then we are in the main thread
- @@current ||= new
- end
-
- # Associates the Thread object to the running system thread.
- protected def self.current=(@@current : Thread) : Thread
- end
+ class_property current_thread : ::Thread { ::Thread.new }
{% end %}
- def self.yield : Nil
- ret = LibC.sched_yield
- raise RuntimeError.from_errno("sched_yield") unless ret == 0
- end
-
- # Returns the Fiber representing the thread's main stack.
- def main_fiber : Fiber
- @main_fiber.not_nil!
- end
-
- # :nodoc:
- def scheduler : Crystal::Scheduler
- @scheduler ||= Crystal::Scheduler.new(main_fiber)
+ private def system_join : Exception?
+ ret = GC.pthread_join(@system_handle)
+ RuntimeError.from_os_error("pthread_join", Errno.new(ret)) unless ret == 0
end
- protected def start
- Thread.threads.push(self)
- Thread.current = self
- @main_fiber = fiber = Fiber.new(stack_address, self)
-
- begin
- @func.call
- rescue ex
- @exception = ex
- ensure
- Thread.threads.delete(self)
- Fiber.inactive(fiber)
- detach { GC.pthread_detach(@th) }
- end
+ private def system_close
+ GC.pthread_detach(@system_handle)
end
private def stack_address : Void*
@@ -139,7 +76,7 @@ class Thread
{% if flag?(:darwin) %}
# FIXME: pthread_get_stacksize_np returns bogus value on macOS X 10.9.0:
- address = LibC.pthread_get_stackaddr_np(@th) - LibC.pthread_get_stacksize_np(@th)
+ address = LibC.pthread_get_stackaddr_np(@system_handle) - LibC.pthread_get_stacksize_np(@system_handle)
{% elsif flag?(:bsd) && !flag?(:openbsd) %}
ret = LibC.pthread_attr_init(out attr)
unless ret == 0
@@ -147,19 +84,19 @@ class Thread
raise RuntimeError.from_os_error("pthread_attr_init", Errno.new(ret))
end
- if LibC.pthread_attr_get_np(@th, pointerof(attr)) == 0
+ if LibC.pthread_attr_get_np(@system_handle, pointerof(attr)) == 0
LibC.pthread_attr_getstack(pointerof(attr), pointerof(address), out _)
end
ret = LibC.pthread_attr_destroy(pointerof(attr))
raise RuntimeError.from_os_error("pthread_attr_destroy", Errno.new(ret)) unless ret == 0
{% elsif flag?(:linux) %}
- if LibC.pthread_getattr_np(@th, out attr) == 0
+ if LibC.pthread_getattr_np(@system_handle, out attr) == 0
LibC.pthread_attr_getstack(pointerof(attr), pointerof(address), out _)
end
ret = LibC.pthread_attr_destroy(pointerof(attr))
raise RuntimeError.from_os_error("pthread_attr_destroy", Errno.new(ret)) unless ret == 0
{% elsif flag?(:openbsd) %}
- ret = LibC.pthread_stackseg_np(@th, out stack)
+ ret = LibC.pthread_stackseg_np(@system_handle, out stack)
raise RuntimeError.from_os_error("pthread_stackseg_np", Errno.new(ret)) unless ret == 0
address =
@@ -172,11 +109,6 @@ class Thread
address
end
-
- # :nodoc:
- def to_unsafe
- @th
- end
end
# In musl (alpine) the calls to unwind API segfaults
diff --git a/src/crystal/system/wasi/thread.cr b/src/crystal/system/wasi/thread.cr
index 805c7fbb77a6..0e641faba785 100644
--- a/src/crystal/system/wasi/thread.cr
+++ b/src/crystal/system/wasi/thread.cr
@@ -1,53 +1,25 @@
-class Thread
- @main_fiber : Fiber?
+module Crystal::System::Thread
+ alias Handle = Nil
- def initialize
- @main_fiber = Fiber.new(stack_address, self)
-
- # TODO: Create thread
- end
-
- def initialize(&func : ->)
- initialize
- end
-
- def join : Nil
- raise NotImplementedError.new("Thread#join")
+ def self.new_handle(thread_obj : ::Thread) : Handle
+ raise NotImplementedError.new("Crystal::System::Thread.new_handle")
end
- def self.yield : Nil
- raise NotImplementedError.new("Thread.yield")
+ def self.current_handle : Handle
+ nil
end
- @@current = Thread.new
-
- # Associates the Thread object to the running system thread.
- protected def self.current=(@@current : Thread) : Thread
- end
-
- # Returns the Thread object associated to the running system thread.
- def self.current : Thread
- @@current
+ def self.yield_current : Nil
+ raise NotImplementedError.new("Crystal::System::Thread.yield_current")
end
- # Create the thread object for the current thread (aka the main thread of the
- # process).
- #
- # TODO: consider moving to `kernel.cr` or `crystal/main.cr`
- self.current = new
-
- # Returns the Fiber representing the thread's main stack.
- def main_fiber
- @main_fiber.not_nil!
- end
+ class_property current_thread : ::Thread { ::Thread.new }
- # :nodoc:
- def scheduler
- @scheduler ||= Crystal::Scheduler.new(main_fiber)
+ private def system_join : Exception?
+ NotImplementedError.new("Crystal::System::Thread#system_join")
end
- protected def start
- raise NotImplementedError.new("Thread#start")
+ private def system_close
end
private def stack_address : Void*
diff --git a/src/crystal/system/win32/thread.cr b/src/crystal/system/win32/thread.cr
index badfff437ed5..9a13bfb4dc03 100644
--- a/src/crystal/system/win32/thread.cr
+++ b/src/crystal/system/win32/thread.cr
@@ -1,115 +1,52 @@
require "c/processthreadsapi"
require "c/synchapi"
-class Thread
- # all thread objects, so the GC can see them (it doesn't scan thread locals)
- protected class_getter(threads) { Thread::LinkedList(Thread).new }
+module Crystal::System::Thread
+ alias Handle = LibC::HANDLE
- @th : LibC::HANDLE
- @exception : Exception?
- @detached = Atomic(UInt8).new(0)
- @main_fiber : Fiber?
-
- # :nodoc:
- property next : Thread?
-
- # :nodoc:
- property previous : Thread?
-
- def self.unsafe_each(&)
- threads.unsafe_each { |thread| yield thread }
+ def to_unsafe
+ @system_handle
end
- # Starts a new system thread.
- def initialize(&@func : ->)
- @th = uninitialized LibC::HANDLE
-
- @th = GC.beginthreadex(
+ private def init_handle
+ @system_handle = GC.beginthreadex(
security: Pointer(Void).null,
stack_size: LibC::UInt.zero,
- start_address: ->(data : Void*) { data.as(Thread).start; LibC::UInt.zero },
+ start_address: ->(data : Void*) { data.as(::Thread).start; LibC::UInt.zero },
arglist: self.as(Void*),
initflag: LibC::UInt.zero,
- thrdaddr: Pointer(LibC::UInt).null)
+ thrdaddr: Pointer(LibC::UInt).null,
+ )
end
- # Used once to initialize the thread object representing the main thread of
- # the process (that already exists).
- def initialize
+ def self.current_handle : Handle
# `GetCurrentThread` returns a _constant_ and is only meaningful as an
# argument to Win32 APIs; to uniquely identify it we must duplicate the handle
- @th = uninitialized LibC::HANDLE
cur_proc = LibC.GetCurrentProcess
- LibC.DuplicateHandle(cur_proc, LibC.GetCurrentThread, cur_proc, pointerof(@th), 0, true, LibC::DUPLICATE_SAME_ACCESS)
-
- @func = ->{}
- @main_fiber = Fiber.new(stack_address, self)
-
- Thread.threads.push(self)
- end
-
- private def detach(&)
- if @detached.compare_and_set(0, 1).last
- yield
+ if LibC.DuplicateHandle(cur_proc, LibC.GetCurrentThread, cur_proc, out handle, 0, true, LibC::DUPLICATE_SAME_ACCESS) == 0
+ raise RuntimeError.from_winerror("DuplicateHandle")
end
+ handle
end
- # Suspends the current thread until this thread terminates.
- def join : Nil
- detach do
- if LibC.WaitForSingleObject(@th, LibC::INFINITE) != LibC::WAIT_OBJECT_0
- @exception ||= RuntimeError.from_winerror("WaitForSingleObject")
- end
- if LibC.CloseHandle(@th) == 0
- @exception ||= RuntimeError.from_winerror("CloseHandle")
- end
- end
-
- if exception = @exception
- raise exception
- end
- end
-
- @[ThreadLocal]
- @@current : Thread?
-
- # Returns the Thread object associated to the running system thread.
- def self.current : Thread
- @@current ||= new
- end
-
- # Associates the Thread object to the running system thread.
- protected def self.current=(@@current : Thread) : Thread
- end
-
- def self.yield : Nil
+ def self.yield_current : Nil
LibC.SwitchToThread
end
- # Returns the Fiber representing the thread's main stack.
- def main_fiber : Fiber
- @main_fiber.not_nil!
- end
+ @[ThreadLocal]
+ class_property current_thread : ::Thread { ::Thread.new }
- # :nodoc:
- def scheduler : Crystal::Scheduler
- @scheduler ||= Crystal::Scheduler.new(main_fiber)
+ private def system_join : Exception?
+ if LibC.WaitForSingleObject(@system_handle, LibC::INFINITE) != LibC::WAIT_OBJECT_0
+ return RuntimeError.from_winerror("WaitForSingleObject")
+ end
+ if LibC.CloseHandle(@system_handle) == 0
+ return RuntimeError.from_winerror("CloseHandle")
+ end
end
- protected def start
- Thread.threads.push(self)
- Thread.current = self
- @main_fiber = fiber = Fiber.new(stack_address, self)
-
- begin
- @func.call
- rescue ex
- @exception = ex
- ensure
- Thread.threads.delete(self)
- Fiber.inactive(fiber)
- detach { LibC.CloseHandle(@th) }
- end
+ private def system_close
+ LibC.CloseHandle(@system_handle)
end
private def stack_address : Void*
@@ -124,9 +61,4 @@ class Thread
low_limit
{% end %}
end
-
- # :nodoc:
- def to_unsafe
- @th
- end
end
diff --git a/src/gc/boehm.cr b/src/gc/boehm.cr
index 28056f75aead..b4dfb0061900 100644
--- a/src/gc/boehm.cr
+++ b/src/gc/boehm.cr
@@ -262,10 +262,8 @@ module GC
end
# :nodoc:
- def self.pthread_join(thread : LibC::PthreadT) : Void*
- ret = LibGC.pthread_join(thread, out value)
- raise RuntimeError.from_os_error("pthread_join", Errno.new(ret)) unless ret == 0
- value
+ def self.pthread_join(thread : LibC::PthreadT)
+ LibGC.pthread_join(thread, nil)
end
# :nodoc:
diff --git a/src/gc/none.cr b/src/gc/none.cr
index 4e4441f6e54e..c71ab05ccd8d 100644
--- a/src/gc/none.cr
+++ b/src/gc/none.cr
@@ -85,10 +85,8 @@ module GC
end
# :nodoc:
- def self.pthread_join(thread : LibC::PthreadT) : Void*
- ret = LibC.pthread_join(thread, out value)
- raise RuntimeError.from_errno("pthread_join") unless ret == 0
- value
+ def self.pthread_join(thread : LibC::PthreadT)
+ LibC.pthread_join(thread, nil)
end
# :nodoc:
diff --git a/src/hash.cr b/src/hash.cr
index 156f2d7d6313..8d8ccea22a0b 100644
--- a/src/hash.cr
+++ b/src/hash.cr
@@ -266,6 +266,16 @@ class Hash(K, V)
# Creates a new empty `Hash` with a *block* that handles missing keys.
#
# ```
+ # inventory = Hash(String, Int32).new(0)
+ # inventory["socks"] = 3
+ # inventory["pickles"] # => 0
+ # ```
+ #
+ # WARNING: When the default block is invoked on a missing key, its return
+ # value is *not* implicitly stored into the hash under that key. If you want
+ # that behaviour, you need to put it explicitly:
+ #
+ # ```
# hash = Hash(String, Int32).new do |hash, key|
# hash[key] = key.size
# end
@@ -294,14 +304,23 @@ class Hash(K, V)
# inventory["pickles"] # => 0
# ```
#
- # NOTE: The default value is passed by reference:
+ # WARNING: When the default value gets returned on a missing key, it is *not*
+ # stored into the hash under that key. If you want that behaviour, please use
+ # the overload with a block.
+ #
+ # WARNING: The default value is returned as-is. It gets neither duplicated nor
+ # cloned. For types with reference semantics this means it will be exactly the
+ # *same* object every time.
+ #
# ```
- # arr = [1, 2, 3]
- # hash = Hash(String, Array(Int32)).new(arr)
- # hash["3"][1] = 4
- # arr # => [1, 4, 3]
+ # hash = Hash(String, Array(Int32)).new([1])
+ # hash["a"][0] = 2
+ # hash["b"] # => [2]
# ```
#
+ # * `.new(&block : (Hash(K, V), K -> V))` is an alternative with a block that
+ # can return a different default value for each invocation.
+ #
# The *initial_capacity* is useful to avoid unnecessary reallocations
# of the internal buffer in case of growth. If the number of elements
# a hash will hold is known, the hash should be initialized with that
diff --git a/src/int.cr b/src/int.cr
index 2abfa640763f..6f0d3aa5881a 100644
--- a/src/int.cr
+++ b/src/int.cr
@@ -897,6 +897,62 @@ struct Int8
0_i8 - self
end
+ # Returns `self` converted to a signed value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_u32.to_signed # => 1_i32
+ # 2_u16.to_signed # => 2_i16
+ # 3_i64.to_signed # => 3_i64
+ # ```
+ def to_signed : Int8
+ self
+ end
+
+ # Returns `self` converted to a signed value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ #
+ # ```
+ # 1_u32.to_signed! # => 1_i32
+ # 65530_u16.to_signed! # => -6_i16
+ # 3_i64.to_signed! # => 3_i64
+ # ```
+ def to_signed! : Int8
+ self
+ end
+
+ # Returns `self` converted to an unsigned value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_i32.to_unsigned # => 1_u32
+ # 2_i16.to_unsigned # => 2_u16
+ # 3_u64.to_unsigned # => 3_u64
+ # ```
+ def to_unsigned : UInt8
+ to_u8
+ end
+
+ # Returns `self` converted to an unsigned value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ #
+ # ```
+ # 1_i32.to_unsigned! # => 1_u32
+ # (-6_i16).to_unsigned! # => 65530_u16
+ # 3_u64.to_unsigned! # => 3_u64
+ # ```
+ def to_unsigned! : UInt8
+ to_u8!
+ end
+
# Returns the absolute value of `self` as an unsigned value of the same size.
#
# Returns `self` if `self` is already an `Int::Unsigned`. This method never
@@ -1027,6 +1083,62 @@ struct Int16
0_i16 - self
end
+ # Returns `self` converted to a signed value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_u32.to_signed # => 1_i32
+ # 2_u16.to_signed # => 2_i16
+ # 3_i64.to_signed # => 3_i64
+ # ```
+ def to_signed : Int16
+ self
+ end
+
+ # Returns `self` converted to a signed value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ #
+ # ```
+ # 1_u32.to_signed! # => 1_i32
+ # 65530_u16.to_signed! # => -6_i16
+ # 3_i64.to_signed! # => 3_i64
+ # ```
+ def to_signed! : Int16
+ self
+ end
+
+ # Returns `self` converted to an unsigned value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_i32.to_unsigned # => 1_u32
+ # 2_i16.to_unsigned # => 2_u16
+ # 3_u64.to_unsigned # => 3_u64
+ # ```
+ def to_unsigned : UInt16
+ to_u16
+ end
+
+ # Returns `self` converted to an unsigned value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ #
+ # ```
+ # 1_i32.to_unsigned! # => 1_u32
+ # (-6_i16).to_unsigned! # => 65530_u16
+ # 3_u64.to_unsigned! # => 3_u64
+ # ```
+ def to_unsigned! : UInt16
+ to_u16!
+ end
+
# Returns the absolute value of `self` as an unsigned value of the same size.
#
# Returns `self` if `self` is already an `Int::Unsigned`. This method never
@@ -1157,6 +1269,62 @@ struct Int32
0 - self
end
+ # Returns `self` converted to a signed value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_u32.to_signed # => 1_i32
+ # 2_u16.to_signed # => 2_i16
+ # 3_i64.to_signed # => 3_i64
+ # ```
+ def to_signed : Int32
+ self
+ end
+
+ # Returns `self` converted to a signed value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ #
+ # ```
+ # 1_u32.to_signed! # => 1_i32
+ # 65530_u16.to_signed! # => -6_i16
+ # 3_i64.to_signed! # => 3_i64
+ # ```
+ def to_signed! : Int32
+ self
+ end
+
+ # Returns `self` converted to an unsigned value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_i32.to_unsigned # => 1_u32
+ # 2_i16.to_unsigned # => 2_u16
+ # 3_u64.to_unsigned # => 3_u64
+ # ```
+ def to_unsigned : UInt32
+ to_u32
+ end
+
+ # Returns `self` converted to an unsigned value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ #
+ # ```
+ # 1_i32.to_unsigned! # => 1_u32
+ # (-6_i16).to_unsigned! # => 65530_u16
+ # 3_u64.to_unsigned! # => 3_u64
+ # ```
+ def to_unsigned! : UInt32
+ to_u32!
+ end
+
# Returns the absolute value of `self` as an unsigned value of the same size.
#
# Returns `self` if `self` is already an `Int::Unsigned`. This method never
@@ -1287,6 +1455,62 @@ struct Int64
0_i64 - self
end
+ # Returns `self` converted to a signed value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_u32.to_signed # => 1_i32
+ # 2_u16.to_signed # => 2_i16
+ # 3_i64.to_signed # => 3_i64
+ # ```
+ def to_signed : Int64
+ self
+ end
+
+ # Returns `self` converted to a signed value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ #
+ # ```
+ # 1_u32.to_signed! # => 1_i32
+ # 65530_u16.to_signed! # => -6_i16
+ # 3_i64.to_signed! # => 3_i64
+ # ```
+ def to_signed! : Int64
+ self
+ end
+
+ # Returns `self` converted to an unsigned value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_i32.to_unsigned # => 1_u32
+ # 2_i16.to_unsigned # => 2_u16
+ # 3_u64.to_unsigned # => 3_u64
+ # ```
+ def to_unsigned : UInt64
+ to_u64
+ end
+
+ # Returns `self` converted to an unsigned value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ #
+ # ```
+ # 1_i32.to_unsigned! # => 1_u32
+ # (-6_i16).to_unsigned! # => 65530_u16
+ # 3_u64.to_unsigned! # => 3_u64
+ # ```
+ def to_unsigned! : UInt64
+ to_u64!
+ end
+
# Returns the absolute value of `self` as an unsigned value of the same size.
#
# Returns `self` if `self` is already an `Int::Unsigned`. This method never
@@ -1420,6 +1644,62 @@ struct Int128
Int128.new(0) - self
end
+ # Returns `self` converted to a signed value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_u32.to_signed # => 1_i32
+ # 2_u16.to_signed # => 2_i16
+ # 3_i64.to_signed # => 3_i64
+ # ```
+ def to_signed : Int128
+ self
+ end
+
+ # Returns `self` converted to a signed value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ #
+ # ```
+ # 1_u32.to_signed! # => 1_i32
+ # 65530_u16.to_signed! # => -6_i16
+ # 3_i64.to_signed! # => 3_i64
+ # ```
+ def to_signed! : Int128
+ self
+ end
+
+ # Returns `self` converted to an unsigned value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_i32.to_unsigned # => 1_u32
+ # 2_i16.to_unsigned # => 2_u16
+ # 3_u64.to_unsigned # => 3_u64
+ # ```
+ def to_unsigned : UInt128
+ to_u128
+ end
+
+ # Returns `self` converted to an unsigned value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ #
+ # ```
+ # 1_i32.to_unsigned! # => 1_u32
+ # (-6_i16).to_unsigned! # => 65530_u16
+ # 3_u64.to_unsigned! # => 3_u64
+ # ```
+ def to_unsigned! : UInt128
+ to_u128!
+ end
+
# Returns the absolute value of `self` as an unsigned value of the same size.
#
# Returns `self` if `self` is already an `Int::Unsigned`. This method never
@@ -1550,6 +1830,62 @@ struct UInt8
0_u8 &- self
end
+ # Returns `self` converted to a signed value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_u32.to_signed # => 1_i32
+ # 2_u16.to_signed # => 2_i16
+ # 3_i64.to_signed # => 3_i64
+ # ```
+ def to_signed : Int8
+ to_i8
+ end
+
+ # Returns `self` converted to a signed value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ #
+ # ```
+ # 1_u32.to_signed! # => 1_i32
+ # 65530_u16.to_signed! # => -6_i16
+ # 3_i64.to_signed! # => 3_i64
+ # ```
+ def to_signed! : Int8
+ to_i8!
+ end
+
+ # Returns `self` converted to an unsigned value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_i32.to_unsigned # => 1_u32
+ # 2_i16.to_unsigned # => 2_u16
+ # 3_u64.to_unsigned # => 3_u64
+ # ```
+ def to_unsigned : UInt8
+ self
+ end
+
+ # Returns `self` converted to an unsigned value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ #
+ # ```
+ # 1_i32.to_unsigned! # => 1_u32
+ # (-6_i16).to_unsigned! # => 65530_u16
+ # 3_u64.to_unsigned! # => 3_u64
+ # ```
+ def to_unsigned! : UInt8
+ self
+ end
+
def abs : self
self
end
@@ -1684,6 +2020,62 @@ struct UInt16
0_u16 &- self
end
+ # Returns `self` converted to a signed value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_u32.to_signed # => 1_i32
+ # 2_u16.to_signed # => 2_i16
+ # 3_i64.to_signed # => 3_i64
+ # ```
+ def to_signed : Int16
+ to_i16
+ end
+
+ # Returns `self` converted to a signed value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ #
+ # ```
+ # 1_u32.to_signed! # => 1_i32
+ # 65530_u16.to_signed! # => -6_i16
+ # 3_i64.to_signed! # => 3_i64
+ # ```
+ def to_signed! : Int16
+ to_i16!
+ end
+
+ # Returns `self` converted to an unsigned value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_i32.to_unsigned # => 1_u32
+ # 2_i16.to_unsigned # => 2_u16
+ # 3_u64.to_unsigned # => 3_u64
+ # ```
+ def to_unsigned : UInt16
+ self
+ end
+
+ # Returns `self` converted to an unsigned value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ #
+ # ```
+ # 1_i32.to_unsigned! # => 1_u32
+ # (-6_i16).to_unsigned! # => 65530_u16
+ # 3_u64.to_unsigned! # => 3_u64
+ # ```
+ def to_unsigned! : UInt16
+ self
+ end
+
def abs : self
self
end
@@ -1818,6 +2210,62 @@ struct UInt32
0_u32 &- self
end
+ # Returns `self` converted to a signed value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_u32.to_signed # => 1_i32
+ # 2_u16.to_signed # => 2_i16
+ # 3_i64.to_signed # => 3_i64
+ # ```
+ def to_signed : Int32
+ to_i32
+ end
+
+ # Returns `self` converted to a signed value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ #
+ # ```
+ # 1_u32.to_signed! # => 1_i32
+ # 65530_u16.to_signed! # => -6_i16
+ # 3_i64.to_signed! # => 3_i64
+ # ```
+ def to_signed! : Int32
+ to_i32!
+ end
+
+ # Returns `self` converted to an unsigned value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_i32.to_unsigned # => 1_u32
+ # 2_i16.to_unsigned # => 2_u16
+ # 3_u64.to_unsigned # => 3_u64
+ # ```
+ def to_unsigned : UInt32
+ self
+ end
+
+ # Returns `self` converted to an unsigned value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ #
+ # ```
+ # 1_i32.to_unsigned! # => 1_u32
+ # (-6_i16).to_unsigned! # => 65530_u16
+ # 3_u64.to_unsigned! # => 3_u64
+ # ```
+ def to_unsigned! : UInt32
+ self
+ end
+
def abs : self
self
end
@@ -1952,6 +2400,62 @@ struct UInt64
0_u64 &- self
end
+ # Returns `self` converted to a signed value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_u32.to_signed # => 1_i32
+ # 2_u16.to_signed # => 2_i16
+ # 3_i64.to_signed # => 3_i64
+ # ```
+ def to_signed : Int64
+ to_i64
+ end
+
+ # Returns `self` converted to a signed value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ #
+ # ```
+ # 1_u32.to_signed! # => 1_i32
+ # 65530_u16.to_signed! # => -6_i16
+ # 3_i64.to_signed! # => 3_i64
+ # ```
+ def to_signed! : Int64
+ to_i64!
+ end
+
+ # Returns `self` converted to an unsigned value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_i32.to_unsigned # => 1_u32
+ # 2_i16.to_unsigned # => 2_u16
+ # 3_u64.to_unsigned # => 3_u64
+ # ```
+ def to_unsigned : UInt64
+ self
+ end
+
+ # Returns `self` converted to an unsigned value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ #
+ # ```
+ # 1_i32.to_unsigned! # => 1_u32
+ # (-6_i16).to_unsigned! # => 65530_u16
+ # 3_u64.to_unsigned! # => 3_u64
+ # ```
+ def to_unsigned! : UInt64
+ self
+ end
+
def abs : self
self
end
@@ -2088,6 +2592,62 @@ struct UInt128
UInt128.new(0) &- self
end
+ # Returns `self` converted to a signed value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_u32.to_signed # => 1_i32
+ # 2_u16.to_signed # => 2_i16
+ # 3_i64.to_signed # => 3_i64
+ # ```
+ def to_signed : Int128
+ to_i128
+ end
+
+ # Returns `self` converted to a signed value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Signed`.
+ #
+ # ```
+ # 1_u32.to_signed! # => 1_i32
+ # 65530_u16.to_signed! # => -6_i16
+ # 3_i64.to_signed! # => 3_i64
+ # ```
+ def to_signed! : Int128
+ to_i128!
+ end
+
+ # Returns `self` converted to an unsigned value of the same size.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ # Raises `OverflowError` in case of overflow.
+ #
+ # ```
+ # 1_i32.to_unsigned # => 1_u32
+ # 2_i16.to_unsigned # => 2_u16
+ # 3_u64.to_unsigned # => 3_u64
+ # ```
+ def to_unsigned : UInt128
+ self
+ end
+
+ # Returns `self` converted to an unsigned value of the same size, wrapping in
+ # case of overflow.
+ #
+ # Simply returns `self` unmodified if `self` is already an `Int::Unsigned`.
+ #
+ # ```
+ # 1_i32.to_unsigned! # => 1_u32
+ # (-6_i16).to_unsigned! # => 65530_u16
+ # 3_u64.to_unsigned! # => 3_u64
+ # ```
+ def to_unsigned! : UInt128
+ self
+ end
+
def abs
self
end
diff --git a/src/kernel.cr b/src/kernel.cr
index 7bca29cef605..c3b3106ccae3 100644
--- a/src/kernel.cr
+++ b/src/kernel.cr
@@ -95,6 +95,13 @@ ARGV = Array.new(ARGC_UNSAFE - 1) { |i| String.new(ARGV_UNSAFE[1 + i]) }
# ```
ARGF = IO::ARGF.new(ARGV, STDIN)
+# The newline constant
+EOL = {% if flag?(:windows) %}
+ "\r\n"
+ {% else %}
+ "\n"
+ {% end %}
+
# Repeatedly executes the block.
#
# ```
diff --git a/src/llvm/ext/llvm_ext.cc b/src/llvm/ext/llvm_ext.cc
index ff74958d75b9..0721394e85a1 100644
--- a/src/llvm/ext/llvm_ext.cc
+++ b/src/llvm/ext/llvm_ext.cc
@@ -1,16 +1,14 @@
#include
+#include
+#include
#define LLVM_VERSION_GE(major, minor) \
(LLVM_VERSION_MAJOR > (major) || LLVM_VERSION_MAJOR == (major) && LLVM_VERSION_MINOR >= (minor))
#include
-#include
-#include
using namespace llvm;
-#include
-
#if LLVM_VERSION_GE(16, 0)
#define makeArrayRef ArrayRef
#endif
@@ -66,75 +64,4 @@ void LLVMExtTargetMachineEnableGlobalIsel(LLVMTargetMachineRef T, LLVMBool Enabl
unwrap(T)->setGlobalISel(Enable);
}
-// Copy paste of https://github.com/llvm/llvm-project/blob/dace8224f38a31636a02fe9c2af742222831f70c/llvm/lib/ExecutionEngine/ExecutionEngineBindings.cpp#L160-L214
-// but with a parameter to set global isel state
-LLVMBool LLVMExtCreateMCJITCompilerForModule(
- LLVMExecutionEngineRef *OutJIT, LLVMModuleRef M,
- LLVMMCJITCompilerOptions *PassedOptions, size_t SizeOfPassedOptions,
- LLVMBool EnableGlobalISel,
- char **OutError) {
- LLVMMCJITCompilerOptions options;
- // If the user passed a larger sized options struct, then they were compiled
- // against a newer LLVM. Tell them that something is wrong.
- if (SizeOfPassedOptions > sizeof(options)) {
- *OutError = strdup(
- "Refusing to use options struct that is larger than my own; assuming "
- "LLVM library mismatch.");
- return 1;
- }
-
-
- // Defend against the user having an old version of the API by ensuring that
- // any fields they didn't see are cleared. We must defend against fields being
- // set to the bitwise equivalent of zero, and assume that this means "do the
- // default" as if that option hadn't been available.
- LLVMInitializeMCJITCompilerOptions(&options, sizeof(options));
- memcpy(&options, PassedOptions, SizeOfPassedOptions);
-
-
- TargetOptions targetOptions;
- targetOptions.EnableFastISel = options.EnableFastISel;
- targetOptions.EnableGlobalISel = EnableGlobalISel;
- std::unique_ptr Mod(unwrap(M));
-
- if (Mod)
- // Set function attribute "frame-pointer" based on
- // NoFramePointerElim.
- for (auto &F : *Mod) {
- auto Attrs = F.getAttributes();
- StringRef Value = options.NoFramePointerElim ? "all" : "none";
- #if LLVM_VERSION_GE(14, 0)
- Attrs = Attrs.addFnAttribute(F.getContext(), "frame-pointer", Value);
- #else
- Attrs = Attrs.addAttribute(F.getContext(), AttributeList::FunctionIndex,
- "frame-pointer", Value);
- #endif
- F.setAttributes(Attrs);
- }
-
-
- std::string Error;
- EngineBuilder builder(std::move(Mod));
- builder.setEngineKind(EngineKind::JIT)
- .setErrorStr(&Error)
- .setOptLevel((CodeGenOpt::Level)options.OptLevel)
- .setTargetOptions(targetOptions);
- bool JIT;
- if (auto CM = unwrap(options.CodeModel, JIT))
- builder.setCodeModel(*CM);
- if (options.MCJMM)
- builder.setMCJITMemoryManager(
- std::unique_ptr(unwrap(options.MCJMM)));
-
- TargetMachine* tm = builder.selectTarget();
- tm->setGlobalISel(EnableGlobalISel);
-
- if (ExecutionEngine *JIT = builder.create(tm)) {
- *OutJIT = wrap(JIT);
- return 0;
- }
- *OutError = strdup(Error.c_str());
- return 1;
-}
-
} // extern "C"
diff --git a/src/llvm/jit_compiler.cr b/src/llvm/jit_compiler.cr
index ee0c92803102..3481212e1b93 100644
--- a/src/llvm/jit_compiler.cr
+++ b/src/llvm/jit_compiler.cr
@@ -5,10 +5,18 @@ class LLVM::JITCompiler
mod.take_ownership { raise "Can't create two JIT compilers for the same module" }
# if LibLLVM.create_jit_compiler_for_module(out @unwrap, mod, 3, out error) != 0
- if LibLLVMExt.create_mc_jit_compiler_for_module(out @unwrap, mod, nil, 0, false, out error) != 0
+ if LibLLVM.create_mc_jit_compiler_for_module(out @unwrap, mod, nil, 0, out error) != 0
raise LLVM.string_and_dispose(error)
end
+ # FIXME: We need to disable global isel until https://reviews.llvm.org/D80898 is released,
+ # or we fixed generating values for 0 sized types.
+ # When removing this, also remove it from the ABI specs and Crystal::Codegen::Target.
+ # See https://github.com/crystal-lang/crystal/issues/9297#issuecomment-636512270
+ # for background info
+ target_machine = LibLLVM.get_execution_engine_target_machine(@unwrap)
+ LibLLVMExt.target_machine_enable_global_isel(target_machine, false)
+
@finalized = false
end
diff --git a/src/llvm/lib_llvm/execution_engine.cr b/src/llvm/lib_llvm/execution_engine.cr
index 1c58b5fcd046..f9de5c10ea39 100644
--- a/src/llvm/lib_llvm/execution_engine.cr
+++ b/src/llvm/lib_llvm/execution_engine.cr
@@ -28,5 +28,6 @@ lib LibLLVM
fun create_mc_jit_compiler_for_module = LLVMCreateMCJITCompilerForModule(out_jit : ExecutionEngineRef*, m : ModuleRef, options : MCJITCompilerOptions*, size_of_options : SizeT, out_error : Char**) : Bool
fun dispose_execution_engine = LLVMDisposeExecutionEngine(ee : ExecutionEngineRef)
fun run_function = LLVMRunFunction(ee : ExecutionEngineRef, f : ValueRef, num_args : UInt, args : GenericValueRef*) : GenericValueRef
+ fun get_execution_engine_target_machine = LLVMGetExecutionEngineTargetMachine(ee : ExecutionEngineRef) : TargetMachineRef
fun get_pointer_to_global = LLVMGetPointerToGlobal(ee : ExecutionEngineRef, global : ValueRef) : Void*
end
diff --git a/src/llvm/lib_llvm_ext.cr b/src/llvm/lib_llvm_ext.cr
index c1a097fd4649..4efb572b6eed 100644
--- a/src/llvm/lib_llvm_ext.cr
+++ b/src/llvm/lib_llvm_ext.cr
@@ -32,5 +32,4 @@ lib LibLLVMExt
name : LibC::Char*) : LibLLVM::ValueRef
fun target_machine_enable_global_isel = LLVMExtTargetMachineEnableGlobalIsel(machine : LibLLVM::TargetMachineRef, enable : Bool)
- fun create_mc_jit_compiler_for_module = LLVMExtCreateMCJITCompilerForModule(jit : LibLLVM::ExecutionEngineRef*, m : LibLLVM::ModuleRef, options : LibLLVM::MCJITCompilerOptions*, options_length : UInt32, enable_global_isel : Bool, error : UInt8**) : Int32
end