diff options
author | Paul Phillips <paulp@improving.org> | 2009-09-09 22:18:54 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2009-09-09 22:18:54 +0000 |
commit | c71af41d6ae8dd156ffbb9e5d82890047c33bb7f (patch) | |
tree | 5281532ab0d655eea5c79d625d501f57eb5420de | |
parent | bc489c725eb256bc39b555cdf7e6966aa8a8b0a3 (diff) | |
download | scala-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.scala | 60 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/Definitions.scala | 1 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/symtab/Symbols.scala | 10 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/UnCurry.scala | 10 | ||||
-rw-r--r-- | src/library/scala/Predef.scala | 8 | ||||
-rw-r--r-- | src/library/scala/annotation/elidable.scala | 64 |
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 + ) +} |