summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2009-09-09 22:18:54 +0000
committerPaul Phillips <paulp@improving.org>2009-09-09 22:18:54 +0000
commitc71af41d6ae8dd156ffbb9e5d82890047c33bb7f (patch)
tree5281532ab0d655eea5c79d625d501f57eb5420de
parentbc489c725eb256bc39b555cdf7e6966aa8a8b0a3 (diff)
downloadscala-c71af41d6ae8dd156ffbb9e5d82890047c33bb7f.tar.gz
scala-c71af41d6ae8dd156ffbb9e5d82890047c33bb7f.tar.bz2
scala-c71af41d6ae8dd156ffbb9e5d82890047c33bb7f.zip
Generalized -Xdisable-assertions into an annota...
Generalized -Xdisable-assertions into an annotation (presently called @elidable) so it can be used with arbitrary code. It takes an argument which sets its priority level, and you can control what code is elided from the command line by setting -Xelide-level to desired threshold.
-rw-r--r--src/compiler/scala/tools/nsc/Settings.scala60
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Definitions.scala1
-rw-r--r--src/compiler/scala/tools/nsc/symtab/Symbols.scala10
-rw-r--r--src/compiler/scala/tools/nsc/transform/UnCurry.scala10
-rw-r--r--src/library/scala/Predef.scala8
-rw-r--r--src/library/scala/annotation/elidable.scala64
6 files changed, 123 insertions, 30 deletions
diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala
index 3225a62049..2452b3adbf 100644
--- a/src/compiler/scala/tools/nsc/Settings.scala
+++ b/src/compiler/scala/tools/nsc/Settings.scala
@@ -10,6 +10,7 @@ import java.io.File
import io.AbstractFile
import util.SourceFile
import Settings._
+import annotation.elidable
class Settings(errorFn: String => Unit) extends ScalacSettings {
def this() = this(Console.println)
@@ -316,8 +317,14 @@ object Settings {
def str(name: String, arg: String, descr: String, default: String) =
new StringSetting(name, arg, descr, default)
- def sint(name: String, descr: String, default: Int, min: Option[Int], max: Option[Int]) =
- new IntSetting(name, descr, default, min, max)
+ def sint(
+ name: String,
+ descr: String,
+ default: Int,
+ range: Option[(Int, Int)] = None,
+ parser: String => Option[Int] = _ => None
+ ) =
+ new IntSetting(name, descr, default, range, parser)
def multi(name: String, arg: String, descr: String) =
new MultiStringSetting(name, arg, descr)
@@ -413,53 +420,55 @@ object Settings {
override def toString() = "%s = %s".format(name, value)
}
- /** A setting represented by a positive integer */
+ /** A setting represented by an integer */
class IntSetting private[Settings](
val name: String,
val descr: String,
val default: Int,
- val min: Option[Int],
- val max: Option[Int])
+ val range: Option[(Int, Int)],
+ parser: String => Option[Int])
extends Setting(descr) {
type T = Int
protected var v = default
+
+ // not stable values!
+ val IntMin = Int.MinValue
+ val IntMax = Int.MaxValue
+ def min = range map (_._1) getOrElse IntMin
+ def max = range map (_._2) getOrElse IntMax
+
override def value_=(s: Int) =
if (isInputValid(s)) super.value_=(s) else errorMsg
// Validate that min and max are consistent
- (min, max) match {
- case (Some(i), Some(j)) => assert(i <= j)
- case _ => ()
- }
+ assert(min <= max)
// Helper to validate an input
- private def isInputValid(k: Int): Boolean = (min, max) match {
- case (Some(i), Some(j)) => (i <= k) && (k <= j)
- case (Some(i), None) => (i <= k)
- case (None, Some(j)) => (k <= j)
- case _ => true
- }
+ private def isInputValid(k: Int): Boolean = (min <= k) && (k <= max)
// Helper to generate a textual explaination of valid inputs
private def getValidText: String = (min, max) match {
- case (Some(i), Some(j)) => "must be between "+i+" and "+j
- case (Some(i), None) => "must be greater than or equal to "+i
- case (None, Some(j)) => "must be less than or equal to "+j
- case _ => throw new Error("this should never be used")
+ case (IntMin, IntMax) => "can be any integer"
+ case (IntMin, x) => "must be less than or equal to "+x
+ case (x, IntMax) => "must be greater than or equal to "+x
+ case _ => "must be between %d and %d".format(min, max)
}
// Ensure that the default value is actually valid
assert(isInputValid(default))
- def parseInt(x: String): Option[Int] =
- try { Some(x.toInt) }
- catch { case _: NumberFormatException => None }
+ def parseArgument(x: String): Option[Int] = {
+ parser(x) orElse {
+ try { Some(x.toInt) }
+ catch { case _: NumberFormatException => None }
+ }
+ }
def errorMsg = errorFn("invalid setting for -"+name+" "+getValidText)
def tryToSet(args: List[String]) =
if (args.isEmpty) errorAndValue("missing argument", None)
- else parseInt(args.head) match {
+ else parseArgument(args.head) match {
case Some(i) => value = i ; Some(args.tail)
case None => errorMsg ; None
}
@@ -740,7 +749,8 @@ trait ScalacSettings {
val assemrefs = StringSetting ("-Xassem-path", "path", "List of assemblies referenced by the program (only relevant with -target:msil)", ".").dependsOn(target, "msil")
val Xchecknull = BooleanSetting ("-Xcheck-null", "Emit warning on selection of nullable reference")
val checkInit = BooleanSetting ("-Xcheckinit", "Add runtime checks on field accessors. Uninitialized accesses result in an exception being thrown.")
- val noassertions = BooleanSetting ("-Xdisable-assertions", "Generate no assertions and assumptions")
+ val elideLevel = IntSetting ("-Xelide-level", "Generate calls to @elidable-marked methods only method priority is greater than argument.",
+ elidable.ASSERTION, None, elidable.byName.get(_))
val Xexperimental = BooleanSetting ("-Xexperimental", "Enable experimental extensions")
val noForwarders = BooleanSetting ("-Xno-forwarders", "Do not generate static forwarders in mirror classes")
val future = BooleanSetting ("-Xfuture", "Turn on future language features")
@@ -787,7 +797,7 @@ trait ScalacSettings {
val Ynogenericsig = BooleanSetting ("-Yno-generic-signatures", "Suppress generation of generic signatures for Java")
val noimports = BooleanSetting ("-Yno-imports", "Compile without any implicit imports")
val nopredefs = BooleanSetting ("-Yno-predefs", "Compile without any implicit predefined values")
- val Yrecursion = IntSetting ("-Yrecursion", "Recursion depth used when locking symbols", 0, Some(0), None)
+ val Yrecursion = IntSetting ("-Yrecursion", "Recursion depth used when locking symbols", 0, Some(0, Int.MaxValue), _ => None)
val selfInAnnots = BooleanSetting ("-Yself-in-annots", "Include a \"self\" identifier inside of annotations")
val Xshowtrees = BooleanSetting ("-Yshow-trees", "Show detailed trees when used in connection with -print:phase")
val skip = PhasesSetting ("-Yskip", "Skip")
diff --git a/src/compiler/scala/tools/nsc/symtab/Definitions.scala b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
index 87c874465a..6f44541098 100644
--- a/src/compiler/scala/tools/nsc/symtab/Definitions.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Definitions.scala
@@ -96,6 +96,7 @@ trait Definitions {
lazy val TailrecClass = getClass("scala.annotation.tailrec")
lazy val SwitchClass = getClass("scala.annotation.switch")
lazy val ExperimentalClass = getClass("scala.annotation.experimental")
+ lazy val ElidableMethodClass = getClass("scala.annotation.elidable")
// fundamental reference classes
lazy val ScalaObjectClass = getClass("scala.ScalaObject")
diff --git a/src/compiler/scala/tools/nsc/symtab/Symbols.scala b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
index b4a2ca9853..745782fbdf 100644
--- a/src/compiler/scala/tools/nsc/symtab/Symbols.scala
+++ b/src/compiler/scala/tools/nsc/symtab/Symbols.scala
@@ -441,6 +441,16 @@ trait Symbols {
None
}
}
+ def elisionLevel: Option[Int] = {
+ if (!hasAnnotation(ElidableMethodClass)) None
+ else annotations find (_.atp.typeSymbol == ElidableMethodClass) flatMap { annot =>
+ // since we default to enabled by default, only look hard for falsity
+ annot.args match {
+ case Literal(Constant(x: Int)) :: Nil => Some(x)
+ case x => println(x) ; None
+ }
+ }
+ }
/** Does this symbol denote a wrapper object of the interpreter or its class? */
final def isInterpreterWrapper =
diff --git a/src/compiler/scala/tools/nsc/transform/UnCurry.scala b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
index 7af0c4f56c..32a66c321f 100644
--- a/src/compiler/scala/tools/nsc/transform/UnCurry.scala
+++ b/src/compiler/scala/tools/nsc/transform/UnCurry.scala
@@ -543,10 +543,12 @@ abstract class UnCurry extends InfoTransform with TypingTransformers {
treeCopy.UnApply(tree, fn1, args1)
case Apply(fn, args) =>
- if (settings.noassertions.value &&
- (fn.symbol ne null) &&
- (fn.symbol.name == nme.assert_ || fn.symbol.name == nme.assume_) &&
- fn.symbol.owner == PredefModule.moduleClass) {
+ def elideFunctionCall(sym: Symbol) =
+ sym != null && (sym.elisionLevel match {
+ case Some(x) => x < settings.elideLevel.value
+ case _ => false
+ })
+ if (elideFunctionCall(fn.symbol)) {
Literal(()).setPos(tree.pos).setType(UnitClass.tpe)
} else if (fn.symbol == Object_synchronized && shouldBeLiftedAnyway(args.head)) {
transform(treeCopy.Apply(tree, fn, List(liftTree(args.head))))
diff --git a/src/library/scala/Predef.scala b/src/library/scala/Predef.scala
index 95d9901575..108729fae0 100644
--- a/src/library/scala/Predef.scala
+++ b/src/library/scala/Predef.scala
@@ -11,7 +11,6 @@
package scala
-
/** The <code>Predef</code> object provides definitions that are
* accessible in all Scala compilation units without explicit
* qualification.
@@ -97,21 +96,28 @@ object Predef {
throw new Throwable()
}
+ import annotation.elidable
+ import annotation.elidable.ASSERTION
+
+ @elidable(ASSERTION)
def assert(assertion: Boolean) {
if (!assertion)
throw new java.lang.AssertionError("assertion failed")
}
+ @elidable(ASSERTION)
def assert(assertion: Boolean, message: => Any) {
if (!assertion)
throw new java.lang.AssertionError("assertion failed: "+ message)
}
+ @elidable(ASSERTION)
def assume(assumption: Boolean) {
if (!assumption)
throw new java.lang.AssertionError("assumption failed")
}
+ @elidable(ASSERTION)
def assume(assumption: Boolean, message: => Any) {
if (!assumption)
throw new java.lang.AssertionError("assumption failed: "+ message)
diff --git a/src/library/scala/annotation/elidable.scala b/src/library/scala/annotation/elidable.scala
new file mode 100644
index 0000000000..ef82c4d8d2
--- /dev/null
+++ b/src/library/scala/annotation/elidable.scala
@@ -0,0 +1,64 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2002-2009, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+
+package scala.annotation
+
+import java.util.logging.Level
+
+/** An annotation for methods for which invocations might
+ * be removed in the generated code.
+ *
+ * Behavior is influenced by passing -Xelide-level <arg>
+ * to scalac. Methods marked elidable will be omitted from
+ * generated code if the priority given the annotation is lower
+ * than to the command line argument. Examples:
+ *
+ * import annotation.elidable._
+ *
+ * @elidable(WARNING) def foo = log("foo")
+ * @elidable(FINE) def bar = log("bar")
+ *
+ * scalac -Xelide-methods-below=1000
+ */
+final class elidable(final val level: Int) extends StaticAnnotation {}
+
+/** This useless appearing code was necessary to allow people to use
+ * named constants for the elidable annotation. This is what it takes
+ * to convince the compiler to fold the constants: otherwise when it's
+ * time to check an elision level it's staring at a tree like
+ * (Select(Level, Select(FINEST, Apply(intValue, Nil))))
+ * instead of the number 300.
+ */
+object elidable {
+ final val ALL = Int.MinValue // Level.ALL.intValue()
+ final val FINEST = 300 // Level.FINEST.intValue()
+ final val FINER = 400 // Level.FINER.intValue()
+ final val FINE = 500 // Level.FINE.intValue()
+ final val CONFIG = 700 // Level.CONFIG.intValue()
+ final val INFO = 800 // Level.INFO.intValue()
+ final val WARNING = 900 // Level.WARNING.intValue()
+ final val SEVERE = 1000 // Level.SEVERE.intValue()
+ final val OFF = Int.MaxValue // Level.OFF.intValue()
+
+ // and since we had to do that anyway, we can add a few of our own
+ final val ASSERTION = 2000 // we should make this more granular
+
+ // for command line parsing so we can use names or ints
+ val byName: Map[String, Int] = Map(
+ "ALL" -> ALL,
+ "FINEST" -> FINEST,
+ "FINER" -> FINER,
+ "FINE" -> FINE,
+ "CONFIG" -> CONFIG,
+ "INFO" -> INFO,
+ "WARNING" -> WARNING,
+ "SEVERE" -> SEVERE,
+ "OFF" -> OFF,
+ "ASSERTION" -> ASSERTION
+ )
+}