Skip to content

Commit

Permalink
Add Checked.option and another test.
Browse files Browse the repository at this point in the history
  • Loading branch information
tixxit committed Aug 8, 2013
1 parent 0151f21 commit 00312c8
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 10 deletions.
36 changes: 32 additions & 4 deletions macros/src/main/scala/spire/macros/Checked.scala
@@ -1,11 +1,18 @@
package spire.macros

import scala.language.existentials

import language.experimental.macros
import scala.reflect.macros.Context

object Checked {
def apply[A](n: A): A = macro checkedImpl[A]
case class ArithmeticOverflowException(message: String) extends ArithmeticException(message)

object Checked {
/**
* Rewrites the expression provided to check many Int/Long arithmetic
* operations for overflow (specifically +, -, *, / and unary_-). If an
* overflow is detected, an `ArithmeticOverflowException` is thrown.
*/
def checked[A](n: A): A = macro checkedImpl[A]

def checkedImpl[A](c: Context)(n: c.Expr[A]): c.Expr[A] = {
Expand All @@ -14,8 +21,29 @@ object Checked {
c.Expr[A](resetTree)
}

private final def overflowLong: Nothing = throw new ArithmeticException("Long arithmetic overflow")
private final def overflowInt: Nothing = throw new ArithmeticException("Int arithmetic overflow")
/**
* This is equivalent to wrapping `checked` in a `Some` in a try/catch block
* and if an `ArithmeticOverflowException` is caught, then `None` is returned
* instead.
*/
def option[A](n: A): Option[A] = macro optionImpl[A]

def optionImpl[A](c: Context)(n: c.Expr[A]): c.Expr[Option[A]] = {
val checkedExpr = checkedImpl[A](c)(n)
c.universe.reify {
try {
Some(checkedExpr.splice)
} catch { case (ex: ArithmeticOverflowException) =>
None
}
}
}

@inline
private final def overflowLong: Nothing = throw new ArithmeticOverflowException("Long arithmetic overflow")

@inline
private final def overflowInt: Nothing = throw new ArithmeticOverflowException("Int arithmetic overflow")

final def negate(x: Long): Long = if (x != Long.MinValue) -x else overflowLong

Expand Down
36 changes: 30 additions & 6 deletions macros/src/test/scala/spire/macros/CheckedTest.scala
@@ -1,7 +1,5 @@
package spire.macros

import scala.language.existentials

import org.scalatest.FunSuite
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.prop.GeneratorDrivenPropertyChecks
Expand Down Expand Up @@ -117,17 +115,17 @@ class CheckedTest extends FunSuite with GeneratorDrivenPropertyChecks with Shoul
}

test("Int upgrades to Long for overflow checks when mixed in binary op") {
checked {
Checked.option {
val x = 2L
val y = Int.MaxValue
x + y
} should equal(Int.MaxValue.toLong + 2)
} should equal(Some(Int.MaxValue.toLong + 2))

checked {
Checked.option {
val x = 2L
val y = Int.MaxValue
y + x
} should equal(Int.MaxValue.toLong + 2)
} should equal(Some(Int.MaxValue.toLong + 2))

evaluating(checked {
val x = Long.MaxValue
Expand All @@ -141,4 +139,30 @@ class CheckedTest extends FunSuite with GeneratorDrivenPropertyChecks with Shoul
y * x
}) should produce[ArithmeticException]
}

test("Byte and Short upgrade to Int when mixed") {
evaluating(checked {
val x = Int.MaxValue
val y = (2: Byte)
x * y
}) should produce[ArithmeticException]

evaluating(checked {
val x = Int.MaxValue
val y = (2: Byte)
y * x
}) should produce[ArithmeticException]

evaluating(checked {
val x = Int.MaxValue
val y = (2: Short)
x * y
}) should produce[ArithmeticException]

evaluating(checked {
val x = Int.MaxValue
val y = (2: Short)
y * x
}) should produce[ArithmeticException]
}
}

0 comments on commit 00312c8

Please sign in to comment.