Skip to content

Try/cycle detection #161

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Oct 11, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/dotty/tools/dotc/Compiler.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ class Compiler {
def phases: List[List[Phase]] =
List(
List(new FrontEnd),
List(new Companions),
List(new FirstTransform),
List(new SuperAccessors),
// pickling and refchecks goes here
List(new ElimRepeated, new ElimLocals),
List(/*new RefChecks,*/ new ElimRepeated, new ElimLocals),
List(new ExtensionMethods),
List(new TailRec),
List(new PatternMatcher,
Expand Down
12 changes: 10 additions & 2 deletions src/dotty/tools/dotc/ast/TreeInfo.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,19 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
/** The number of arguments in an application */
def numArgs(tree: Tree): Int = unsplice(tree) match {
case Apply(fn, args) => numArgs(fn) + args.length
case TypeApply(fn, args) => numArgs(fn)
case Block(stats, expr) => numArgs(expr)
case TypeApply(fn, _) => numArgs(fn)
case Block(_, expr) => numArgs(expr)
case _ => 0
}

/** The (last) list of arguments of an application */
def arguments(tree: Tree): List[Tree] = unsplice(tree) match {
case Apply(_, args) => args
case TypeApply(fn, _) => arguments(fn)
case Block(_, expr) => arguments(expr)
case _ => Nil
}

/** Is tree a self constructor call this(...)? I.e. a call to a constructor of the
* same object?
*/
Expand Down
3 changes: 2 additions & 1 deletion src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,8 @@ class ScalaSettings extends Settings.SettingGroup {
val logFreeTerms = BooleanSetting("-Xlog-free-terms", "Print a message when reification creates a free term.")
val logFreeTypes = BooleanSetting("-Xlog-free-types", "Print a message when reification resorts to generating a free type.")
val maxClassfileName = IntSetting("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, 72 to 255)
val Xmigration28 = BooleanSetting("-Xmigration", "Warn about constructs whose behavior may have changed between 2.7 and 2.8.")
val Xmigration = VersionSetting("-Xmigration", "Warn about constructs whose behavior may have changed since version.")
val Xsource = VersionSetting("-Xsource", "Treat compiler input as Scala source for the specified version.")
val Xnojline = BooleanSetting("-Xnojline", "Do not use JLine for editing.")
val Xverify = BooleanSetting("-Xverify", "Verify generic signatures in generated bytecode (asm backend only.)")
val plugin = MultiStringSetting("-Xplugin", "file", "Load one or more plugins from files.")
Expand Down
183 changes: 183 additions & 0 deletions src/dotty/tools/dotc/config/ScalaVersion.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
/* @author James Iry
*/
package dotty.tools.dotc.config

import scala.util.{Try, Success, Failure}

/**
* Represents a single Scala version in a manner that
* supports easy comparison and sorting.
*/
sealed abstract class ScalaVersion extends Ordered[ScalaVersion] {
def unparse: String
}

/**
* A scala version that sorts higher than all actual versions
*/
case object NoScalaVersion extends ScalaVersion {
def unparse = "none"

def compare(that: ScalaVersion): Int = that match {
case NoScalaVersion => 0
case _ => 1
}
}

/**
* A specific Scala version, not one of the magic min/max versions. An SpecificScalaVersion
* may or may not be a released version - i.e. this same class is used to represent
* final, release candidate, milestone, and development builds. The build argument is used
* to segregate builds
*/
case class SpecificScalaVersion(major: Int, minor: Int, rev: Int, build: ScalaBuild) extends ScalaVersion {
def unparse = s"${major}.${minor}.${rev}.${build.unparse}"

def compare(that: ScalaVersion): Int = that match {
case SpecificScalaVersion(thatMajor, thatMinor, thatRev, thatBuild) =>
// this could be done more cleanly by importing scala.math.Ordering.Implicits, but we have to do these
// comparisons a lot so I'm using brute force direct style code
if (major < thatMajor) -1
else if (major > thatMajor) 1
else if (minor < thatMinor) -1
else if (minor > thatMinor) 1
else if (rev < thatRev) -1
else if (rev > thatRev) 1
else build compare thatBuild
case AnyScalaVersion => 1
case NoScalaVersion => -1
}
}

/**
* A Scala version that sorts lower than all actual versions
*/
case object AnyScalaVersion extends ScalaVersion {
def unparse = "any"

def compare(that: ScalaVersion): Int = that match {
case AnyScalaVersion => 0
case _ => -1
}
}

/**
* Methods for parsing ScalaVersions
*/
object ScalaVersion {
private val dot = "\\."
private val dash = "\\-"
private def not(s:String) = s"[^${s}]"
private val R = s"((${not(dot)}*)(${dot}(${not(dot)}*)(${dot}(${not(dash)}*)(${dash}(.*))?)?)?)".r

def parse(versionString : String): Try[ScalaVersion] = {
def failure = Failure(new NumberFormatException(
s"There was a problem parsing ${versionString}. " +
"Versions should be in the form major[.minor[.revision]] " +
"where each part is a positive number, as in 2.10.1. " +
"The minor and revision parts are optional."
))

def toInt(s: String) = s match {
case null | "" => 0
case _ => s.toInt
}

def isInt(s: String) = Try(toInt(s)).isSuccess

import ScalaBuild._

def toBuild(s: String) = s match {
case null | "FINAL" => Final
case s if (s.toUpperCase.startsWith("RC") && isInt(s.substring(2))) => RC(toInt(s.substring(2)))
case s if (s.toUpperCase.startsWith("M") && isInt(s.substring(1))) => Milestone(toInt(s.substring(1)))
case _ => Development(s)
}

try versionString match {
case "" | "any" => Success(AnyScalaVersion)
case "none" => Success(NoScalaVersion)
case R(_, majorS, _, minorS, _, revS, _, buildS) =>
Success(SpecificScalaVersion(toInt(majorS), toInt(minorS), toInt(revS), toBuild(buildS)))
case _ => failure
} catch {
case e: NumberFormatException => failure
}
}

/**
* The version of the compiler running now
*/
val current = parse(util.Properties.versionNumberString).get
}

/**
* Represents the data after the dash in major.minor.rev-build
*/
abstract class ScalaBuild extends Ordered[ScalaBuild] {
/**
* Return a version of this build information that can be parsed back into the
* same ScalaBuild
*/
def unparse: String
}

object ScalaBuild {

/** A development, test, nightly, snapshot or other "unofficial" build
*/
case class Development(id: String) extends ScalaBuild {
def unparse = s"-${id}"

def compare(that: ScalaBuild) = that match {
// sorting two development builds based on id is reasonably valid for two versions created with the same schema
// otherwise it's not correct, but since it's impossible to put a total ordering on development build versions
// this is a pragmatic compromise
case Development(thatId) => id compare thatId
// assume a development build is newer than anything else, that's not really true, but good luck
// mapping development build versions to other build types
case _ => 1
}
}

/** A final build
*/
case object Final extends ScalaBuild {
def unparse = ""

def compare(that: ScalaBuild) = that match {
case Final => 0
// a final is newer than anything other than a development build or another final
case Development(_) => -1
case _ => 1
}
}

/** A candidate for final release
*/
case class RC(n: Int) extends ScalaBuild {
def unparse = s"-RC${n}"

def compare(that: ScalaBuild) = that match {
// compare two rcs based on their RC numbers
case RC(thatN) => n - thatN
// an rc is older than anything other than a milestone or another rc
case Milestone(_) => 1
case _ => -1
}
}

/** An intermediate release
*/
case class Milestone(n: Int) extends ScalaBuild {
def unparse = s"-M${n}"

def compare(that: ScalaBuild) = that match {
// compare two milestones based on their milestone numbers
case Milestone(thatN) => n - thatN
// a milestone is older than anything other than another milestone
case _ => -1

}
}
}
9 changes: 9 additions & 0 deletions src/dotty/tools/dotc/config/Settings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ object Settings {
val IntTag = ClassTag.Int
val StringTag = ClassTag(classOf[String])
val ListTag = ClassTag(classOf[List[_]])
val VersionTag = ClassTag(classOf[ScalaVersion])

class SettingsState(initialValues: Seq[Any]) {
private var values = ArrayBuffer(initialValues: _*)
Expand Down Expand Up @@ -132,6 +133,11 @@ object Settings {
case _: NumberFormatException =>
fail(s"$arg2 is not an integer argument for $name", args2)
}
case (VersionTag, _) =>
ScalaVersion.parse(argRest) match {
case Success(v) => update(v, args)
case Failure(ex) => fail(ex.getMessage, args)
}
case (_, Nil) =>
missingArg
}
Expand Down Expand Up @@ -246,5 +252,8 @@ object Settings {

def PrefixSetting(name: String, pre: String, descr: String): Setting[List[String]] =
publish(Setting(name, descr, Nil, prefix = pre))

def VersionSetting(name: String, descr: String, default: ScalaVersion = NoScalaVersion): Setting[ScalaVersion] =
publish(Setting(name, descr, default))
}
}
33 changes: 33 additions & 0 deletions src/dotty/tools/dotc/core/Annotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package dotty.tools.dotc
package core

import Symbols._, Types._, util.Positions._, Contexts._, Constants._, ast.tpd._
import config.ScalaVersion

object Annotations {

Expand All @@ -15,6 +16,14 @@ object Annotations {

def derivedAnnotation(tree: Tree)(implicit ctx: Context) =
if (tree eq this.tree) this else Annotation(tree)

def arguments(implicit ctx: Context) = ast.tpd.arguments(tree)
def argument(i: Int)(implicit ctx: Context): Option[Tree] = {
val args = arguments
if (i < args.length) Some(args(i)) else None
}
def argumentConstant(i: Int)(implicit ctx: Context): Option[Constant] =
for (ConstantType(c) <- argument(i) map (_.tpe)) yield c
}

case class ConcreteAnnotation(t: Tree) extends Annotation {
Expand Down Expand Up @@ -69,4 +78,28 @@ object Annotations {
val tref = cls.typeRef
Annotation(defn.ThrowsAnnot.typeRef.appliedTo(tref), Ident(tref))
}

/** A decorator that provides queries for specific annotations
* of a symbol.
*/
implicit class AnnotInfo(val sym: Symbol) extends AnyVal {

def isDeprecated(implicit ctx: Context) =
sym.hasAnnotation(defn.DeprecatedAnnot)

def deprecationMessage(implicit ctx: Context) =
for (annot <- sym.getAnnotation(defn.DeprecatedAnnot);
arg <- annot.argumentConstant(0))
yield arg.stringValue

def migrationVersion(implicit ctx: Context) =
for (annot <- sym.getAnnotation(defn.MigrationAnnot);
arg <- annot.argumentConstant(1))
yield ScalaVersion.parse(arg.stringValue)

def migrationMessage(implicit ctx: Context) =
for (annot <- sym.getAnnotation(defn.MigrationAnnot);
arg <- annot.argumentConstant(0))
yield ScalaVersion.parse(arg.stringValue)
}
}
5 changes: 5 additions & 0 deletions src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ class Definitions {
lazy val JavaPackageVal = ctx.requiredPackage("java")
lazy val JavaLangPackageVal = ctx.requiredPackage("java.lang")

// fundamental modules
lazy val SysPackage = ctx.requiredModule("scala.sys.package")
def Sys_error = ctx.requiredMethod(SysPackage.moduleClass.asClass, nme.error)

/** Note: We cannot have same named methods defined in Object and Any (and AnyVal, for that matter)
* because after erasure the Any and AnyVal references get remapped to the Object methods
* which would result in a double binding assertion failure.
Expand Down Expand Up @@ -292,6 +296,7 @@ class Definitions {
lazy val ScalaSignatureAnnot = ctx.requiredClass("scala.reflect.ScalaSignature")
lazy val ScalaLongSignatureAnnot = ctx.requiredClass("scala.reflect.ScalaLongSignature")
lazy val DeprecatedAnnot = ctx.requiredClass("scala.deprecated")
lazy val MigrationAnnot = ctx.requiredClass("scala.migration")
lazy val AnnotationDefaultAnnot = ctx.requiredClass("dotty.annotation.internal.AnnotationDefault")
lazy val ThrowsAnnot = ctx.requiredClass("scala.throws")
lazy val UncheckedAnnot = ctx.requiredClass("scala.unchecked")
Expand Down
17 changes: 7 additions & 10 deletions src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ object SymDenotations {
}

private def completeFrom(completer: LazyType)(implicit ctx: Context): Unit = {
if (myFlags is Touched) throw new CyclicReference(this)
if (myFlags is Touched) throw CyclicReference(this)
myFlags |= Touched

// completions.println(s"completing ${this.debugString}")
Expand Down Expand Up @@ -200,14 +200,9 @@ object SymDenotations {
dropOtherAnnotations(annotations, cls).nonEmpty

/** Optionally, the arguments of the first annotation matching the given class symbol */
final def getAnnotationArgs(cls: Symbol)(implicit ctx: Context): Option[List[tpd.Tree]] =
final def getAnnotation(cls: Symbol)(implicit ctx: Context): Option[Annotation] =
dropOtherAnnotations(annotations, cls) match {
case annot :: _ =>
Some(
annot.tree match {
case Trees.Apply(_, args) => args
case _ => Nil
})
case annot :: _ => Some(annot)
case nil => None
}

Expand Down Expand Up @@ -1039,7 +1034,7 @@ object SymDenotations {
}

private def computeBases(implicit ctx: Context): Unit = {
if (myBaseClasses eq Nil) throw new CyclicReference(this)
if (myBaseClasses eq Nil) throw CyclicReference(this)
myBaseClasses = Nil
val seen = new mutable.BitSet
val locked = new mutable.BitSet
Expand Down Expand Up @@ -1299,7 +1294,7 @@ object SymDenotations {
basetp = computeBaseTypeRefOf(tp)
baseTypeRefCache.put(tp, basetp)
} else if (basetp == NoPrefix) {
throw new CyclicReference(this)
throw CyclicReference(this)
}
basetp
case _ =>
Expand Down Expand Up @@ -1476,6 +1471,8 @@ object SymDenotations {
def complete(denot: SymDenotation)(implicit ctx: Context): Unit = unsupported("complete")
}

object NoCompleter extends NoCompleter

/** A lazy type for modules that points to the module class.
* Needed so that `moduleClass` works before completion.
* Completion of modules is always completion of the underlying
Expand Down
Loading