From 1dab1dcf0393bfe6576706baef6e7e6fe94732fc Mon Sep 17 00:00:00 2001 From: David Keller Date: Sun, 12 Dec 2021 16:20:33 +0100 Subject: [PATCH 1/9] Add codegen int128 support --- src/compiler/crystal/codegen/const.cr | 22 +++++++------- src/compiler/crystal/codegen/types.cr | 2 +- src/compiler/crystal/semantic/ast.cr | 2 +- .../crystal/semantic/math_interpreter.cr | 29 ++++++++++--------- src/compiler/crystal/syntax/ast.cr | 18 +++++++----- 5 files changed, 40 insertions(+), 33 deletions(-) diff --git a/src/compiler/crystal/codegen/const.cr b/src/compiler/crystal/codegen/const.cr index 83c4a934e4e7..d042ef6632ea 100644 --- a/src/compiler/crystal/codegen/const.cr +++ b/src/compiler/crystal/codegen/const.cr @@ -194,16 +194,18 @@ class Crystal::CodeGenVisitor # We inline constants. Otherwise we use an LLVM const global. @last = case value = const.compile_time_value - when Bool then int1(value ? 1 : 0) - when Char then int32(value.ord) - when Int8 then int8(value) - when Int16 then int16(value) - when Int32 then int32(value) - when Int64 then int64(value) - when UInt8 then int8(value) - when UInt16 then int16(value) - when UInt32 then int32(value) - when UInt64 then int64(value) + when Bool then int1(value ? 1 : 0) + when Char then int32(value.ord) + when Int8 then int8(value) + when Int16 then int16(value) + when Int32 then int32(value) + when Int64 then int64(value) + when Int128 then int128(value) + when UInt8 then int8(value) + when UInt16 then int16(value) + when UInt32 then int32(value) + when UInt64 then int64(value) + when UInt128 then int128(value) else set_current_debug_location node if @debug.line_numbers? last = read_const_pointer(const) diff --git a/src/compiler/crystal/codegen/types.cr b/src/compiler/crystal/codegen/types.cr index 95b7223f3c52..e2d613ac3436 100644 --- a/src/compiler/crystal/codegen/types.cr +++ b/src/compiler/crystal/codegen/types.cr @@ -196,7 +196,7 @@ module Crystal !(initializer || no_init_flag? || simple?) end - @compile_time_value : (Int16 | Int32 | Int64 | Int8 | UInt16 | UInt32 | UInt64 | UInt8 | Bool | Char | Nil) + @compile_time_value : (Int128 | Int16 | Int32 | Int64 | Int8 | UInt128 | UInt16 | UInt32 | UInt64 | UInt8 | Bool | Char | Nil) @computed_compile_time_value = false # Returns a value if this constant's value can be evaluated at diff --git a/src/compiler/crystal/semantic/ast.cr b/src/compiler/crystal/semantic/ast.cr index 5fdd23f581f7..26825e9bc900 100644 --- a/src/compiler/crystal/semantic/ast.cr +++ b/src/compiler/crystal/semantic/ast.cr @@ -41,7 +41,7 @@ module Crystal case self_type = self.type? when IntegerType case self_type.kind - when :i8, :u8, :i16, :u16, :i32, :u32, :i64, :u64 + when :i8, :u8, :i16, :u16, :i32, :u32, :i64, :u64, :i128, :u128 true else false diff --git a/src/compiler/crystal/semantic/math_interpreter.cr b/src/compiler/crystal/semantic/math_interpreter.cr index f4501c70fef7..48ac29bfd47e 100644 --- a/src/compiler/crystal/semantic/math_interpreter.cr +++ b/src/compiler/crystal/semantic/math_interpreter.cr @@ -8,17 +8,19 @@ struct Crystal::MathInterpreter def interpret(node : NumberLiteral) case node.kind - when :i8, :i16, :i32, :i64, :u8, :u16, :u32, :u64 + when :i8, :i16, :i32, :i64, :i128, :u8, :u16, :u32, :u64, :u128 target_kind = @target_type.try(&.kind) || node.kind case target_kind - when :i8 then node.value.to_i8? || node.raise "invalid Int8: #{node.value}" - when :u8 then node.value.to_u8? || node.raise "invalid UInt8: #{node.value}" - when :i16 then node.value.to_i16? || node.raise "invalid Int16: #{node.value}" - when :u16 then node.value.to_u16? || node.raise "invalid UInt16: #{node.value}" - when :i32 then node.value.to_i32? || node.raise "invalid Int32: #{node.value}" - when :u32 then node.value.to_u32? || node.raise "invalid UInt32: #{node.value}" - when :i64 then node.value.to_i64? || node.raise "invalid Int64: #{node.value}" - when :u64 then node.value.to_u64? || node.raise "invalid UInt64: #{node.value}" + when :i8 then node.value.to_i8? || node.raise "invalid Int8: #{node.value}" + when :u8 then node.value.to_u8? || node.raise "invalid UInt8: #{node.value}" + when :i16 then node.value.to_i16? || node.raise "invalid Int16: #{node.value}" + when :u16 then node.value.to_u16? || node.raise "invalid UInt16: #{node.value}" + when :i32 then node.value.to_i32? || node.raise "invalid Int32: #{node.value}" + when :u32 then node.value.to_u32? || node.raise "invalid UInt32: #{node.value}" + when :i64 then node.value.to_i64? || node.raise "invalid Int64: #{node.value}" + when :u64 then node.value.to_u64? || node.raise "invalid UInt64: #{node.value}" + when :i128 then node.value.to_i128? || node.raise "invalid Int128: #{node.value}" + when :u128 then node.value.to_u128? || node.raise "invalid UInt128: #{node.value}" else node.raise "enum type must be an integer, not #{target_kind}" end @@ -43,10 +45,11 @@ struct Crystal::MathInterpreter when "+" then +left when "-" case left - when Int8 then -left - when Int16 then -left - when Int32 then -left - when Int64 then -left + when Int8 then -left + when Int16 then -left + when Int32 then -left + when Int64 then -left + when Int128 then -left else interpret_call_macro(node) end diff --git a/src/compiler/crystal/syntax/ast.cr b/src/compiler/crystal/syntax/ast.cr index 4b01d5c14506..a32303d976e9 100644 --- a/src/compiler/crystal/syntax/ast.cr +++ b/src/compiler/crystal/syntax/ast.cr @@ -249,14 +249,16 @@ module Crystal def integer_value case kind - when :i8 then value.to_i8 - when :i16 then value.to_i16 - when :i32 then value.to_i32 - when :i64 then value.to_i64 - when :u8 then value.to_u8 - when :u16 then value.to_u16 - when :u32 then value.to_u32 - when :u64 then value.to_u64 + when :i8 then value.to_i8 + when :i16 then value.to_i16 + when :i32 then value.to_i32 + when :i64 then value.to_i64 + when :i128 then value.to_i128 + when :u8 then value.to_u8 + when :u16 then value.to_u16 + when :u32 then value.to_u32 + when :u64 then value.to_u64 + when :u128 then value.to_u128 else raise "Bug: called 'integer_value' for non-integer literal" end From 637844f77ac448bf7730c49a4ab687bd2aaa1660 Mon Sep 17 00:00:00 2001 From: David Keller Date: Sun, 12 Dec 2021 17:09:11 +0100 Subject: [PATCH 2/9] Allow U/Int128 math in macros --- src/compiler/crystal/macros/methods.cr | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/compiler/crystal/macros/methods.cr b/src/compiler/crystal/macros/methods.cr index 95c04ccbc513..050d9478dba5 100644 --- a/src/compiler/crystal/macros/methods.cr +++ b/src/compiler/crystal/macros/methods.cr @@ -530,10 +530,12 @@ module Crystal when :i16 then @value.to_i16 when :i32 then @value.to_i32 when :i64 then @value.to_i64 + when :i128 then @value.to_i128 when :u8 then @value.to_u8 when :u16 then @value.to_u16 when :u32 then @value.to_u32 when :u64 then @value.to_u64 + when :u128 then @value.to_u128 when :f32 then @value.to_f32 when :f64 then @value.to_f64 else From 1288baaf347eb2406d1905f2d509aa3dcd83d5b4 Mon Sep 17 00:00:00 2001 From: David Keller Date: Sun, 12 Dec 2021 17:12:10 +0100 Subject: [PATCH 3/9] Remove u/i128 from ASTNode#supports_autocast? (no type to cast to) --- src/compiler/crystal/semantic/ast.cr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/crystal/semantic/ast.cr b/src/compiler/crystal/semantic/ast.cr index 26825e9bc900..5fdd23f581f7 100644 --- a/src/compiler/crystal/semantic/ast.cr +++ b/src/compiler/crystal/semantic/ast.cr @@ -41,7 +41,7 @@ module Crystal case self_type = self.type? when IntegerType case self_type.kind - when :i8, :u8, :i16, :u16, :i32, :u32, :i64, :u64, :i128, :u128 + when :i8, :u8, :i16, :u16, :i32, :u32, :i64, :u64 true else false From 2658f057d810274b7a05fc904101b9757fb26f93 Mon Sep 17 00:00:00 2001 From: David Keller Date: Sun, 12 Dec 2021 18:01:15 +0100 Subject: [PATCH 4/9] Add int128 support for Crystal::Program#int? --- src/compiler/crystal/program.cr | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/compiler/crystal/program.cr b/src/compiler/crystal/program.cr index 722501add55e..752881f7d910 100644 --- a/src/compiler/crystal/program.cr +++ b/src/compiler/crystal/program.cr @@ -516,14 +516,16 @@ module Crystal # Returns the `IntegerType` that matches the given Int value def int?(int) case int - when Int8 then int8 - when Int16 then int16 - when Int32 then int32 - when Int64 then int64 - when UInt8 then uint8 - when UInt16 then uint16 - when UInt32 then uint32 - when UInt64 then uint64 + when Int8 then int8 + when Int16 then int16 + when Int32 then int32 + when Int64 then int64 + when Int128 then int128 + when UInt8 then uint8 + when UInt16 then uint16 + when UInt32 then uint32 + when UInt64 then uint64 + when UInt128 then uint128 else nil end From 29ffcffb8749f33729acae1f97fed661ce1f0ff4 Mon Sep 17 00:00:00 2001 From: David Keller Date: Sun, 12 Dec 2021 18:09:01 +0100 Subject: [PATCH 5/9] Format code --- src/compiler/crystal/macros/methods.cr | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/compiler/crystal/macros/methods.cr b/src/compiler/crystal/macros/methods.cr index 050d9478dba5..05fa3df5825e 100644 --- a/src/compiler/crystal/macros/methods.cr +++ b/src/compiler/crystal/macros/methods.cr @@ -526,18 +526,18 @@ module Crystal def to_number case @kind - when :i8 then @value.to_i8 - when :i16 then @value.to_i16 - when :i32 then @value.to_i32 - when :i64 then @value.to_i64 + when :i8 then @value.to_i8 + when :i16 then @value.to_i16 + when :i32 then @value.to_i32 + when :i64 then @value.to_i64 when :i128 then @value.to_i128 - when :u8 then @value.to_u8 - when :u16 then @value.to_u16 - when :u32 then @value.to_u32 - when :u64 then @value.to_u64 + when :u8 then @value.to_u8 + when :u16 then @value.to_u16 + when :u32 then @value.to_u32 + when :u64 then @value.to_u64 when :u128 then @value.to_u128 - when :f32 then @value.to_f32 - when :f64 then @value.to_f64 + when :f32 then @value.to_f32 + when :f64 then @value.to_f64 else raise "Unknown kind: #{@kind}" end From 47e0e9e817197b8c234ce4d29f77614c879023ed Mon Sep 17 00:00:00 2001 From: David Keller Date: Sun, 12 Dec 2021 19:40:20 +0100 Subject: [PATCH 6/9] Add specs --- spec/compiler/codegen/enum_spec.cr | 23 ++++++++++++++++++++ spec/compiler/codegen/generic_class_spec.cr | 21 ++++++++++++++++++ spec/compiler/macro/macro_methods_spec.cr | 20 +++++++++++++++++ spec/compiler/semantic/generic_class_spec.cr | 13 +++++++++++ 4 files changed, 77 insertions(+) diff --git a/spec/compiler/codegen/enum_spec.cr b/spec/compiler/codegen/enum_spec.cr index 2d30fdd9e5a3..dff430a5483b 100644 --- a/spec/compiler/codegen/enum_spec.cr +++ b/spec/compiler/codegen/enum_spec.cr @@ -348,4 +348,27 @@ describe "Code gen: enum" do Foo::V33.value )).to_u64.should eq(1_u64 << 32) end + + it "can define flags enum : UInt128 with 128 values" do + run(%( + @[Flags] + enum Foo : UInt128 + #{Array.new(128) { |i| "V#{i + 1}" }.join "\n"} + end + + Foo::V64.value.to_u64! + )).to_u64.should eq(1_u64 << 63) + end + + it "can define flags enum : UInt128 with compile-time interpreted values" do + run(%( + enum Foo : UInt128 + A = 1_u128 << 6 + B = 1_u128 << 20 + C = 1_u128 << 60 + end + + Foo::A.value.to_u64! + )).to_u64.should eq(1 << 6) + end end diff --git a/spec/compiler/codegen/generic_class_spec.cr b/spec/compiler/codegen/generic_class_spec.cr index 49baf0b62d33..c203a4c6a864 100644 --- a/spec/compiler/codegen/generic_class_spec.cr +++ b/spec/compiler/codegen/generic_class_spec.cr @@ -488,4 +488,25 @@ describe "Code gen: generic class type" do Baz.new )) end + + it "codegens compile-time interpreted generic int128" do + run(%( + require "prelude" + + CONST = 1_i128 + 2_i128 + class Foo(T) + def initialize() + end + + def t_incr + T + 1 + end + end + + class Bar < Foo(CONST) + end + + Bar.new.t_incr + )).to_i.should eq(4) + end end diff --git a/spec/compiler/macro/macro_methods_spec.cr b/spec/compiler/macro/macro_methods_spec.cr index e132ff75acea..d4a909e8c100 100644 --- a/spec/compiler/macro/macro_methods_spec.cr +++ b/spec/compiler/macro/macro_methods_spec.cr @@ -265,6 +265,7 @@ module Crystal it "executes unary -" do assert_macro "{{-(3)}}", "-3" + assert_macro "{{-(3_i128)}}", "-3_i128" end it "executes unary ~" do @@ -276,12 +277,24 @@ module Crystal assert_macro "{{1e-123_f32.kind}}", ":f32" assert_macro "{{1.0.kind}}", ":f64" assert_macro "{{0xde7ec7ab1e_u64.kind}}", ":u64" + assert_macro "{{1_u128.kind}}", ":u128" + assert_macro "{{-20i128.kind}}", ":i128" end it "#to_number" do assert_macro "{{ 4_u8.to_number }}", "4" assert_macro "{{ 2147483648.to_number }}", "2147483648" assert_macro "{{ 1_f32.to_number }}", "1.0" + assert_macro "{{ 4_u128.to_number }}", "4" + assert_macro "{{ -20i128.to_number }}", "-20" + end + + # Requires #11571 + pending "executes math operations using U/Int128" do + assert_macro "{{18446744073709551615_u128 + 1}}", "18446744073709551616" + assert_macro "{{18446744073709551_i128 - 1_u128}}", "18446744073709550" + assert_macro "{{18446744073709551615_u128 * 10}}", "184467440737095516150" + assert_macro "{{18446744073709551610_u128 // 10}}", "1844674407370955161" end end @@ -3029,4 +3042,11 @@ module Crystal assert_macro %({{ (x.conds << "a"; x.conds.size) }}), "2", {x: node} end end + + describe "int128 support" do + it "" do + node = When.new([2.int32, 3.int32] of ASTNode, 4.int32) + assert_macro %({{ (x.conds << "a"; x.conds.size) }}), "2", {x: node} + end + end end diff --git a/spec/compiler/semantic/generic_class_spec.cr b/spec/compiler/semantic/generic_class_spec.cr index be29a944125d..9933abfbb875 100644 --- a/spec/compiler/semantic/generic_class_spec.cr +++ b/spec/compiler/semantic/generic_class_spec.cr @@ -1148,6 +1148,19 @@ describe "Semantic: generic class" do )) { generic_class "Foo", 1.int32 } end + it "can use type var that resolves to number in restriction using Int128" do + assert_type(%( + class Foo(N) + def foo : Foo(N) + self + end + end + + f = Foo(1_i128).new + f.foo + )) { generic_class "Foo", 1.int128 } + end + it "doesn't consider unbound generic instantiations as concrete (#7200)" do assert_type(%( module Moo From de7b4b8601ad786c6131500da42f3c1cffef5f31 Mon Sep 17 00:00:00 2001 From: David Keller Date: Wed, 15 Dec 2021 11:45:46 +0100 Subject: [PATCH 7/9] Remove unnecessary spec --- spec/compiler/macro/macro_methods_spec.cr | 7 ------- 1 file changed, 7 deletions(-) diff --git a/spec/compiler/macro/macro_methods_spec.cr b/spec/compiler/macro/macro_methods_spec.cr index d4a909e8c100..c76bc193e404 100644 --- a/spec/compiler/macro/macro_methods_spec.cr +++ b/spec/compiler/macro/macro_methods_spec.cr @@ -3042,11 +3042,4 @@ module Crystal assert_macro %({{ (x.conds << "a"; x.conds.size) }}), "2", {x: node} end end - - describe "int128 support" do - it "" do - node = When.new([2.int32, 3.int32] of ASTNode, 4.int32) - assert_macro %({{ (x.conds << "a"; x.conds.size) }}), "2", {x: node} - end - end end From c813d0550f68ea0f7ccdab0ff5d7fb1f7406cadc Mon Sep 17 00:00:00 2001 From: David Keller Date: Sat, 18 Dec 2021 22:40:57 +0100 Subject: [PATCH 8/9] Enable pending spec --- spec/compiler/macro/macro_methods_spec.cr | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/compiler/macro/macro_methods_spec.cr b/spec/compiler/macro/macro_methods_spec.cr index c76bc193e404..76b0c106d579 100644 --- a/spec/compiler/macro/macro_methods_spec.cr +++ b/spec/compiler/macro/macro_methods_spec.cr @@ -289,8 +289,7 @@ module Crystal assert_macro "{{ -20i128.to_number }}", "-20" end - # Requires #11571 - pending "executes math operations using U/Int128" do + it "executes math operations using U/Int128" do assert_macro "{{18446744073709551615_u128 + 1}}", "18446744073709551616" assert_macro "{{18446744073709551_i128 - 1_u128}}", "18446744073709550" assert_macro "{{18446744073709551615_u128 * 10}}", "184467440737095516150" From db34567918560eaec80885ee88f97bb2cd0f1ef4 Mon Sep 17 00:00:00 2001 From: David Keller Date: Sun, 19 Dec 2021 18:51:58 +0100 Subject: [PATCH 9/9] Assert proper types in macro spec --- spec/compiler/macro/macro_methods_spec.cr | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/compiler/macro/macro_methods_spec.cr b/spec/compiler/macro/macro_methods_spec.cr index 76b0c106d579..7d69cca4c153 100644 --- a/spec/compiler/macro/macro_methods_spec.cr +++ b/spec/compiler/macro/macro_methods_spec.cr @@ -290,10 +290,10 @@ module Crystal end it "executes math operations using U/Int128" do - assert_macro "{{18446744073709551615_u128 + 1}}", "18446744073709551616" - assert_macro "{{18446744073709551_i128 - 1_u128}}", "18446744073709550" - assert_macro "{{18446744073709551615_u128 * 10}}", "184467440737095516150" - assert_macro "{{18446744073709551610_u128 // 10}}", "1844674407370955161" + assert_macro "{{18446744073709551615_u128 + 1}}", "18446744073709551616_u128" + assert_macro "{{18446744073709551_i128 - 1_u128}}", "18446744073709550_i128" + assert_macro "{{18446744073709551615_u128 * 10}}", "184467440737095516150_u128" + assert_macro "{{18446744073709551610_u128 // 10}}", "1844674407370955161_u128" end end