From 05cb10f0f355a48e5e4e803870f39aeb2c354cdd Mon Sep 17 00:00:00 2001 From: bishabosha Date: Tue, 25 Feb 2020 01:17:05 +0100 Subject: [PATCH 1/2] add bitwise compiletime operations --- .../dotty/tools/dotc/core/Definitions.scala | 1 + .../src/dotty/tools/dotc/core/Types.scala | 6 ++ .../src/scala/compiletime/ops/package.scala | 47 ++++++++++- tests/neg/singleton-ops-int.scala | 30 +++++++ tests/pos/singleton-ops-composition.scala | 3 +- tests/run/singleton-ops-flags.scala | 84 +++++++++++++++++++ 6 files changed, 168 insertions(+), 3 deletions(-) create mode 100644 tests/run/singleton-ops-flags.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 3873ee2e6249..9e16bd62641a 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -955,6 +955,7 @@ class Definitions { tpnme.Plus, tpnme.Minus, tpnme.Times, tpnme.Div, tpnme.Mod, tpnme.Lt, tpnme.Gt, tpnme.Ge, tpnme.Le, tpnme.Abs, tpnme.Negate, tpnme.Min, tpnme.Max, tpnme.ToString, + tpnme.Xor, tpnme.AND, tpnme.OR, tpnme.ASR, tpnme.LSL, tpnme.LSR ) private val compiletimePackageBooleanTypes: Set[Name] = Set(tpnme.Not, tpnme.Xor, tpnme.And, tpnme.Or) private val compiletimePackageStringTypes: Set[Name] = Set(tpnme.Plus) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 1f23faa69fd5..16c8c5e680b6 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3842,6 +3842,12 @@ object Types { case tpnme.Gt if nArgs == 2 => constantFold2(intValue, _ > _) case tpnme.Ge if nArgs == 2 => constantFold2(intValue, _ >= _) case tpnme.Le if nArgs == 2 => constantFold2(intValue, _ <= _) + case tpnme.Xor if nArgs == 2 => constantFold2(intValue, _ ^ _) + case tpnme.AND if nArgs == 2 => constantFold2(intValue, _ & _) + case tpnme.OR if nArgs == 2 => constantFold2(intValue, _ | _) + case tpnme.ASR if nArgs == 2 => constantFold2(intValue, _ >> _) + case tpnme.LSL if nArgs == 2 => constantFold2(intValue, _ << _) + case tpnme.LSR if nArgs == 2 => constantFold2(intValue, _ >>> _) case tpnme.Min if nArgs == 2 => constantFold2(intValue, _ min _) case tpnme.Max if nArgs == 2 => constantFold2(intValue, _ max _) case _ => None diff --git a/library/src/scala/compiletime/ops/package.scala b/library/src/scala/compiletime/ops/package.scala index d7714f63e487..8aeea59853a2 100644 --- a/library/src/scala/compiletime/ops/package.scala +++ b/library/src/scala/compiletime/ops/package.scala @@ -62,12 +62,55 @@ package object ops { @infix type /[X <: Int, Y <: Int] <: Int /** Remainder of the division of `X` by `Y`. - * ```scala + * ```scala * val mod: 5 % 2 = 1 * ``` */ @infix type %[X <: Int, Y <: Int] <: Int + /** Binary left shift of `X` by `Y`. + * ```scala + * val lshift: 1 << 2 = 4 + * ``` + */ + @infix type <<[X <: Int, Y <: Int] <: Int + + /** Binary right shift of `X` by `Y`. + * ```scala + * val rshift: 10 >> 1 = 5 + * ``` + */ + @infix type >>[X <: Int, Y <: Int] <: Int + + /** Binary right shift of `X` by `Y`, filling the left with zeros. + * ```scala + * val rshiftzero: 10 >>> 1 = 5 + * ``` + */ + @infix type >>>[X <: Int, Y <: Int] <: Int + + /** Bitwise and of `X` and `Y`. + * ```scala + * val and1: 4 & 4 = 4 + * val and2: 10 & 5 = 0 + * ``` + */ + @infix type &[X <: Int, Y <: Int] <: Int + + /** Bitwise or of `X` and `Y`. + * ```scala + * val or: 10 | 11 = 11 + * ``` + */ + @infix type |[X <: Int, Y <: Int] <: Int + + /** Bitwise xor of `X` and `Y`. + * ```scala + * val xor: 10 ^ 30 = 20 + * ``` + */ + @infix type ^[X <: Int, Y <: Int] <: Int + /** Less-than comparison of two `Int` singleton types. * ```scala * val lt1: 4 < 2 = false @@ -124,7 +167,7 @@ package object ops { /** Maximum of two `Int` singleton types. * ```scala - * val abs: Abs[-1] = 1 + * val max: Max[-1, 1] = 1 * ``` */ type Max[X <: Int, Y <: Int] <: Int diff --git a/tests/neg/singleton-ops-int.scala b/tests/neg/singleton-ops-int.scala index 5f67ab1d68f1..3cbff452a4ad 100644 --- a/tests/neg/singleton-ops-int.scala +++ b/tests/neg/singleton-ops-int.scala @@ -72,4 +72,34 @@ object Test { val t49: ToString[-1] = "-1" val t50: ToString[0] = "-0" // error val t51: ToString[200] = "100" // error + + val t52: 1 ^ 2 = 3 + val t53: 1 ^ 3 = 3 // error + val t54: -1 ^ -2 = 1 + val t55: -1 ^ -3 = 1 // error + + val t56: 1 | 2 = 3 + val t57: 10 | 12 = 13 // error + val t58: -11 | 12 = -3 + val t59: -111 | -10 = 0 // error + + val t60: 1 & 1 = 1 + val t61: 1 & 2 = 0 + val t62: -1 & -3 = 3 // error + val t63: -1 & -1 = 1 // error + + val t64: 1 << 1 = 2 + val t65: 1 << 2 = 4 + val t66: 1 << 3 = 8 + val t67: 1 << 4 = 0 // error + + val t68: 100 >> 2 = 25 + val t69: 123456789 >> 71 = 964506 + val t70: -7 >> 3 = -1 + val t71: -7 >> 3 = 0 // error + + val t72: -1 >>> 10000 = 65535 + val t73: -7 >>> 3 = 536870911 + val t74: -7 >>> 3 = -1 // error + } diff --git a/tests/pos/singleton-ops-composition.scala b/tests/pos/singleton-ops-composition.scala index 86f280a61b4e..93a232efea14 100644 --- a/tests/pos/singleton-ops-composition.scala +++ b/tests/pos/singleton-ops-composition.scala @@ -1,9 +1,10 @@ import scala.compiletime.ops.boolean._ -import scala.compiletime.ops.int._ +import scala.compiletime.ops.int.{^ => ^^,_} // must rename int.^ or get clash with boolean.^ object Test { val t0: 1 + 2 * 3 = 7 val t1: (2 * 7 + 1) % 10 = 5 val t3: 1 * 1 + 2 * 2 + 3 * 3 + 4 * 4 = 30 val t4: true && false || true && true || false ^ false = true + val t5: 100 << 2 >>> 2 >> 2 ^^ 3 | (7 & 7) = 31 } diff --git a/tests/run/singleton-ops-flags.scala b/tests/run/singleton-ops-flags.scala new file mode 100644 index 000000000000..eaef14927b84 --- /dev/null +++ b/tests/run/singleton-ops-flags.scala @@ -0,0 +1,84 @@ +package example { + + import compiletime.S + import compiletime.ops.int.<< + + object TastyFlags: + + final val EmptyFlags = baseFlags + final val Erased = EmptyFlags.next + final val Internal = Erased.next + final val Inline = Internal.next + final val InlineProxy = Inline.next + final val Opaque = InlineProxy.next + final val Scala2x = Opaque.next + final val Extension = Scala2x.next + final val Given = Extension.next + final val Exported = Given.next + final val NoInits = Exported.next + final val TastyMacro = NoInits.next + final val Enum = TastyMacro.next + final val Open = Enum.next + + type LastFlag = Open.idx.type + + def (s: FlagSet).debug: String = + if s == EmptyFlags then "EmptyFlags" + else s.toSingletonSets[LastFlag].map ( [n <: Int] => (flag: SingletonFlagSet[n]) => flag match { + case Erased => "Erased" + case Internal => "Internal" + case Inline => "Inline" + case InlineProxy => "InlineProxy" + case Opaque => "Opaque" + case Scala2x => "Scala2x" + case Extension => "Extension" + case Given => "Given" + case Exported => "Exported" + case NoInits => "NoInits" + case TastyMacro => "TastyMacro" + case Enum => "Enum" + case Open => "Open" + }) mkString(" | ") + + object opaques: + + opaque type FlagSet = Int + opaque type EmptyFlagSet <: FlagSet = 0 + opaque type SingletonFlagSet[N <: Int] <: FlagSet = 1 << N + + opaque type SingletonSets[N <: Int] = Int + + private def [N <: Int](n: N).shift: 1 << N = ( 1 << n ).asInstanceOf + private def [N <: Int](n: N).succ : S[N] = ( n + 1 ).asInstanceOf + + final val baseFlags: EmptyFlagSet = 0 + + def (s: EmptyFlagSet).next: SingletonFlagSet[0] = 1 + def [N <: Int: ValueOf](s: SingletonFlagSet[N]).next: SingletonFlagSet[S[N]] = valueOf[N].succ.shift + def [N <: Int: ValueOf](s: SingletonFlagSet[N]).idx: N = valueOf[N] + def [N <: Int](s: FlagSet).toSingletonSets: SingletonSets[N] = s + def (s: FlagSet) | (t: FlagSet): FlagSet = s | t + + def [A, N <: Int: ValueOf](ss: SingletonSets[N]).map(f: [t <: Int] => (s: SingletonFlagSet[t]) => A): List[A] = + val maxFlag = valueOf[N] + val buf = List.newBuilder[A] + var current = 0 + while (current <= maxFlag) { + val flag = current.shift + if ((flag & ss) != 0) { + buf += f(flag) + } + current += 1 + } + buf.result + + end opaques + + export opaques._ + +} + + +import example.TastyFlags._ + +@main def Test = assert((Open | Given | Inline | Erased).debug == "Erased | Inline | Given | Open") From e2b0de0bf70bbde5a9a92dc7fa91b36537b02a87 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 4 Mar 2020 16:52:09 +0100 Subject: [PATCH 2/2] rename bitwise and/or compiletime int operations --- .../dotty/tools/dotc/core/Definitions.scala | 2 +- .../src/dotty/tools/dotc/core/StdNames.scala | 44 ++++++++++--------- .../src/dotty/tools/dotc/core/Types.scala | 4 +- .../src/scala/compiletime/ops/package.scala | 30 ++++++------- tests/neg/singleton-ops-int.scala | 18 ++++---- tests/pos/singleton-ops-composition.scala | 2 +- 6 files changed, 51 insertions(+), 49 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 9e16bd62641a..2093ba94b564 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -955,7 +955,7 @@ class Definitions { tpnme.Plus, tpnme.Minus, tpnme.Times, tpnme.Div, tpnme.Mod, tpnme.Lt, tpnme.Gt, tpnme.Ge, tpnme.Le, tpnme.Abs, tpnme.Negate, tpnme.Min, tpnme.Max, tpnme.ToString, - tpnme.Xor, tpnme.AND, tpnme.OR, tpnme.ASR, tpnme.LSL, tpnme.LSR + tpnme.Xor, tpnme.BitwiseAnd, tpnme.BitwiseOr, tpnme.ASR, tpnme.LSL, tpnme.LSR ) private val compiletimePackageBooleanTypes: Set[Name] = Set(tpnme.Not, tpnme.Xor, tpnme.And, tpnme.Or) private val compiletimePackageStringTypes: Set[Name] = Set(tpnme.Plus) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index ef22a6c14549..6ae10e0765bb 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -208,27 +208,29 @@ object StdNames { final val IOOBException: N = "IndexOutOfBoundsException" final val FunctionXXL: N = "FunctionXXL" - final val Abs: N = "Abs" - final val And: N = "&&" - final val Div: N = "/" - final val Equals: N = "==" - final val Ge: N = ">=" - final val Gt: N = ">" - final val Le: N = "<=" - final val Lt: N = "<" - final val Max: N = "Max" - final val Min: N = "Min" - final val Minus: N = "-" - final val Mod: N = "%" - final val Negate: N = "Negate" - final val Not: N = "!" - final val NotEquals: N = "!=" - final val Or: N = "||" - final val Plus: N = "+" - final val S: N = "S" - final val Times: N = "*" - final val ToString: N = "ToString" - final val Xor: N = "^" + final val Abs: N = "Abs" + final val And: N = "&&" + final val BitwiseAnd: N = "BitwiseAnd" + final val BitwiseOr: N = "BitwiseOr" + final val Div: N = "/" + final val Equals: N = "==" + final val Ge: N = ">=" + final val Gt: N = ">" + final val Le: N = "<=" + final val Lt: N = "<" + final val Max: N = "Max" + final val Min: N = "Min" + final val Minus: N = "-" + final val Mod: N = "%" + final val Negate: N = "Negate" + final val Not: N = "!" + final val NotEquals: N = "!=" + final val Or: N = "||" + final val Plus: N = "+" + final val S: N = "S" + final val Times: N = "*" + final val ToString: N = "ToString" + final val Xor: N = "^" final val ClassfileAnnotation: N = "ClassfileAnnotation" final val ClassManifest: N = "ClassManifest" diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 16c8c5e680b6..38492d28b5b3 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3843,8 +3843,8 @@ object Types { case tpnme.Ge if nArgs == 2 => constantFold2(intValue, _ >= _) case tpnme.Le if nArgs == 2 => constantFold2(intValue, _ <= _) case tpnme.Xor if nArgs == 2 => constantFold2(intValue, _ ^ _) - case tpnme.AND if nArgs == 2 => constantFold2(intValue, _ & _) - case tpnme.OR if nArgs == 2 => constantFold2(intValue, _ | _) + case tpnme.BitwiseAnd if nArgs == 2 => constantFold2(intValue, _ & _) + case tpnme.BitwiseOr if nArgs == 2 => constantFold2(intValue, _ | _) case tpnme.ASR if nArgs == 2 => constantFold2(intValue, _ >> _) case tpnme.LSL if nArgs == 2 => constantFold2(intValue, _ << _) case tpnme.LSR if nArgs == 2 => constantFold2(intValue, _ >>> _) diff --git a/library/src/scala/compiletime/ops/package.scala b/library/src/scala/compiletime/ops/package.scala index 8aeea59853a2..ea9fdbd942cc 100644 --- a/library/src/scala/compiletime/ops/package.scala +++ b/library/src/scala/compiletime/ops/package.scala @@ -89,21 +89,6 @@ package object ops { */ @infix type >>>[X <: Int, Y <: Int] <: Int - /** Bitwise and of `X` and `Y`. - * ```scala - * val and1: 4 & 4 = 4 - * val and2: 10 & 5 = 0 - * ``` - */ - @infix type &[X <: Int, Y <: Int] <: Int - - /** Bitwise or of `X` and `Y`. - * ```scala - * val or: 10 | 11 = 11 - * ``` - */ - @infix type |[X <: Int, Y <: Int] <: Int - /** Bitwise xor of `X` and `Y`. * ```scala * val xor: 10 ^ 30 = 20 @@ -143,6 +128,21 @@ package object ops { */ @infix type <=[X <: Int, Y <: Int] <: Boolean + /** Bitwise and of `X` and `Y`. + * ```scala + * val and1: BitwiseAnd[4, 4] = 4 + * val and2: BitwiseAnd[10, 5] = 0 + * ``` + */ + type BitwiseAnd[X <: Int, Y <: Int] <: Int + + /** Bitwise or of `X` and `Y`. + * ```scala + * val or: BitwiseOr[10, 11] = 11 + * ``` + */ + type BitwiseOr[X <: Int, Y <: Int] <: Int + /** Absolute value of an `Int` singleton type. * ```scala * val abs: Abs[-1] = 1 diff --git a/tests/neg/singleton-ops-int.scala b/tests/neg/singleton-ops-int.scala index 3cbff452a4ad..8b0288dd0ea9 100644 --- a/tests/neg/singleton-ops-int.scala +++ b/tests/neg/singleton-ops-int.scala @@ -78,15 +78,15 @@ object Test { val t54: -1 ^ -2 = 1 val t55: -1 ^ -3 = 1 // error - val t56: 1 | 2 = 3 - val t57: 10 | 12 = 13 // error - val t58: -11 | 12 = -3 - val t59: -111 | -10 = 0 // error - - val t60: 1 & 1 = 1 - val t61: 1 & 2 = 0 - val t62: -1 & -3 = 3 // error - val t63: -1 & -1 = 1 // error + val t56: BitwiseOr[1, 2] = 3 + val t57: BitwiseOr[10, 12] = 13 // error + val t58: BitwiseOr[-11, 12] = -3 + val t59: BitwiseOr[-111, -10] = 0 // error + + val t60: BitwiseAnd[1, 1] = 1 + val t61: BitwiseAnd[1, 2] = 0 + val t62: BitwiseAnd[-1, -3] = 3 // error + val t63: BitwiseAnd[-1, -1] = 1 // error val t64: 1 << 1 = 2 val t65: 1 << 2 = 4 diff --git a/tests/pos/singleton-ops-composition.scala b/tests/pos/singleton-ops-composition.scala index 93a232efea14..7cc2b93806fb 100644 --- a/tests/pos/singleton-ops-composition.scala +++ b/tests/pos/singleton-ops-composition.scala @@ -6,5 +6,5 @@ object Test { val t1: (2 * 7 + 1) % 10 = 5 val t3: 1 * 1 + 2 * 2 + 3 * 3 + 4 * 4 = 30 val t4: true && false || true && true || false ^ false = true - val t5: 100 << 2 >>> 2 >> 2 ^^ 3 | (7 & 7) = 31 + val t5: BitwiseOr[100 << 2 >>> 2 >> 2 ^^ 3, BitwiseAnd[7, 7]] = 31 }