From 0e99a5590a1c6047f8dd1985663bae6e606b4fe2 Mon Sep 17 00:00:00 2001 From: David Keller Date: Sat, 29 Jan 2022 22:05:45 +0100 Subject: [PATCH] Add support for `Int128` in codegen and macros (#11576) --- spec/compiler/codegen/enum_spec.cr | 23 +++++++++++++++ spec/compiler/codegen/generic_class_spec.cr | 21 ++++++++++++++ spec/compiler/macro/macro_methods_spec.cr | 12 ++++++++ spec/compiler/semantic/generic_class_spec.cr | 13 +++++++++ src/compiler/crystal/codegen/const.cr | 22 +++++++------- src/compiler/crystal/codegen/types.cr | 2 +- src/compiler/crystal/macros/methods.cr | 22 +++++++------- src/compiler/crystal/program.cr | 18 +++++++----- .../crystal/semantic/math_interpreter.cr | 29 ++++++++++--------- src/compiler/crystal/syntax/ast.cr | 18 +++++++----- 10 files changed, 130 insertions(+), 50 deletions(-) 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 c1f0fb76194c..fbf0e783de46 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,23 @@ 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 + + it "executes math operations using U/Int128" do + 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 diff --git a/spec/compiler/semantic/generic_class_spec.cr b/spec/compiler/semantic/generic_class_spec.cr index 305b24e53419..bcc06e9654a9 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 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 356cecbd50cc..df81ea966471 100644 --- a/src/compiler/crystal/codegen/types.cr +++ b/src/compiler/crystal/codegen/types.cr @@ -202,7 +202,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/macros/methods.cr b/src/compiler/crystal/macros/methods.cr index 262a72bef4f2..e63356385e5c 100644 --- a/src/compiler/crystal/macros/methods.cr +++ b/src/compiler/crystal/macros/methods.cr @@ -551,16 +551,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 :u8 then @value.to_u8 - when :u16 then @value.to_u16 - when :u32 then @value.to_u32 - when :u64 then @value.to_u64 - when :f32 then @value.to_f32 - when :f64 then @value.to_f64 + 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 + when :f32 then @value.to_f32 + when :f64 then @value.to_f64 else raise "Unknown kind: #{@kind}" end diff --git a/src/compiler/crystal/program.cr b/src/compiler/crystal/program.cr index 66a2cd4c5c9d..9b8af42a74e5 100644 --- a/src/compiler/crystal/program.cr +++ b/src/compiler/crystal/program.cr @@ -519,14 +519,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 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 bc40937ae206..d6924a0e6727 100644 --- a/src/compiler/crystal/syntax/ast.cr +++ b/src/compiler/crystal/syntax/ast.cr @@ -255,14 +255,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