Skip to content

Commit

Permalink
Add support for Int128 in codegen and macros (#11576)
Browse files Browse the repository at this point in the history
  • Loading branch information
BlobCodes authored Jan 29, 2022
1 parent 8d3155e commit 0e99a55
Show file tree
Hide file tree
Showing 10 changed files with 130 additions and 50 deletions.
23 changes: 23 additions & 0 deletions spec/compiler/codegen/enum_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
21 changes: 21 additions & 0 deletions spec/compiler/codegen/generic_class_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
12 changes: 12 additions & 0 deletions spec/compiler/macro/macro_methods_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down
13 changes: 13 additions & 0 deletions spec/compiler/semantic/generic_class_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 12 additions & 10 deletions src/compiler/crystal/codegen/const.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion src/compiler/crystal/codegen/types.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 12 additions & 10 deletions src/compiler/crystal/macros/methods.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 10 additions & 8 deletions src/compiler/crystal/program.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 16 additions & 13 deletions src/compiler/crystal/semantic/math_interpreter.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
18 changes: 10 additions & 8 deletions src/compiler/crystal/syntax/ast.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 0e99a55

Please sign in to comment.