diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala
index fff4b517b6a4..09e58d186de4 100644
--- a/src/dotty/tools/dotc/Compiler.scala
+++ b/src/dotty/tools/dotc/Compiler.scala
@@ -6,7 +6,7 @@ import Contexts._
import Periods._
import Symbols._
import Scopes._
-import typer.{FrontEnd, Typer, Mode, ImportInfo}
+import typer.{FrontEnd, Typer, Mode, ImportInfo, RefChecks}
import reporting.ConsoleReporter
import dotty.tools.dotc.core.Phases.Phase
import dotty.tools.dotc.transform._
@@ -19,14 +19,14 @@ class Compiler {
def phases: List[List[Phase]] =
List(
List(new FrontEnd),
- List(new Companions),
+ List(new FirstTransform, new SyntheticMethods),
List(new SuperAccessors),
- // pickling and refchecks goes here
- List(new ElimRepeated, new ElimLocals),
+ // pickling goes here
+ List(new RefChecks, new ElimRepeated, new ElimLocals),
List(new ExtensionMethods),
List(new TailRec),
List(new PatternMatcher,
- new LazyValTranformContext().transformer,
+ // new LazyValTranformContext().transformer, // disabled, awaiting fixes
new Splitter),
List(new Nullarify,
new TypeTestsCasts,
diff --git a/src/dotty/tools/dotc/ElimLocals.scala b/src/dotty/tools/dotc/ElimLocals.scala
index cc971f05c5fe..98da95f61b96 100644
--- a/src/dotty/tools/dotc/ElimLocals.scala
+++ b/src/dotty/tools/dotc/ElimLocals.scala
@@ -6,11 +6,11 @@ import DenotTransformers.SymTransformer
import Phases.Phase
import Contexts.Context
import SymDenotations.SymDenotation
-import TreeTransforms.TreeTransform
+import TreeTransforms.MiniPhaseTransform
import Flags.Local
/** Widens all private[this] and protected[this] qualifiers to just private/protected */
-class ElimLocals extends TreeTransform with SymTransformer { thisTransformer =>
+class ElimLocals extends MiniPhaseTransform with SymTransformer { thisTransformer =>
override def name = "elimlocals"
def transformSym(ref: SymDenotation)(implicit ctx: Context) =
diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala
index c3d0f9c3a8a3..705d14f03203 100644
--- a/src/dotty/tools/dotc/ast/Desugar.scala
+++ b/src/dotty/tools/dotc/ast/Desugar.scala
@@ -288,11 +288,9 @@ object desugar {
DefDef(synthetic, name, Nil, Nil, TypeTree(), rhs)
val isDefinedMeth = syntheticProperty(nme.isDefined, Literal(Constant(true)))
val productArityMeth = syntheticProperty(nme.productArity, Literal(Constant(arity)))
- def selectorName(n: Int) =
- if (arity == 1) nme.get else nme.selectorName(n)
val caseParams = constrVparamss.head.toArray
val productElemMeths = for (i <- 0 until arity) yield
- syntheticProperty(selectorName(i), Select(This(EmptyTypeName), caseParams(i).name))
+ syntheticProperty(nme.selectorName(i), Select(This(EmptyTypeName), caseParams(i).name))
val copyMeths =
if (mods is Abstract) Nil
else {
@@ -312,12 +310,12 @@ object desugar {
def productConstr(n: Int) = {
val tycon = ref(defn.ProductNClass(n).typeRef)
val targs = constrVparamss.head map (_.tpt)
- AppliedTypeTree(tycon, targs)
+ if (targs.isEmpty) tycon else AppliedTypeTree(tycon, targs)
}
// Case classes get a ProductN parent
var parents1 = parents
- if ((mods is Case) && 2 <= arity && arity <= Definitions.MaxTupleArity)
+ if ((mods is Case) && arity <= Definitions.MaxTupleArity)
parents1 = parents1 :+ productConstr(arity)
// The thicket which is the desugared version of the companion object
@@ -326,7 +324,8 @@ object desugar {
moduleDef(
ModuleDef(
Modifiers(Synthetic), name.toTermName,
- Template(emptyConstructor, parentTpt :: Nil, EmptyValDef, defs))).toList
+ Template(emptyConstructor, parentTpt :: Nil, EmptyValDef, defs)))
+ .withPos(cdef.pos).toList
// The companion object defifinitions, if a companion is needed, Nil otherwise.
// companion definitions include:
@@ -340,7 +339,8 @@ object desugar {
val companions =
if (mods is Case) {
val parent =
- if (constrTparams.nonEmpty) anyRef // todo: also use anyRef if constructor has a dependent method type (or rule that out)!
+ if (constrTparams.nonEmpty || constrVparamss.length > 1) anyRef
+ // todo: also use anyRef if constructor has a dependent method type (or rule that out)!
else (constrVparamss :\ classTypeRef) ((vparams, restpe) => Function(vparams map (_.tpt), restpe))
val applyMeths =
if (mods is Abstract) Nil
@@ -388,8 +388,9 @@ object desugar {
val tparamAccessors = derivedTparams map { tdef =>
cpy.TypeDef(tdef, originalTparams.next.mods, tdef.name, tdef.rhs, tdef.tparams)
}
+ val caseAccessor = if (mods is Case) CaseAccessor else EmptyFlags
val vparamAccessors = derivedVparamss.flatten map { vdef =>
- cpy.ValDef(vdef, originalVparams.next.mods, vdef.name, vdef.tpt, vdef.rhs)
+ cpy.ValDef(vdef, originalVparams.next.mods | caseAccessor, vdef.name, vdef.tpt, vdef.rhs)
}
cpy.TypeDef(cdef, mods, name,
cpy.Template(impl, constr, parents1, self1,
diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala
index cc3e53abcd8a..ca225993046c 100644
--- a/src/dotty/tools/dotc/ast/TreeInfo.scala
+++ b/src/dotty/tools/dotc/ast/TreeInfo.scala
@@ -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?
*/
@@ -253,7 +261,7 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] =>
* is an abstract typoe declaration
*/
def lacksDefinition(mdef: MemberDef) = mdef match {
- case mdef: ValOrDefDef => mdef.rhs.isEmpty && !mdef.name.isConstructorName
+ case mdef: ValOrDefDef => mdef.rhs.isEmpty && !mdef.name.isConstructorName && !mdef.mods.is(ParamAccessor)
case mdef: TypeDef => mdef.rhs.isEmpty || mdef.rhs.isInstanceOf[TypeBoundsTree]
case _ => false
}
diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala
index d38d4695eedf..81f48cd37d38 100644
--- a/src/dotty/tools/dotc/ast/tpd.scala
+++ b/src/dotty/tools/dotc/ast/tpd.scala
@@ -405,6 +405,21 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def appliedToTypeTrees(targs: List[Tree])(implicit ctx: Context): Tree =
if (targs.isEmpty) tree else TypeApply(tree, targs)
+
+ def isInstance(tp: Type)(implicit ctx: Context): Tree =
+ tree.select(defn.Any_isInstanceOf).appliedToType(tp)
+
+ def asInstance(tp: Type)(implicit ctx: Context): Tree =
+ tree.select(defn.Any_asInstanceOf).appliedToType(tp)
+
+ def ensureConforms(tp: Type)(implicit ctx: Context): Tree =
+ if (tree.tpe <:< tp) tree else asInstance(tp)
+
+ def and(that: Tree)(implicit ctx: Context): Tree =
+ tree.select(defn.Boolean_&&).appliedTo(that)
+
+ def or(that: Tree)(implicit ctx: Context): Tree =
+ tree.select(defn.Boolean_||).appliedTo(that)
}
implicit class ListOfTreeDecorator(val xs: List[tpd.Tree]) extends AnyVal {
@@ -648,15 +663,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
def runtimeCall(name: TermName, args: List[Tree])(implicit ctx: Context): Tree = ???
- def mkAnd(tree1: Tree, tree2: Tree)(implicit ctx: Context) =
- tree1.select(defn.Boolean_and).appliedTo(tree2)
-
- def mkAsInstanceOf(tree: Tree, pt: Type)(implicit ctx: Context): Tree =
- tree.select(defn.Any_asInstanceOf).appliedToType(pt)
-
- def ensureConforms(tree: Tree, pt: Type)(implicit ctx: Context): Tree =
- if (tree.tpe <:< pt) tree else mkAsInstanceOf(tree, pt)
-
// ensure that constructors are fully applied?
// ensure that normal methods are fully applied?
diff --git a/src/dotty/tools/dotc/config/Printers.scala b/src/dotty/tools/dotc/config/Printers.scala
index 5bfe1d0b6829..0cbfb6880ca0 100644
--- a/src/dotty/tools/dotc/config/Printers.scala
+++ b/src/dotty/tools/dotc/config/Printers.scala
@@ -23,6 +23,7 @@ object Printers {
val completions = noPrinter
val gadts = noPrinter
val hk = noPrinter
+ val variances = noPrinter
val incremental = noPrinter
val config = noPrinter
val transforms = noPrinter
diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala
index aab2942bdfe4..af2e42b776b4 100644
--- a/src/dotty/tools/dotc/config/ScalaSettings.scala
+++ b/src/dotty/tools/dotc/config/ScalaSettings.scala
@@ -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.")
diff --git a/src/dotty/tools/dotc/config/ScalaVersion.scala b/src/dotty/tools/dotc/config/ScalaVersion.scala
new file mode 100644
index 000000000000..7d45854417c6
--- /dev/null
+++ b/src/dotty/tools/dotc/config/ScalaVersion.scala
@@ -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
+
+ }
+ }
+}
diff --git a/src/dotty/tools/dotc/config/Settings.scala b/src/dotty/tools/dotc/config/Settings.scala
index 17d4d67125e8..531c49bfbc52 100644
--- a/src/dotty/tools/dotc/config/Settings.scala
+++ b/src/dotty/tools/dotc/config/Settings.scala
@@ -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: _*)
@@ -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
}
@@ -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))
}
}
\ No newline at end of file
diff --git a/src/dotty/tools/dotc/core/Annotations.scala b/src/dotty/tools/dotc/core/Annotations.scala
index b4b7ebd24e2a..f67381ddcd7b 100644
--- a/src/dotty/tools/dotc/core/Annotations.scala
+++ b/src/dotty/tools/dotc/core/Annotations.scala
@@ -2,6 +2,7 @@ package dotty.tools.dotc
package core
import Symbols._, Types._, util.Positions._, Contexts._, Constants._, ast.tpd._
+import config.ScalaVersion
object Annotations {
@@ -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 {
@@ -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)
+ }
}
\ No newline at end of file
diff --git a/src/dotty/tools/dotc/core/Decorators.scala b/src/dotty/tools/dotc/core/Decorators.scala
index cd7b4689689f..c96f1ba31108 100644
--- a/src/dotty/tools/dotc/core/Decorators.scala
+++ b/src/dotty/tools/dotc/core/Decorators.scala
@@ -130,7 +130,7 @@ object Decorators {
*/
implicit class PhaseListDecorator(val names: List[String]) extends AnyVal {
def containsPhase(phase: Phase): Boolean = phase match {
- case phase: TreeTransformer => phase.transformations.exists(containsPhase)
+ case phase: TreeTransformer => phase.transformations.exists(trans => containsPhase(trans.phase))
case _ => names exists (n => n == "all" || phase.name.startsWith(n))
}
}
diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala
index 286d1437f553..82ed3ce1d6cd 100644
--- a/src/dotty/tools/dotc/core/Definitions.scala
+++ b/src/dotty/tools/dotc/core/Definitions.scala
@@ -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.
@@ -177,8 +181,17 @@ class Definitions {
lazy val ScalaPredefModule = ctx.requiredModule("scala.Predef")
lazy val ScalaRuntimeModule = ctx.requiredModule("scala.runtime.ScalaRunTime")
- lazy val BoxesRunTimeModule = ctx.requiredModule("scala.runtime.BoxesRunTime")
- lazy val BoxesRunTimeClass = BoxesRunTimeModule.moduleClass
+ lazy val ScalaRuntimeClass = ScalaRuntimeModule.moduleClass.asClass
+
+ def runtimeMethod(name: PreName) = ctx.requiredMethod(ScalaRuntimeClass, name)
+
+ lazy val BoxesRunTimeModule = ctx.requiredModule("scala.runtime.BoxesRunTime")
+ lazy val BoxesRunTimeClass = BoxesRunTimeModule.moduleClass.asClass
+ lazy val ScalaStaticsModule = ctx.requiredModule("scala.runtime.Statics")
+ lazy val ScalaStaticsClass = ScalaStaticsModule.moduleClass.asClass
+
+ def staticsMethod(name: PreName) = ctx.requiredMethod(ScalaStaticsClass, name)
+
lazy val DottyPredefModule = ctx.requiredModule("dotty.DottyPredef")
lazy val NilModule = ctx.requiredModule("scala.collection.immutable.Nil")
lazy val PredefConformsClass = ctx.requiredClass("scala.Predef." + tpnme.Conforms)
@@ -200,8 +213,8 @@ class Definitions {
lazy val UnitClass = valueClassSymbol("scala.Unit", BoxedUnitClass, java.lang.Void.TYPE, UnitEnc)
lazy val BooleanClass = valueClassSymbol("scala.Boolean", BoxedBooleanClass, java.lang.Boolean.TYPE, BooleanEnc)
lazy val Boolean_! = BooleanClass.requiredMethod(nme.UNARY_!)
- lazy val Boolean_and = BooleanClass.requiredMethod(nme.ZAND)
- lazy val Boolean_or = BooleanClass.requiredMethod(nme.ZOR)
+ lazy val Boolean_&& = BooleanClass.requiredMethod(nme.ZAND)
+ lazy val Boolean_|| = BooleanClass.requiredMethod(nme.ZOR)
lazy val ByteClass = valueClassSymbol("scala.Byte", BoxedByteClass, java.lang.Byte.TYPE, ByteEnc)
lazy val ShortClass = valueClassSymbol("scala.Short", BoxedShortClass, java.lang.Short.TYPE, ShortEnc)
@@ -269,6 +282,9 @@ class Definitions {
lazy val JavaSerializableClass = ctx.requiredClass("java.lang.Serializable")
lazy val ComparableClass = ctx.requiredClass("java.lang.Comparable")
lazy val ProductClass = ctx.requiredClass("scala.Product")
+
+ lazy val Product_canEqual = ProductClass.requiredMethod(nme.canEqual_)
+
lazy val LanguageModuleClass = ctx.requiredModule("dotty.language").moduleClass.asClass
// Annotation base classes
@@ -292,6 +308,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.annotation.migration")
lazy val AnnotationDefaultAnnot = ctx.requiredClass("dotty.annotation.internal.AnnotationDefault")
lazy val ThrowsAnnot = ctx.requiredClass("scala.throws")
lazy val UncheckedAnnot = ctx.requiredClass("scala.unchecked")
@@ -389,7 +406,7 @@ class Definitions {
lazy val Function0_apply = FunctionClass(0).requiredMethod(nme.apply)
lazy val TupleClass = mkArityArray("scala.Tuple", MaxTupleArity, 2)
- lazy val ProductNClass = mkArityArray("scala.Product", MaxTupleArity, 2)
+ lazy val ProductNClass = mkArityArray("scala.Product", MaxTupleArity, 0)
lazy val FunctionClasses: Set[Symbol] = FunctionClass.toSet
lazy val TupleClasses: Set[Symbol] = TupleClass.toSet
@@ -406,6 +423,8 @@ class Definitions {
lazy val RootImports = List[Symbol](JavaLangPackageVal, ScalaPackageVal, ScalaPredefModule, DottyPredefModule)
+ lazy val overriddenBySynthetic = Set[Symbol](Any_equals, Any_hashCode, Any_toString, Product_canEqual)
+
def isTupleType(tp: Type)(implicit ctx: Context) = {
val arity = tp.dealias.argInfos.length
arity <= MaxTupleArity && (tp isRef TupleClass(arity))
diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala
index 022230ff81a5..fa2292c60b71 100644
--- a/src/dotty/tools/dotc/core/Denotations.scala
+++ b/src/dotty/tools/dotc/core/Denotations.scala
@@ -202,7 +202,7 @@ object Denotations {
def matchingDenotation(site: Type, targetType: Type)(implicit ctx: Context): SingleDenotation =
if (isOverloaded)
atSignature(targetType.signature).matchingDenotation(site, targetType)
- else if (exists && !(site.memberInfo(symbol) matches targetType))
+ else if (exists && !site.memberInfo(symbol).matchesLoosely(targetType))
NoDenotation
else
asSingleDenotation
@@ -231,13 +231,16 @@ object Denotations {
else if (denot1.signature matches denot2.signature) {
val info1 = denot1.info
val info2 = denot2.info
+ val sym1 = denot1.symbol
val sym2 = denot2.symbol
val sym2Accessible = sym2.isAccessibleFrom(pre)
- if (sym2Accessible && info2 <:< info1) denot2
+ def prefer(info1: Type, sym1: Symbol, info2: Type, sym2: Symbol) =
+ info1 <:< info2 &&
+ (sym1.isAsConcrete(sym2) || !(info2 <:< info1))
+ if (sym2Accessible && prefer(info2, sym2, info1, sym1)) denot2
else {
- val sym1 = denot1.symbol
val sym1Accessible = sym1.isAccessibleFrom(pre)
- if (sym1Accessible && info1 <:< info2) denot1
+ if (sym1Accessible && prefer(info1, sym1, info2, sym2)) denot1
else if (sym1Accessible && sym2.exists && !sym2Accessible) denot1
else if (sym2Accessible && sym1.exists && !sym1Accessible) denot2
else {
@@ -550,7 +553,9 @@ object Denotations {
startPid = cur.validFor.firstPhaseId
else {
next match {
- case next: ClassDenotation => next.resetFlag(Frozen)
+ case next: ClassDenotation =>
+ assert(!next.is(Package), s"illegal transfomation of package denotation by transformer ${ctx.withPhase(transformer).phase}")
+ next.resetFlag(Frozen)
case _ =>
}
next.nextInRun = cur.nextInRun
diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala
index c527cef62209..20427516df24 100644
--- a/src/dotty/tools/dotc/core/Flags.scala
+++ b/src/dotty/tools/dotc/core/Flags.scala
@@ -304,7 +304,7 @@ object Flags {
/** Info can be refined during GADT pattern match */
final val GADTFlexType = typeFlag(25, "")
- /** A case parameter (or its accessor, or a GADT skolem) */
+ /** A case parameter accessor */
final val CaseAccessor = termFlag(26, "")
/** An type parameter which is an alias for some other (non-visible) type parameter */
@@ -444,7 +444,7 @@ object Flags {
final val RetainedTypeArgFlags = VarianceFlags | ExpandedName | Protected | Local
/** Modules always have these flags set */
- final val ModuleCreationFlags = ModuleVal | Final | Stable
+ final val ModuleCreationFlags = ModuleVal | Lazy | Final | Stable
/** Module classes always have these flags set */
final val ModuleClassCreationFlags = ModuleClass | Final
diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala
index aabde4cf9fb0..cecc5d1d7e23 100644
--- a/src/dotty/tools/dotc/core/Phases.scala
+++ b/src/dotty/tools/dotc/core/Phases.scala
@@ -8,9 +8,8 @@ import DenotTransformers._
import Denotations._
import config.Printers._
import scala.collection.mutable.{ListBuffer, ArrayBuffer}
-import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, TreeTransform}
+import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, MiniPhase, TreeTransform}
import dotty.tools.dotc.transform.TreeTransforms
-import TreeTransforms.Separator
import Periods._
trait Phases {
@@ -81,7 +80,7 @@ object Phases {
val phasesInBlock: Set[String] = phasess(i).map(_.name).toSet
for(phase<-phasess(i)) {
phase match {
- case p: TreeTransform =>
+ case p: MiniPhase =>
val unmetRequirements = p.runsAfterGroupsOf &~ prevPhases
assert(unmetRequirements.isEmpty,
@@ -91,9 +90,9 @@ object Phases {
assert(false, s"Only tree transforms can be squashed, ${phase.name} can not be squashed")
}
}
- val transforms = phasess(i).asInstanceOf[List[TreeTransform]]
+ val transforms = phasess(i).asInstanceOf[List[MiniPhase]].map(_.treeTransform)
val block = new TreeTransformer {
- override def name: String = transformations.map(_.name).mkString("TreeTransform:{", ", ", "}")
+ override def name: String = transformations.map(_.phase.name).mkString("TreeTransform:{", ", ", "}")
override def transformations: Array[TreeTransform] = transforms.toArray
}
squashedPhases += block
@@ -181,7 +180,7 @@ object Phases {
final val erasureName = "erasure"
final val flattenName = "flatten"
- abstract class Phase extends DotClass {
+ trait Phase extends DotClass {
def name: String
diff --git a/src/dotty/tools/dotc/core/Scopes.scala b/src/dotty/tools/dotc/core/Scopes.scala
index 919e35a7e97b..426df83bcddb 100644
--- a/src/dotty/tools/dotc/core/Scopes.scala
+++ b/src/dotty/tools/dotc/core/Scopes.scala
@@ -176,9 +176,9 @@ object Scopes {
/** enter a symbol in this scope. */
final def enter[T <: Symbol](sym: T)(implicit ctx: Context): T = {
- if (sym.isType) {
+ if (sym.isType && ctx.phaseId <= ctx.typerPhase.id) {
assert(lookup(sym.name) == NoSymbol,
- s"duplicate type ${sym.debugString}; previous was ${lookup(sym.name).debugString}") // !!! DEBUG
+ s"duplicate ${sym.debugString}; previous was ${lookup(sym.name).debugString}") // !!! DEBUG
}
newScopeEntry(sym)
sym
diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala
index 91a8e4345918..a19f824e54ed 100644
--- a/src/dotty/tools/dotc/core/SymDenotations.scala
+++ b/src/dotty/tools/dotc/core/SymDenotations.scala
@@ -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}")
@@ -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
}
@@ -719,20 +714,29 @@ object SymDenotations {
/** The non-private symbol whose name and type matches the type of this symbol
* in the given class.
- * @param inClass The class containing the symbol's definition
+ * @param inClass The class containing the result symbol's definition
* @param site The base type from which member types are computed
*
* inClass <-- find denot.symbol class C { <-- symbol is here
*
* site: Subtype of both inClass and C
*/
- final def matchingSymbol(inClass: Symbol, site: Type)(implicit ctx: Context): Symbol = {
+ final def matchingDecl(inClass: Symbol, site: Type)(implicit ctx: Context): Symbol = {
var denot = inClass.info.nonPrivateDecl(name)
if (denot.isTerm) // types of the same name always match
denot = denot.matchingDenotation(site, site.memberInfo(symbol))
denot.symbol
}
+ /** The non-private member of `site` whose name and type matches the type of this symbol
+ */
+ final def matchingMember(site: Type)(implicit ctx: Context): Symbol = {
+ var denot = site.nonPrivateMember(name)
+ if (denot.isTerm) // types of the same name always match
+ denot = denot.matchingDenotation(site, site.memberInfo(symbol))
+ denot.symbol
+ }
+
/** If false, this symbol cannot possibly participate in an override,
* either as overrider or overridee.
*/
@@ -742,7 +746,7 @@ object SymDenotations {
/** The symbol, in class `inClass`, that is overridden by this denotation. */
final def overriddenSymbol(inClass: ClassSymbol)(implicit ctx: Context): Symbol =
if (!canMatchInheritedSymbols && (owner ne inClass)) NoSymbol
- else matchingSymbol(inClass, owner.thisType)
+ else matchingDecl(inClass, owner.thisType)
/** All symbols overriden by this denotation. */
final def allOverriddenSymbols(implicit ctx: Context): Iterator[Symbol] =
@@ -762,7 +766,7 @@ object SymDenotations {
* @param ofclazz is a subclass of this symbol's owner
*/
final def overridingSymbol(inClass: ClassSymbol)(implicit ctx: Context): Symbol =
- if (canMatchInheritedSymbols) matchingSymbol(inClass, inClass.thisType)
+ if (canMatchInheritedSymbols) matchingDecl(inClass, inClass.thisType)
else NoSymbol
/** The symbol accessed by a super in the definition of this symbol when
@@ -772,7 +776,7 @@ object SymDenotations {
final def superSymbolIn(base: Symbol)(implicit ctx: Context): Symbol = {
def loop(bcs: List[ClassSymbol]): Symbol = bcs match {
case bc :: bcs1 =>
- val sym = matchingSymbol(bcs.head, base.thisType)
+ val sym = matchingDecl(bcs.head, base.thisType)
.suchThat(alt => !(alt is Deferred)).symbol
if (sym.exists) sym else loop(bcs.tail)
case _ =>
@@ -801,7 +805,7 @@ object SymDenotations {
*/
final def accessBoundary(base: Symbol)(implicit ctx: Context): Symbol = {
val fs = flags
- if (fs is PrivateOrLocal) owner
+ if (fs is Private) owner
else if (fs is StaticProtected) defn.RootClass
else if (privateWithin.exists && !ctx.phase.erasedTypes) privateWithin
else if (fs is Protected) base
@@ -1039,7 +1043,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
@@ -1299,7 +1303,7 @@ object SymDenotations {
basetp = computeBaseTypeRefOf(tp)
baseTypeRefCache.put(tp, basetp)
} else if (basetp == NoPrefix) {
- throw new CyclicReference(this)
+ throw CyclicReference(this)
}
basetp
case _ =>
@@ -1476,6 +1480,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
diff --git a/src/dotty/tools/dotc/core/Symbols.scala b/src/dotty/tools/dotc/core/Symbols.scala
index 1767d7c0c5ca..06414818f1a0 100644
--- a/src/dotty/tools/dotc/core/Symbols.scala
+++ b/src/dotty/tools/dotc/core/Symbols.scala
@@ -382,7 +382,11 @@ object Symbols {
*/
def enteredAfter(phase: DenotTransformer)(implicit ctx: Context): this.type = {
val nextCtx = ctx.withPhase(phase.next)
- this.owner.asClass.ensureFreshScopeAfter(phase)(nextCtx)
+ if (this.owner.is(Package)) {
+ denot.validFor |= InitialPeriod
+ if (this is Module) this.moduleClass.validFor |= InitialPeriod
+ }
+ else this.owner.asClass.ensureFreshScopeAfter(phase)(nextCtx)
entered(nextCtx)
}
diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala
index c63788ec9377..2c8e9902b8d5 100644
--- a/src/dotty/tools/dotc/core/TypeApplications.scala
+++ b/src/dotty/tools/dotc/core/TypeApplications.scala
@@ -463,16 +463,18 @@ class TypeApplications(val self: Type) extends AnyVal {
self.appliedTo(tparams map (_.typeRef)).LambdaAbstract(tparams)
}
- /** If this type has a base type `B[T1, ..., Tn]` where the type parameters
- * of `B` match one-by-one the variances of `tparams`, convert it to
+ /** Test whether this type has a base type `B[T1, ..., Tn]` where the type parameters
+ * of `B` match one-by-one the variances of `tparams`, and where the lambda
+ * abstracted type
*
* LambdaXYZ { type Apply = B[$hkArg$0, ..., $hkArg$n] }
* { type $hkArg$0 = T1; ...; type $hkArg$n = Tn }
*
+ * satisfies predicate `p`. Try base types in the order of ther occurrence in `baseClasses`.
* A type parameter matches a varianve V if it has V as its variance or if V == 0.
*/
- def EtaLiftTo(tparams: List[Symbol])(implicit ctx: Context): Type = {
- def tryLift(bcs: List[ClassSymbol]): Type = bcs match {
+ def testLifted(tparams: List[Symbol], p: Type => Boolean)(implicit ctx: Context): Boolean = {
+ def tryLift(bcs: List[ClassSymbol]): Boolean = bcs match {
case bc :: bcs1 =>
val tp = self.baseTypeWithArgs(bc)
val targs = tp.argInfos
@@ -481,19 +483,20 @@ class TypeApplications(val self: Type) extends AnyVal {
param2.variance == param2.variance || param2.variance == 0
if ((tycon.typeParams corresponds tparams)(variancesMatch)) {
val expanded = tycon.EtaExpand
- val res = (expanded /: targs) { (partialInst, targ) =>
+ val lifted = (expanded /: targs) { (partialInst, targ) =>
val tparam = partialInst.typeParams.head
RefinedType(partialInst, tparam.name, targ.bounds.withVariance(tparam.variance))
}
- hk.println(i"eta lifting $self --> $res")
- res
+ ctx.traceIndented(i"eta lifting $self --> $lifted", hk) {
+ p(lifted) || tryLift(bcs1)
+ }
}
else tryLift(bcs1)
case nil =>
- NoType
+ false
}
- if (tparams.isEmpty) NoType
- else if (typeParams.nonEmpty) EtaExpand
+ if (tparams.isEmpty) false
+ else if (typeParams.nonEmpty) p(EtaExpand)
else tryLift(self.baseClasses)
}
}
\ No newline at end of file
diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala
index 1e1d02be2fd5..48286fe471f7 100644
--- a/src/dotty/tools/dotc/core/TypeComparer.scala
+++ b/src/dotty/tools/dotc/core/TypeComparer.scala
@@ -315,7 +315,8 @@ class TypeComparer(initctx: Context) extends DotClass {
private def rebase(tp: NamedType): Type = {
def rebaseFrom(prefix: Type): Type = {
rebaseQual(prefix, tp.name, considerBoth = true) match {
- case rt: RefinedType if rt ne prefix => tp.derivedSelect(RefinedThis(rt))
+ case rt: RefinedType if rt ne prefix =>
+ tp.derivedSelect(RefinedThis(rt)).dealias // dealias to short-circuit cycles spanning type aliases or LazyRefs
case _ => tp
}
}
@@ -453,7 +454,7 @@ class TypeComparer(initctx: Context) extends DotClass {
def isHKSubType = tp2.name == tpnme.Apply && {
val lambda2 = tp2.prefix.LambdaClass(forcing = true)
lambda2.exists && !tp1.isLambda &&
- isSubType(tp1.EtaLiftTo(lambda2.typeParams), tp2.prefix)
+ tp1.testLifted(lambda2.typeParams, isSubType(_, tp2.prefix))
}
def compareNamed = {
implicit val ctx: Context = this.ctx // Dotty deviation: implicits need explicit type
@@ -511,6 +512,8 @@ class TypeComparer(initctx: Context) extends DotClass {
case NoType => true
}
compareWild
+ case tp2: LazyRef =>
+ isSubType(tp1, tp2.ref)
case tp2: AnnotatedType =>
isSubType(tp1, tp2.tpe) // todo: refine?
case AndType(tp21, tp22) =>
@@ -568,6 +571,8 @@ class TypeComparer(initctx: Context) extends DotClass {
case _ => true
}
compareWild
+ case tp1: LazyRef =>
+ isSubType(tp1.ref, tp2)
case tp1: AnnotatedType =>
isSubType(tp1.tpe, tp2)
case ErrorType =>
@@ -668,7 +673,7 @@ class TypeComparer(initctx: Context) extends DotClass {
|| hasMatchingMember(name2)
|| fourthTry(tp1, tp2)
)
- || needsEtaLift(tp1, tp2) && isSubType(tp1.EtaLiftTo(tp2.typeParams), tp2)
+ || needsEtaLift(tp1, tp2) && tp1.testLifted(tp2.typeParams, isSubType(_, tp2))
)
}
compareRefined
@@ -763,7 +768,7 @@ class TypeComparer(initctx: Context) extends DotClass {
isNewSubType(tp1.parent, tp2)
}
finally pendingRefinedBases = saved
- } || needsEtaLift(tp2, tp1) && isSubType(tp1, tp2.EtaLiftTo(tp1.typeParams))
+ } || needsEtaLift(tp2, tp1) && tp2.testLifted(tp1.typeParams, isSubType(tp1, _))
case AndType(tp11, tp12) =>
isNewSubType(tp11, tp2) || isNewSubType(tp12, tp2)
case _ =>
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 474958b86575..e5c86ef44c2f 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -31,6 +31,8 @@ import language.implicitConversions
object Types {
+ private var recCount = 0
+
/** The class of types.
* The principal subclasses and sub-objects are as follows:
*
@@ -208,6 +210,10 @@ object Types {
final def forallParts(p: Type => Boolean)(implicit ctx: Context): Boolean =
!existsPart(!p(_))
+ /** Performs operation on all parts of this type */
+ final def foreachPart(p: Type => Unit)(implicit ctx: Context): Unit =
+ new ForeachAccumulator(p).apply((), this)
+
/** The parts of this type which are type or term refs */
final def namedParts(implicit ctx: Context): collection.Set[NamedType] =
namedPartsWith(alwaysTrue)
@@ -218,9 +224,6 @@ object Types {
def namedPartsWith(p: NamedType => Boolean)(implicit ctx: Context): collection.Set[NamedType] =
new NamedPartsAccumulator(p).apply(mutable.LinkedHashSet(), this)
- // needed?
- //final def foreach(f: Type => Unit): Unit = ???
-
/** Map function `f` over elements of an AndType, rebuilding with function `g` */
def mapReduceAnd[T](f: Type => T)(g: (T, T) => T)(implicit ctx: Context): T = stripTypeVar match {
case AndType(tp1, tp2) => g(tp1.mapReduceAnd(f)(g), tp2.mapReduceAnd(f)(g))
@@ -378,6 +381,8 @@ object Types {
* flags in `excluded` from consideration.
*/
final def findMember(name: Name, pre: Type, excluded: FlagSet)(implicit ctx: Context): Denotation = try {
+ recCount += 1
+ assert(recCount < 20)
@tailrec def go(tp: Type): Denotation = tp match {
case tp: RefinedType =>
if (name eq tp.refinedName) goRefined(tp) else go(tp.parent)
@@ -435,8 +440,10 @@ object Types {
case ex: MergeError =>
throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}")
case ex: Throwable =>
- println(s"error occurred during: $this: ${this.widen} member $name")
+ println(i"findMember exception for $this: ${this.widen} member $name")
throw ex // DEBUG
+ } finally {
+ recCount -= 1
}
/** The set of names of members of this type that pass the given name filter
@@ -537,6 +544,16 @@ object Types {
this, that, alwaysMatchSimple = !ctx.phase.erasedTypes)
}
+ /** This is the same as `matches` except that it also matches => T with T and
+ * vice versa.
+ */
+ def matchesLoosely(that: Type)(implicit ctx: Context): Boolean =
+ (this matches that) || {
+ val thisResult = this.widenExpr
+ val thatResult = that.widenExpr
+ (this eq thisResult) != (that eq thatResult) && (thisResult matchesLoosely thatResult)
+ }
+
/** The basetype TypeRef of this type with given class symbol,
* but without including any type arguments
*/
@@ -598,7 +615,9 @@ object Types {
case _ => this
}
- /** Follow aliases until type is no longer an alias type. */
+ /** Follow aliases and derefernces LazyRefs and instantiated TypeVars until type
+ * is no longer alias type, LazyRef, or instantiated type variable.
+ */
final def dealias(implicit ctx: Context): Type = this match {
case tp: TypeRef =>
tp.info match {
@@ -608,6 +627,8 @@ object Types {
case tp: TypeVar =>
val tp1 = tp.instanceOpt
if (tp1.exists) tp1.dealias else tp
+ case tp: LazyRef =>
+ tp.ref.dealias
case tp => tp
}
@@ -642,6 +663,14 @@ object Types {
case _ => NoType
}
+ /** The chain of underlying types as long as type is a TypeProxy.
+ * Useful for diagnostics
+ */
+ def underlyingChain(implicit ctx: Context): List[Type] = this match {
+ case tp: TypeProxy => tp :: tp.underlying.underlyingChain
+ case _ => Nil
+ }
+
/** A prefix-less termRef to a new skolem symbol that has the given type as info */
def narrow(implicit ctx: Context): TermRef = TermRef(NoPrefix, ctx.newSkolem(this))
@@ -1130,7 +1159,11 @@ object Types {
(lastSymbol eq null) ||
(lastSymbol.defRunId != sym.defRunId) ||
(lastSymbol.defRunId == NoRunId) ||
- (lastSymbol.infoOrCompleter == ErrorType),
+ (lastSymbol.infoOrCompleter == ErrorType ||
+ defn.overriddenBySynthetic.contains(lastSymbol)
+ // for overriddenBySynthetic symbols a TermRef such as SomeCaseClass.this.hashCode
+ // might be rewritten from Object#hashCode to the hashCode generated at SyntheticMethods
+ ),
s"data race? overwriting symbol of $this / ${this.getClass} / ${lastSymbol.id} / ${sym.id}")
protected def sig: Signature = Signature.NotAMethod
@@ -1217,7 +1250,7 @@ object Types {
if (ctx.underlyingRecursions < LogPendingUnderlyingThreshold)
op
else if (ctx.pendingUnderlying contains this)
- throw new CyclicReference(symbol)
+ throw CyclicReference(symbol)
else
try {
ctx.pendingUnderlying += this
@@ -1468,6 +1501,12 @@ object Types {
unique(new CachedConstantType(value))
}
+ case class LazyRef(refFn: () => Type) extends UncachedProxyType with ValueType {
+ lazy val ref = refFn()
+ override def underlying(implicit ctx: Context) = ref
+ override def toString = s"LazyRef($ref)"
+ }
+
// --- Refined Type ---------------------------------------------------------
/** A refined type parent { refinement }
@@ -2407,6 +2446,9 @@ object Types {
case tp @ SuperType(thistp, supertp) =>
tp.derivedSuperType(this(thistp), this(supertp))
+ case tp: LazyRef =>
+ LazyRef(() => this(tp.ref))
+
case tp: ClassInfo =>
mapClassInfo(tp)
@@ -2571,6 +2613,11 @@ object Types {
def apply(x: Boolean, tp: Type) = x || p(tp) || foldOver(x, tp)
}
+ class ForeachAccumulator(p: Type => Unit)(implicit ctx: Context) extends TypeAccumulator[Unit] {
+ override def stopAtStatic = false
+ def apply(x: Unit, tp: Type): Unit = foldOver(p(tp), tp)
+ }
+
class NamedPartsAccumulator(p: NamedType => Boolean)(implicit ctx: Context) extends TypeAccumulator[mutable.Set[NamedType]] {
override def stopAtStatic = false
def maybeAdd(x: mutable.Set[NamedType], tp: NamedType) = if (p(tp)) x += tp else x
@@ -2656,10 +2703,17 @@ object Types {
extends FatalTypeError(
s"""malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}""")
- class CyclicReference(val denot: SymDenotation)
+ class CyclicReference private (val denot: SymDenotation)
extends FatalTypeError(s"cyclic reference involving $denot") {
def show(implicit ctx: Context) = s"cyclic reference involving ${denot.show}"
- printStackTrace()
+ }
+
+ object CyclicReference {
+ def apply(denot: SymDenotation)(implicit ctx: Context): CyclicReference = {
+ val ex = new CyclicReference(denot)
+ if (!(ctx.mode is typer.Mode.CheckCyclic)) ex.printStackTrace()
+ ex
+ }
}
class MergeError(msg: String) extends FatalTypeError(msg)
diff --git a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
index 59658c9c14f0..193c872f1a47 100644
--- a/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
+++ b/src/dotty/tools/dotc/core/pickling/ClassfileParser.scala
@@ -11,6 +11,7 @@ import java.lang.Integer.toHexString
import scala.collection.{ mutable, immutable }
import scala.collection.mutable.{ ListBuffer, ArrayBuffer }
import scala.annotation.switch
+import typer.Checking.checkNonCyclic
import io.AbstractFile
class ClassfileParser(
@@ -337,7 +338,11 @@ class ClassfileParser(
val savedIndex = index
try {
index = start
- denot.info = sig2typeBounds(tparams, skiptvs = false)
+ denot.info =
+ checkNonCyclic( // we need the checkNonCyclic call to insert LazyRefs for F-bounded cycles
+ denot.symbol,
+ sig2typeBounds(tparams, skiptvs = false),
+ reportErrors = false)
} finally {
index = savedIndex
}
diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala
index de3f626da44d..2e21358e4891 100644
--- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala
+++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala
@@ -15,6 +15,7 @@ import printing.Texts._
import printing.Printer
import io.AbstractFile
import util.common._
+import typer.Checking.checkNonCyclic
import PickleBuffer._
import scala.reflect.internal.pickling.PickleFormat._
import Decorators._
@@ -516,7 +517,11 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot:
denot setFlag Scala2x
case denot =>
val tp1 = depoly(tp, denot)
- denot.info = if (tag == ALIASsym) TypeAlias(tp1) else tp1
+ denot.info =
+ if (tag == ALIASsym) TypeAlias(tp1)
+ else if (denot.isType) checkNonCyclic(denot.symbol, tp1, reportErrors = false)
+ // we need the checkNonCyclic call to insert LazyRefs for F-bounded cycles
+ else tp1
if (denot.isConstructor) addConstructorTypeParams(denot)
if (atEnd) {
assert(!(denot is SuperAccessor), denot)
diff --git a/src/dotty/tools/dotc/core/transform/Erasure.scala b/src/dotty/tools/dotc/core/transform/Erasure.scala
index 587f0c088304..41254c9826d8 100644
--- a/src/dotty/tools/dotc/core/transform/Erasure.scala
+++ b/src/dotty/tools/dotc/core/transform/Erasure.scala
@@ -145,12 +145,15 @@ class Erasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcard
case tp: PolyType =>
this(tp.resultType)
case tp @ ClassInfo(pre, cls, classParents, decls, _) =>
- def eraseTypeRef(p: TypeRef) = this(p).asInstanceOf[TypeRef]
- val parents: List[TypeRef] =
- if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil
- else if (cls eq defn.ArrayClass) defn.ObjectClass.typeRef :: Nil
- else removeLaterObjects(classParents.mapConserve(eraseTypeRef))
- tp.derivedClassInfo(this(pre), parents, decls, this(tp.selfType))
+ if (cls is Package) tp
+ else {
+ def eraseTypeRef(p: TypeRef) = this(p).asInstanceOf[TypeRef]
+ val parents: List[TypeRef] =
+ if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil
+ else if (cls eq defn.ArrayClass) defn.ObjectClass.typeRef :: Nil
+ else removeLaterObjects(classParents.mapConserve(eraseTypeRef))
+ tp.derivedClassInfo(this(pre), parents, decls, this(tp.selfType))
+ }
case NoType | NoPrefix | ErrorType =>
tp
case tp: WildcardType if wildcardOK =>
diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
index acba22afeda5..ab248a4fcd65 100644
--- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala
+++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala
@@ -109,7 +109,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
return (toTextLocal(tycon) ~ "[" ~ Text(args map argText, ", ") ~ "]").close
}
case tp: TypeRef =>
- if ((tp.symbol is TypeParam | TypeArgument) && !ctx.phase.erasedTypes && !tp.symbol.isCompleting) {
+ val hideType = tp.symbol is TypeParam | TypeArgument | ExpandedName
+ if (hideType && !ctx.phase.erasedTypes && !tp.symbol.isCompleting) {
tp.info match {
case TypeAlias(hi) => return toText(hi)
case _ => if (tp.prefix.isInstanceOf[ThisType]) return nameString(tp.symbol)
@@ -486,7 +487,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
override def toTextFlags(sym: Symbol) = {
var flags = sym.flagsUNSAFE
if (flags is TypeParam) flags = flags &~ Protected
- Text(flags.flagStrings.filterNot(_.startsWith("<")) map stringToText, " ")
+ Text((flags & SourceModifierFlags).flagStrings map stringToText, " ")
}
override def toText(denot: Denotation): Text = denot match {
diff --git a/src/dotty/tools/dotc/transform/CollectEntryPoints.scala b/src/dotty/tools/dotc/transform/CollectEntryPoints.scala
index 0e9f98e79496..d5ae9084065a 100644
--- a/src/dotty/tools/dotc/transform/CollectEntryPoints.scala
+++ b/src/dotty/tools/dotc/transform/CollectEntryPoints.scala
@@ -1,6 +1,6 @@
package dotty.tools.dotc.transform
-import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer}
+import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer, MiniPhaseTransform}
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Contexts.Context
import scala.collection.mutable.ListBuffer
@@ -23,7 +23,7 @@ import StdNames._
import dotty.tools.dotc.util.Positions.Position
import dotty.tools.dotc.config.JavaPlatform
-class CollectEntryPoints extends TreeTransform {
+class CollectEntryPoints extends MiniPhaseTransform {
/** perform context-dependant initialization */
override def init(implicit ctx: Context, info: TransformerInfo): Unit = {
diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala
index 4bef41d8f3a5..33d742a17208 100644
--- a/src/dotty/tools/dotc/transform/Constructors.scala
+++ b/src/dotty/tools/dotc/transform/Constructors.scala
@@ -9,7 +9,7 @@ import dotty.tools.dotc.core.StdNames._
* Right now it's a dummy.
* Awaiting for real implemetation
*/
-class Constructors extends TreeTransform {
+class Constructors extends MiniPhaseTransform {
override def name: String = "constructors"
override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = {
diff --git a/src/dotty/tools/dotc/transform/ElimRepeated.scala b/src/dotty/tools/dotc/transform/ElimRepeated.scala
index 30396eb83635..3635a874138a 100644
--- a/src/dotty/tools/dotc/transform/ElimRepeated.scala
+++ b/src/dotty/tools/dotc/transform/ElimRepeated.scala
@@ -4,7 +4,7 @@ package transform
import core._
import Names._
import Types._
-import TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer}
+import TreeTransforms.{TransformerInfo, MiniPhaseTransform, TreeTransformer}
import ast.Trees.flatten
import Flags._
import Contexts.Context
@@ -20,7 +20,7 @@ import TypeUtils._
/** A transformer that removes repeated parameters (T*) from all types, replacing
* them with Seq types.
*/
-class ElimRepeated extends TreeTransform with InfoTransformer { thisTransformer =>
+class ElimRepeated extends MiniPhaseTransform with InfoTransformer { thisTransformer =>
import ast.tpd._
override def name = "elimrepeated"
diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala
index e5fd9d85d328..06e3506dfb88 100644
--- a/src/dotty/tools/dotc/transform/Erasure.scala
+++ b/src/dotty/tools/dotc/transform/Erasure.scala
@@ -25,7 +25,7 @@ import dotty.tools.dotc.core.Flags
import ValueClasses._
import TypeUtils._
-class Erasure extends Phase with DenotTransformer {
+class Erasure extends Phase with DenotTransformer { thisTransformer =>
override def name: String = "erasure"
@@ -35,10 +35,27 @@ class Erasure extends Phase with DenotTransformer {
def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match {
case ref: SymDenotation =>
assert(ctx.phase == this, s"transforming $ref at ${ctx.phase}")
- val owner = ref.owner
- ref.copySymDenotation(
- owner = if (owner eq defn.AnyClass) defn.ObjectClass else owner,
- info = transformInfo(ref.symbol, ref.info))
+ if (ref.symbol eq defn.ObjectClass) {
+ // Aftre erasure, all former Any members are now Object members
+ val ClassInfo(pre, _, ps, decls, selfInfo) = ref.info
+ val extendedScope = decls.cloneScope
+ defn.AnyClass.classInfo.decls.foreach(extendedScope.enter)
+ ref.copySymDenotation(
+ info = transformInfo(ref.symbol,
+ ClassInfo(pre, defn.ObjectClass, ps, extendedScope, selfInfo))
+ )
+ }
+ else {
+ val oldOwner = ref.owner
+ val newOwner = if (oldOwner eq defn.AnyClass) defn.ObjectClass else oldOwner
+ val oldInfo = ref.info
+ val newInfo = transformInfo(ref.symbol, oldInfo)
+ if ((oldOwner eq newOwner) && (oldInfo eq newInfo)) ref
+ else {
+ assert(!ref.is(Flags.PackageClass), s"trans $ref @ ${ctx.phase} oldOwner = $oldOwner, newOwner = $newOwner, oldInfo = $oldInfo, newInfo = $newInfo ${oldOwner eq newOwner} ${oldInfo eq newInfo}")
+ ref.copySymDenotation(owner = newOwner, info = newInfo)
+ }
+ }
case ref =>
ref.derivedSingleDenotation(ref.symbol, erasure(ref.info))
}
@@ -145,7 +162,7 @@ object Erasure {
cast(runtimeCall(nme.toObjectArray, tree :: Nil), pt)
case _ =>
ctx.log(s"casting from ${tree.showSummary}: ${tree.tpe.show} to ${pt.show}")
- mkAsInstanceOf(tree, pt)
+ tree.asInstance(pt)
}
}
@@ -296,9 +313,9 @@ object Erasure {
val newSymbol = member.symbol(ctx)
assert(oldSymbol.name(beforeCtx) == newSymbol.name,
s"${oldSymbol.name(beforeCtx)} bridging with ${newSymbol.name}")
- val newOverriden = oldSymbol.denot.allOverriddenSymbols.toSet
- val oldOverriden = newSymbol.allOverriddenSymbols(beforeCtx).toSet
- val neededBridges = oldOverriden -- newOverriden
+ val newOverridden = oldSymbol.denot.allOverriddenSymbols.toSet // TODO: clarify new <-> old in a comment; symbols are swapped here
+ val oldOverridden = newSymbol.allOverriddenSymbols(beforeCtx).toSet // TODO: can we find a more efficient impl? newOverridden does not have to be a set!
+ val neededBridges = oldOverridden -- newOverridden
var minimalSet = Set[Symbol]()
// compute minimal set of bridges that are needed:
@@ -316,9 +333,9 @@ object Erasure {
)
clash match {
case Some(cl) =>
- ctx.error(s"bridge for method ${newSymbol.show(beforeCtx)}\n" +
- s"clashes with ${cl.symbol.show(beforeCtx)}\n" +
- s"both have same type after erasure: ${bridge.symbol.info.show}")
+ ctx.error(i"bridge for method ${newSymbol.showLocated(beforeCtx)} of type ${newSymbol.info(beforeCtx)}\n" +
+ i"clashes with ${cl.symbol.showLocated(beforeCtx)} of type ${cl.symbol.info(beforeCtx)}\n" +
+ i"both have same type after erasure: ${bridge.symbol.info}")
case None => minimalSet += bridge
}
}
@@ -347,7 +364,7 @@ object Erasure {
}
val bridge = ctx.newSymbol(newDef.symbol.owner,
parentSym.name, parentSym.flags | Flags.Bridge, parentSym.info, coord = newDef.symbol.owner.coord).asTerm
- bridge.entered // this should be safe, as we're executing in context of next phase
+ bridge.enteredAfter(ctx.phase.prev.asInstanceOf[DenotTransformer]) // this should be safe, as we're executing in context of next phase
ctx.debuglog(s"generating bridge from ${newDef.symbol} to $bridge")
val sel: Tree = This(newDef.symbol.owner.asClass).select(newDef.symbol.termRef)
diff --git a/src/dotty/tools/dotc/transform/Companions.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala
similarity index 60%
rename from src/dotty/tools/dotc/transform/Companions.scala
rename to src/dotty/tools/dotc/transform/FirstTransform.scala
index f6e3dbfdcba4..39791918bd37 100644
--- a/src/dotty/tools/dotc/transform/Companions.scala
+++ b/src/dotty/tools/dotc/transform/FirstTransform.scala
@@ -3,19 +3,27 @@ package transform
import core._
import Names._
-import TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer}
-import ast.Trees.flatten
+import TreeTransforms.{TransformerInfo, MiniPhaseTransform, TreeTransformer}
+import ast.Trees._
import Flags._
+import Types._
+import Constants.Constant
import Contexts.Context
import Symbols._
import scala.collection.mutable
import DenotTransformers._
+import typer.Checking
import Names.Name
import NameOps._
-/** A transformer that creates companion objects for all classes except module classes. */
-class Companions extends TreeTransform with IdentityDenotTransformer { thisTransformer =>
+/** The first tree transform
+ * - ensures there are companion objects for all classes except module classes
+ * - eliminates some kinds of trees: Imports, NamedArgs, all TypTrees other than TypeTree
+ * - checks the bounds of AppliedTypeTrees
+ * - stubs out native methods
+ */
+class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer { thisTransformer =>
import ast.tpd._
override def name = "companions"
@@ -61,6 +69,27 @@ class Companions extends TreeTransform with IdentityDenotTransformer { thisTrans
addMissingCompanions(reorder(stats))
}
+ override def transformDefDef(ddef: DefDef)(implicit ctx: Context, info: TransformerInfo) =
+ if (ddef.symbol.hasAnnotation(defn.NativeAnnot)) {
+ ddef.symbol.resetFlag(Deferred)
+ DefDef(ddef.symbol.asTerm,
+ _ => ref(defn.Sys_error).withPos(ddef.pos)
+ .appliedTo(Literal(Constant("native method stub"))))
+ } else ddef
+
override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] =
ast.Trees.flatten(reorderAndComplete(trees)(ctx.withPhase(thisTransformer.next)))
+
+ override def transformOther(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = tree match {
+ case tree: Import => EmptyTree
+ case tree: NamedArg => tree.arg
+ case AppliedTypeTree(tycon, args) =>
+ val tparams = tycon.tpe.typeSymbol.typeParams
+ Checking.checkBounds(
+ args, tparams.map(_.info.bounds), (tp, argTypes) => tp.substDealias(tparams, argTypes))
+ TypeTree(tree.tpe).withPos(tree.pos)
+ case tree =>
+ if (tree.isType) TypeTree(tree.tpe, tree).withPos(tree.pos)
+ else tree
+ }
}
diff --git a/src/dotty/tools/dotc/transform/InterceptedMethods.scala b/src/dotty/tools/dotc/transform/InterceptedMethods.scala
index 6dd66ec7592a..a8ca754deb7c 100644
--- a/src/dotty/tools/dotc/transform/InterceptedMethods.scala
+++ b/src/dotty/tools/dotc/transform/InterceptedMethods.scala
@@ -40,7 +40,7 @@ import StdNames._
* using the most precise overload available
* - `x.getClass` for getClass in primitives becomes `x.getClass` with getClass in class Object.
*/
-class InterceptedMethods extends TreeTransform {
+class InterceptedMethods extends MiniPhaseTransform {
import tpd._
diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala
index 02e5ed5a7d3b..75fc7ef2e6fb 100644
--- a/src/dotty/tools/dotc/transform/LazyVals.scala
+++ b/src/dotty/tools/dotc/transform/LazyVals.scala
@@ -8,7 +8,7 @@ import Symbols._
import Decorators._
import NameOps._
import StdNames.nme
-import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransformer, TreeTransform}
+import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransformer, MiniPhaseTransform}
import dotty.tools.dotc.ast.Trees._
import dotty.tools.dotc.ast.{untpd, tpd}
import dotty.tools.dotc.core.Constants.Constant
@@ -43,7 +43,7 @@ class LazyValTranformContext {
}
}
- class LazyValsTransform extends TreeTransform with DenotTransformer {
+ class LazyValsTransform extends MiniPhaseTransform with DenotTransformer {
override def name: String = "LazyVals"
diff --git a/src/dotty/tools/dotc/transform/Literalize.scala b/src/dotty/tools/dotc/transform/Literalize.scala
index 14ce8fd05c68..03b2b9978f15 100644
--- a/src/dotty/tools/dotc/transform/Literalize.scala
+++ b/src/dotty/tools/dotc/transform/Literalize.scala
@@ -15,7 +15,7 @@ import ast.Trees._
* The constant types are eliminated by erasure, so we need to keep
* the info about constantness in the trees.
*/
-class Literalize extends TreeTransform {
+class Literalize extends MiniPhaseTransform {
import ast.tpd._
override def name: String = "literalize"
diff --git a/src/dotty/tools/dotc/transform/Nullarify.scala b/src/dotty/tools/dotc/transform/Nullarify.scala
index 8d967cc1a7d0..5756d848ac8a 100644
--- a/src/dotty/tools/dotc/transform/Nullarify.scala
+++ b/src/dotty/tools/dotc/transform/Nullarify.scala
@@ -34,7 +34,7 @@ import ast.Trees._
* expr ==> () => expr if other expr is an argument to a call-by-name parameter
*
*/
-class Nullarify extends TreeTransform with InfoTransformer {
+class Nullarify extends MiniPhaseTransform with InfoTransformer {
import ast.tpd._
override def name: String = "nullarify"
diff --git a/src/dotty/tools/dotc/transform/OverridingPairs.scala b/src/dotty/tools/dotc/transform/OverridingPairs.scala
index 5c857bc38b03..d0bc90389d38 100644
--- a/src/dotty/tools/dotc/transform/OverridingPairs.scala
+++ b/src/dotty/tools/dotc/transform/OverridingPairs.scala
@@ -8,7 +8,7 @@ import collection.mutable.HashMap
import collection.immutable.BitSet
import scala.annotation.tailrec
-/** A class that yields a kind of iterator (`Cursor`),
+/** A module that can produce a kind of iterator (`Cursor`),
* which yields all pairs of overriding/overridden symbols
* that are visible in some baseclass, unless there's a parent class
* that already contains the same pairs.
@@ -16,7 +16,7 @@ import scala.annotation.tailrec
* Adapted from the 2.9 version of OverridingPairs. The 2.10 version is IMO
* way too unwieldy to be maintained.
*/
-abstract class OverridingPairs {
+object OverridingPairs {
/** The cursor class
* @param base the base class that contains the overriding pairs
@@ -102,7 +102,7 @@ abstract class OverridingPairs {
def hasNext: Boolean = curEntry ne null
@tailrec
- final def next: Unit = {
+ final def next(): Unit = {
if (curEntry ne null) {
overriding = curEntry.sym
if (nextEntry ne null) {
diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala
index 40a1574832e4..373fae12f9de 100644
--- a/src/dotty/tools/dotc/transform/PatternMatcher.scala
+++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala
@@ -16,7 +16,7 @@ import ast.Trees._
/** This transform eliminates patterns. Right now it's a dummy.
* Awaiting the real pattern matcher.
*/
-class PatternMatcher extends TreeTransform {
+class PatternMatcher extends MiniPhaseTransform {
import ast.tpd._
override def name: String = "patternMatcher"
diff --git a/src/dotty/tools/dotc/transform/Splitter.scala b/src/dotty/tools/dotc/transform/Splitter.scala
index 921aa1916508..6def41419df2 100644
--- a/src/dotty/tools/dotc/transform/Splitter.scala
+++ b/src/dotty/tools/dotc/transform/Splitter.scala
@@ -12,7 +12,7 @@ import Contexts._, Types._, Decorators._, Denotations._, Symbols._, SymDenotatio
*
* For now, only self references are treated.
*/
-class Splitter extends TreeTransform {
+class Splitter extends MiniPhaseTransform {
import ast.tpd._
override def name: String = "splitter"
diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala
index 5720c3bd921b..2496cf5a922d 100644
--- a/src/dotty/tools/dotc/transform/SuperAccessors.scala
+++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala
@@ -220,7 +220,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this
try tree match {
// Don't transform patterns or strange trees will reach the matcher (ticket #4062)
- // TODO Drop once this runs after pattern matcher
+ // TODO Query `ctx.mode is Pattern` instead.
case CaseDef(pat, guard, body) =>
cpy.CaseDef(tree, pat, transform(guard), transform(body))
@@ -276,7 +276,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this
.installAfter(thisTransformer)
val superAcc =
Super(This(currentClass), tpnme.EMPTY, inConstrCall = false).select(alias)
- DefDef(sym, ensureConforms(superAcc, sym.info.widen))
+ DefDef(sym, superAcc.ensureConforms(sym.info.widen))
}
return forwarder(ctx.withPhase(thisTransformer.next))
}
diff --git a/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/src/dotty/tools/dotc/transform/SyntheticMethods.scala
new file mode 100644
index 000000000000..4f9b2c9fa004
--- /dev/null
+++ b/src/dotty/tools/dotc/transform/SyntheticMethods.scala
@@ -0,0 +1,174 @@
+package dotty.tools.dotc
+package transform
+
+import core._
+import Symbols._, Types._, Contexts._, Names._, StdNames._, Constants._
+import scala.collection.{ mutable, immutable }
+import Flags._
+import TreeTransforms._
+import DenotTransformers._
+import ast.Trees._
+import ast.untpd
+import Decorators._
+import ValueClasses.isDerivedValueClass
+import scala.collection.mutable.ListBuffer
+import scala.language.postfixOps
+
+/** Synthetic method implementations for case classes, case objects,
+ * and value classes.
+ * Selectively added to case classes/objects, unless a non-default
+ * implementation already exists:
+ * def equals(other: Any): Boolean
+ * def hashCode(): Int
+ * def canEqual(other: Any): Boolean
+ * def toString(): String
+ * Special handling:
+ * protected def readResolve(): AnyRef
+ *
+ * Selectively added to value classes, unless a non-default
+ * implementation already exists:
+ *
+ * def equals(other: Any): Boolean
+ * def hashCode(): Int
+ */
+class SyntheticMethods extends MiniPhaseTransform with IdentityDenotTransformer { thisTransformer =>
+ import ast.tpd._
+
+ val name = "synthetics"
+
+ private var valueSymbols: List[Symbol] = _
+ private var caseSymbols: List[Symbol] = _
+
+ override def init(implicit ctx: Context, info: TransformerInfo) = {
+ valueSymbols = List(defn.Any_hashCode, defn.Any_equals)
+ caseSymbols = valueSymbols ++ List(defn.Any_toString, defn.Product_canEqual)
+ }
+
+ /** The synthetic methods of the case or value class `clazz`.
+ */
+ def syntheticMethods(clazz: ClassSymbol)(implicit ctx: Context): List[Tree] = {
+ val clazzType = clazz.typeRef
+ def accessors = clazz.decls.filter(_ is CaseAccessor)
+
+ val symbolsToSynthesize: List[Symbol] =
+ if (clazz.is(Case)) caseSymbols
+ else if (isDerivedValueClass(clazz)) valueSymbols
+ else Nil
+
+ def syntheticDefIfMissing(sym: Symbol): List[Tree] = {
+ val existing = sym.matchingMember(clazz.thisType)
+ if (existing == sym || existing.is(Deferred)) syntheticDef(sym) :: Nil
+ else Nil
+ }
+
+ def syntheticDef(sym: Symbol): Tree = {
+ val synthetic = sym.copy(
+ owner = clazz,
+ flags = sym.flags &~ Deferred | Synthetic | Override,
+ coord = clazz.coord).enteredAfter(thisTransformer).asTerm
+
+ def forwardToRuntime(vrefss: List[List[Tree]]): Tree =
+ ref(defn.runtimeMethod("_" + sym.name.toString)).appliedToArgs(This(clazz) :: vrefss.head)
+
+ def syntheticRHS(implicit ctx: Context): List[List[Tree]] => Tree = synthetic.name match {
+ case nme.hashCode_ => vrefss => hashCodeBody
+ case nme.toString_ => forwardToRuntime
+ case nme.equals_ => vrefss => equalsBody(vrefss.head.head)
+ case nme.canEqual_ => vrefss => canEqualBody(vrefss.head.head)
+ }
+ ctx.log(s"adding $synthetic to $clazz at ${ctx.phase}")
+ DefDef(synthetic, syntheticRHS(ctx.withOwner(synthetic)))
+ }
+
+ /** The class
+ *
+ * case class C(x: T, y: U)
+ *
+ * gets the equals method:
+ *
+ * def equals(that: Any): Boolean =
+ * (this eq that) || {
+ * that match {
+ * case x$0 @ (_: C) => this.x == this$0.x && this.y == x$0.y
+ * case _ => false
+ * }
+ *
+ * If C is a value class the initial `eq` test is omitted.
+ */
+ def equalsBody(that: Tree)(implicit ctx: Context): Tree = {
+ val thatAsClazz = ctx.newSymbol(ctx.owner, nme.x_0, Synthetic, clazzType, coord = ctx.owner.pos) // x$0
+ def wildcardAscription(tp: Type) =
+ Typed(untpd.Ident(nme.WILDCARD).withType(tp), TypeTree(tp))
+ val pattern = Bind(thatAsClazz, wildcardAscription(clazzType)) // x$0 @ (_: C)
+ val comparisons = accessors map (accessor =>
+ This(clazz).select(accessor).select(defn.Any_==).appliedTo(ref(thatAsClazz).select(accessor)))
+ val rhs = // this.x == this$0.x && this.y == x$0.y
+ if (comparisons.isEmpty) Literal(Constant(true)) else comparisons.reduceLeft(_ and _)
+ val matchingCase = CaseDef(pattern, EmptyTree, rhs) // case x$0 @ (_: C) => this.x == this$0.x && this.y == x$0.y
+ val defaultCase = CaseDef(wildcardAscription(defn.AnyType), EmptyTree, Literal(Constant(false))) // case _ => false
+ val matchExpr = Match(that, List(matchingCase, defaultCase))
+ if (isDerivedValueClass(clazz)) matchExpr
+ else {
+ val eqCompare = This(clazz).select(defn.Object_eq).appliedTo(that.asInstance(defn.ObjectType))
+ eqCompare or matchExpr
+ }
+ }
+
+ /** The class
+ *
+ * case class C(x: T, y: T)
+ *
+ * get the hashCode method:
+ *
+ * def hashCode: Int = {
+ * var acc: Int = 0xcafebabe;
+ * acc = Statics.mix(acc, x);
+ * acc = Statics.mix(acc, Statics.this.anyHash(y));
+ * Statics.finalizeHash(acc, 2)
+ * }
+ */
+ def hashCodeBody(implicit ctx: Context): Tree = {
+ val acc = ctx.newSymbol(ctx.owner, "acc".toTermName, Mutable | Synthetic, defn.IntType, coord = ctx.owner.pos)
+ val accDef = ValDef(acc, Literal(Constant(0xcafebabe)))
+ val mixes = for (accessor <- accessors.toList) yield
+ Assign(ref(acc), ref(defn.staticsMethod("mix")).appliedTo(ref(acc), hashImpl(accessor)))
+ val finish = ref(defn.staticsMethod("finalizeHash")).appliedTo(ref(acc), Literal(Constant(accessors.size)))
+ Block(accDef :: mixes, finish)
+ }
+
+ /** The hashCode implementation for given symbol `sym`. */
+ def hashImpl(sym: Symbol)(implicit ctx: Context): Tree = {
+ val d = defn
+ import d._
+ sym.info.finalResultType.typeSymbol match {
+ case UnitClass | NullClass => Literal(Constant(0))
+ case BooleanClass => If(ref(sym), Literal(Constant(1231)), Literal(Constant(1237)))
+ case IntClass => ref(sym)
+ case ShortClass | ByteClass | CharClass => ref(sym).select(nme.toInt)
+ case LongClass => ref(staticsMethod("longHash")).appliedTo(ref(sym))
+ case DoubleClass => ref(staticsMethod("doubleHash")).appliedTo(ref(sym))
+ case FloatClass => ref(staticsMethod("floatHash")).appliedTo(ref(sym))
+ case _ => ref(staticsMethod("anyHash")).appliedTo(ref(sym))
+ }
+ }
+
+ /** The class
+ *
+ * case class C(...)
+ *
+ * gets the canEqual method
+ *
+ * def canEqual(that: Any) = that.isInstanceOf[C]
+ */
+ def canEqualBody(that: Tree): Tree = that.isInstance(clazzType)
+
+ symbolsToSynthesize flatMap syntheticDefIfMissing
+ }
+
+ override def transformTemplate(impl: Template)(implicit ctx: Context, info: TransformerInfo) =
+ if (ctx.owner.is(Case) || isDerivedValueClass(ctx.owner))
+ cpy.Template(impl, impl.constr, impl.parents, impl.self,
+ impl.body ++ syntheticMethods(ctx.owner.asClass)(ctx.withPhase(thisTransformer.next)))
+ else
+ impl
+}
diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala
index d3bec6f902c2..fd414a225ee8 100644
--- a/src/dotty/tools/dotc/transform/TailRec.scala
+++ b/src/dotty/tools/dotc/transform/TailRec.scala
@@ -10,7 +10,7 @@ import dotty.tools.dotc.core.Symbols._
import dotty.tools.dotc.core.Types._
import dotty.tools.dotc.core._
import dotty.tools.dotc.transform.TailRec._
-import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform}
+import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, MiniPhaseTransform}
/**
* A Tail Rec Transformer
@@ -62,7 +62,7 @@ import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform
* self recursive functions, that's why it's renamed to tailrec
*
*/
-class TailRec extends TreeTransform with DenotTransformer with FullParameterization {
+class TailRec extends MiniPhaseTransform with DenotTransformer with FullParameterization {
import dotty.tools.dotc.ast.tpd._
@@ -287,7 +287,7 @@ class TailRec extends TreeTransform with DenotTransformer with FullParameterizat
val tree1 = rewriteTry(tree)
propagateType(tree, tree1)
- case Apply(fun, args) if fun.symbol == defn.Boolean_or || fun.symbol == defn.Boolean_and =>
+ case Apply(fun, args) if fun.symbol == defn.Boolean_|| || fun.symbol == defn.Boolean_&& =>
tpd.cpy.Apply(tree, fun, transform(args))
case Apply(fun, args) =>
diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala
index 8e7c4f6d01f3..129553264e2e 100644
--- a/src/dotty/tools/dotc/transform/TreeTransform.scala
+++ b/src/dotty/tools/dotc/transform/TreeTransform.scala
@@ -6,8 +6,10 @@ import dotty.tools.dotc.core.Contexts.Context
import dotty.tools.dotc.core.Phases.Phase
import dotty.tools.dotc.core.Symbols.Symbol
import dotty.tools.dotc.core.Flags.PackageVal
+import dotty.tools.dotc.typer.Mode
import dotty.tools.dotc.ast.Trees._
import dotty.tools.dotc.core.Decorators._
+import dotty.tools.dotc.util.DotClass
import scala.annotation.tailrec
import config.Printers.transforms
@@ -15,49 +17,47 @@ object TreeTransforms {
import tpd._
/** The base class of tree transforms. For each kind of tree K, there are
- * two methods which can be overridden:
- *
- * prepareForK // return a new TreeTransform which gets applied to the K
- * // node and its children
- * transformK // transform node of type K
- *
- * If a transform does not need to visit a node or any of its children, it
- * signals this fact by returning a NoTransform from a prepare method.
- *
- * If all transforms in a group are NoTransforms, the tree is no longer traversed.
- *
- *
- * Performance analysis: Taking the dotty compiler frontend as a use case, we are aiming for a warm performance of
- * about 4000 lines / sec. This means 6 seconds for a codebase of 24'000 lines. Of these the frontend consumes
- * over 2.5 seconds, erasure and code generation will most likely consume over 1 second each. So we would have
- * about 1 sec for all other transformations in our budget. Of this second, let's assume a maximum of 20% for
- * the general dispatch overhead as opposed to the concrete work done in transformations. So that leaves us with
- * 0.2sec, or roughly 600M processor cycles.
- *
- * Now, to the amount of work that needs to be done. The codebase produces of about 250'000 trees after typechecking.
- * Transformations are likely to make this bigger so let's assume 300K trees on average. We estimate to have about 100
- * micro-transformations. Let's say 5 transformation groups of 20 micro-transformations each. (by comparison,
- * scalac has in excess of 20 phases, and most phases do multiple transformations). There are then 30M visits
- * of a node by a transformation. Each visit has a budget of 20 processor cycles.
- *
- * A more detailed breakdown: I assume that about one third of all transformations have real work to do for each node.
- * This might look high, but keep in mind that the most common nodes are Idents and Selects, and most transformations
- * touch these. By contrast the amount of work for generating new transformations should be negligible.
- *
- * So, in 400 clock cycles we need to (1) perform a pattern match according to the type of node, (2) generate new
- * transformations if applicable, (3) reconstitute the tree node from the result of transforming the children, and
- * (4) chain 7 out of 20 transformations over the resulting tree node. I believe the current algorithm is suitable
- * for achieving this goal, but there can be no wasted cycles anywhere.
- */
- abstract class TreeTransform extends Phase {
+ * two methods which can be overridden:
+ *
+ * prepareForK // return a new TreeTransform which gets applied to the K
+ * // node and its children
+ * transformK // transform node of type K
+ *
+ * If a transform does not need to visit a node or any of its children, it
+ * signals this fact by returning a NoTransform from a prepare method.
+ *
+ * If all transforms in a group are NoTransforms, the tree is no longer traversed.
+ *
+ *
+ * Performance analysis: Taking the dotty compiler frontend as a use case, we are aiming for a warm performance of
+ * about 4000 lines / sec. This means 6 seconds for a codebase of 24'000 lines. Of these the frontend consumes
+ * over 2.5 seconds, erasure and code generation will most likely consume over 1 second each. So we would have
+ * about 1 sec for all other transformations in our budget. Of this second, let's assume a maximum of 20% for
+ * the general dispatch overhead as opposed to the concrete work done in transformations. So that leaves us with
+ * 0.2sec, or roughly 600M processor cycles.
+ *
+ * Now, to the amount of work that needs to be done. The codebase produces of about 250'000 trees after typechecking.
+ * Transformations are likely to make this bigger so let's assume 300K trees on average. We estimate to have about 100
+ * micro-transformations. Let's say 5 transformation groups of 20 micro-transformations each. (by comparison,
+ * scalac has in excess of 20 phases, and most phases do multiple transformations). There are then 30M visits
+ * of a node by a transformation. Each visit has a budget of 20 processor cycles.
+ *
+ * A more detailed breakdown: I assume that about one third of all transformations have real work to do for each node.
+ * This might look high, but keep in mind that the most common nodes are Idents and Selects, and most transformations
+ * touch these. By contrast the amount of work for generating new transformations should be negligible.
+ *
+ * So, in 400 clock cycles we need to (1) perform a pattern match according to the type of node, (2) generate new
+ * transformations if applicable, (3) reconstitute the tree node from the result of transforming the children, and
+ * (4) chain 7 out of 20 transformations over the resulting tree node. I believe the current algorithm is suitable
+ * for achieving this goal, but there can be no wasted cycles anywhere.
+ */
+ abstract class TreeTransform extends DotClass {
+
+ def phase: MiniPhase
/** id of this treeTransform in group */
var idx: Int = _
- /** List of names of phases that should have finished their processing of all compilation units
- * before this phase starts */
- def runsAfterGroupsOf: Set[String] = Set.empty
-
def prepareForIdent(tree: Ident)(implicit ctx: Context) = this
def prepareForSelect(tree: Select)(implicit ctx: Context) = this
def prepareForThis(tree: This)(implicit ctx: Context) = this
@@ -121,6 +121,7 @@ object TreeTransforms {
def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo): Tree = tree
def transformPackageDef(tree: PackageDef)(implicit ctx: Context, info: TransformerInfo): Tree = tree
def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = trees
+ def transformOther(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = tree
/** Transform tree using all transforms of current group (including this one) */
def transform(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transform(tree, info, 0)
@@ -132,11 +133,21 @@ object TreeTransforms {
def transformFollowing(tree: Tree)(implicit ctx: Context, info: TransformerInfo): Tree = info.group.transformSingle(tree, idx + 1)
/** perform context-dependant initialization */
- def init(implicit ctx:Context, info: TransformerInfo): Unit = {}
+ def init(implicit ctx: Context, info: TransformerInfo): Unit = {}
+ }
+
+ /** A phase that defines a TreeTransform to be used in a group */
+ trait MiniPhase extends Phase { thisPhase =>
+ def treeTransform: TreeTransform
+
+ /** List of names of phases that should have finished their processing of all compilation units
+ * before this phase starts
+ */
+ def runsAfterGroupsOf: Set[String] = Set.empty
protected def mkTreeTransformer = new TreeTransformer {
- override def name: String = TreeTransform.this.name
- override def transformations = Array(TreeTransform.this)
+ override def name: String = thisPhase.name
+ override def transformations = Array(treeTransform)
}
override def run(implicit ctx: Context): Unit = {
@@ -144,24 +155,30 @@ object TreeTransforms {
}
}
+ /** A mini phase that is its own tree transform */
+ abstract class MiniPhaseTransform extends TreeTransform with MiniPhase {
+ def treeTransform = this
+ def phase = this
+ }
+
val NoTransform = new TreeTransform {
- override def name: String = "NoTransform"
+ def phase = unsupported("phase")
idx = -1
}
- class Separator extends TreeTransform {
- override def name: String = "Separator"
+/* disabled; not needed anywhere
+ class Separator extends TreeTransform(phaseId) {
+ //override def name: String = "Separator"
idx = -1
}
-
+*/
type Mutator[T] = (TreeTransform, T, Context) => TreeTransform
- class TransformerInfo(val transformers: Array[TreeTransform], val nx: NXTransformations, val group:TreeTransformer)
+ class TransformerInfo(val transformers: Array[TreeTransform], val nx: NXTransformations, val group: TreeTransformer)
- /**
- * This class maintains track of which methods are redefined in MiniPhases and creates execution plans for transformXXX and prepareXXX
- * Thanks to Martin for this idea
- * @see NXTransformations.index for format of plan
+ /** This class maintains track of which methods are redefined in MiniPhases and creates execution plans for transformXXX and prepareXXX
+ * Thanks to Martin for this idea
+ * @see NXTransformations.index for format of plan
*/
class NXTransformations {
@@ -170,11 +187,11 @@ object TreeTransforms {
else hasRedefinedMethod(cls.getSuperclass, name)
/** Create an index array `next` of size one larger than teh size of `transforms` such that
- * for each index i, `next(i)` is the smallest index j such that
- *
- * i <= j
- * j == transforms.length || transform(j) defines a non-default method with given `name`
- */
+ * for each index i, `next(i)` is the smallest index j such that
+ *
+ * i <= j
+ * j == transforms.length || transform(j) defines a non-default method with given `name`
+ */
private def index(transformations: Array[TreeTransform], name: String): Array[Int] = {
val len = transformations.length
val next = new Array[Int](len + 1)
@@ -281,6 +298,7 @@ object TreeTransforms {
nxTransTemplate = index(transformations, "transformTemplate")
nxTransPackageDef = index(transformations, "transformPackageDef")
nxTransStats = index(transformations, "transformStats")
+ nxTransOther = index(transformations, "transformOther")
}
def this(prev: NXTransformations, changedTansformation: TreeTransform, transformationIndex: Int, reuse: Boolean = false) = {
@@ -349,12 +367,12 @@ object TreeTransforms {
nxTransTemplate = indexUpdate(prev.nxTransTemplate, changedTansformation, transformationIndex, "transformTemplate", copy)
nxTransPackageDef = indexUpdate(prev.nxTransPackageDef, changedTansformation, transformationIndex, "transformPackageDef", copy)
nxTransStats = indexUpdate(prev.nxTransStats, changedTansformation, transformationIndex, "transformStats", copy)
+ nxTransOther = indexUpdate(prev.nxTransOther, changedTansformation, transformationIndex, "transformOther", copy)
}
- /**
- * Those arrays are used as "execution plan" in order to only execute non-tivial transformations\preparations
- * for every integer i array(i) contains first non trivial transformation\preparation on particular tree subtype.
- * If no nontrivial transformation are left stored value is greater than transformers.size
+ /** Those arrays are used as "execution plan" in order to only execute non-tivial transformations\preparations
+ * for every integer i array(i) contains first non trivial transformation\preparation on particular tree subtype.
+ * If no nontrivial transformation are left stored value is greater than transformers.size
*/
var nxPrepIdent: Array[Int] = _
var nxPrepSelect: Array[Int] = _
@@ -419,6 +437,7 @@ object TreeTransforms {
var nxTransTemplate: Array[Int] = _
var nxTransPackageDef: Array[Int] = _
var nxTransStats: Array[Int] = _
+ var nxTransOther: Array[Int] = _
}
/** A group of tree transforms that are applied in sequence during the same phase */
@@ -442,7 +461,7 @@ object TreeTransforms {
var allDone = i < l
while (i < l) {
val oldTransform = result(i)
- val newTransform = mutator(oldTransform, tree, ctx.withPhase(oldTransform))
+ val newTransform = mutator(oldTransform, tree, ctx.withPhase(oldTransform.phase))
allDone = allDone && (newTransform eq NoTransform)
if (!(oldTransform eq newTransform)) {
if (!transformersCopied) result = result.clone()
@@ -490,12 +509,12 @@ object TreeTransforms {
val prepForTypeDef: Mutator[TypeDef] = (trans, tree, ctx) => trans.prepareForTypeDef(tree)(ctx)
val prepForTemplate: Mutator[Template] = (trans, tree, ctx) => trans.prepareForTemplate(tree)(ctx)
val prepForPackageDef: Mutator[PackageDef] = (trans, tree, ctx) => trans.prepareForPackageDef(tree)(ctx)
- val prepForStats: Mutator[List[Tree]]= (trans, trees, ctx) => trans.prepareForStats(trees)(ctx)
+ val prepForStats: Mutator[List[Tree]] = (trans, trees, ctx) => trans.prepareForStats(trees)(ctx)
def transform(t: Tree)(implicit ctx: Context): Tree = {
val initialTransformations = transformations
val info = new TransformerInfo(initialTransformations, new NXTransformations(initialTransformations), this)
- initialTransformations.zipWithIndex.foreach{
+ initialTransformations.zipWithIndex.foreach {
case (transform, id) =>
transform.idx = id
transform.init(ctx, info)
@@ -507,7 +526,7 @@ object TreeTransforms {
final private[TreeTransforms] def goIdent(tree: Ident, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformIdent(tree)(ctx.withPhase(trans), info) match {
+ trans.transformIdent(tree)(ctx.withPhase(trans.phase), info) match {
case t: Ident => goIdent(t, info.nx.nxTransIdent(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -518,7 +537,7 @@ object TreeTransforms {
final private[TreeTransforms] def goSelect(tree: Select, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformSelect(tree)(ctx.withPhase(trans), info) match {
+ trans.transformSelect(tree)(ctx.withPhase(trans.phase), info) match {
case t: Select => goSelect(t, info.nx.nxTransSelect(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -529,7 +548,7 @@ object TreeTransforms {
final private[TreeTransforms] def goThis(tree: This, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformThis(tree)(ctx.withPhase(trans), info) match {
+ trans.transformThis(tree)(ctx.withPhase(trans.phase), info) match {
case t: This => goThis(t, info.nx.nxTransThis(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -540,7 +559,7 @@ object TreeTransforms {
final private[TreeTransforms] def goSuper(tree: Super, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformSuper(tree)(ctx.withPhase(trans), info) match {
+ trans.transformSuper(tree)(ctx.withPhase(trans.phase), info) match {
case t: Super => goSuper(t, info.nx.nxTransSuper(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -551,7 +570,7 @@ object TreeTransforms {
final private[TreeTransforms] def goApply(tree: Apply, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformApply(tree)(ctx.withPhase(trans), info) match {
+ trans.transformApply(tree)(ctx.withPhase(trans.phase), info) match {
case t: Apply => goApply(t, info.nx.nxTransApply(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -562,7 +581,7 @@ object TreeTransforms {
final private[TreeTransforms] def goTypeApply(tree: TypeApply, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformTypeApply(tree)(ctx.withPhase(trans), info) match {
+ trans.transformTypeApply(tree)(ctx.withPhase(trans.phase), info) match {
case t: TypeApply => goTypeApply(t, info.nx.nxTransTypeApply(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -573,7 +592,7 @@ object TreeTransforms {
final private[TreeTransforms] def goNew(tree: New, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformNew(tree)(ctx.withPhase(trans), info) match {
+ trans.transformNew(tree)(ctx.withPhase(trans.phase), info) match {
case t: New => goNew(t, info.nx.nxTransNew(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -584,7 +603,7 @@ object TreeTransforms {
final private[TreeTransforms] def goPair(tree: Pair, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformPair(tree)(ctx.withPhase(trans), info) match {
+ trans.transformPair(tree)(ctx.withPhase(trans.phase), info) match {
case t: Pair => goPair(t, info.nx.nxTransPair(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -595,7 +614,7 @@ object TreeTransforms {
final private[TreeTransforms] def goTyped(tree: Typed, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformTyped(tree)(ctx.withPhase(trans), info) match {
+ trans.transformTyped(tree)(ctx.withPhase(trans.phase), info) match {
case t: Typed => goTyped(t, info.nx.nxTransTyped(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -606,7 +625,7 @@ object TreeTransforms {
final private[TreeTransforms] def goAssign(tree: Assign, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformAssign(tree)(ctx.withPhase(trans), info) match {
+ trans.transformAssign(tree)(ctx.withPhase(trans.phase), info) match {
case t: Assign => goAssign(t, info.nx.nxTransAssign(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -617,7 +636,7 @@ object TreeTransforms {
final private[TreeTransforms] def goLiteral(tree: Literal, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformLiteral(tree)(ctx.withPhase(trans), info) match {
+ trans.transformLiteral(tree)(ctx.withPhase(trans.phase), info) match {
case t: Literal => goLiteral(t, info.nx.nxTransLiteral(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -628,7 +647,7 @@ object TreeTransforms {
final private[TreeTransforms] def goBlock(tree: Block, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformBlock(tree)(ctx.withPhase(trans), info) match {
+ trans.transformBlock(tree)(ctx.withPhase(trans.phase), info) match {
case t: Block => goBlock(t, info.nx.nxTransBlock(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -639,7 +658,7 @@ object TreeTransforms {
final private[TreeTransforms] def goIf(tree: If, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformIf(tree)(ctx.withPhase(trans), info) match {
+ trans.transformIf(tree)(ctx.withPhase(trans.phase), info) match {
case t: If => goIf(t, info.nx.nxTransIf(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -650,7 +669,7 @@ object TreeTransforms {
final private[TreeTransforms] def goClosure(tree: Closure, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformClosure(tree)(ctx.withPhase(trans), info) match {
+ trans.transformClosure(tree)(ctx.withPhase(trans.phase), info) match {
case t: Closure => goClosure(t, info.nx.nxTransClosure(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -661,7 +680,7 @@ object TreeTransforms {
final private[TreeTransforms] def goMatch(tree: Match, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformMatch(tree)(ctx.withPhase(trans), info) match {
+ trans.transformMatch(tree)(ctx.withPhase(trans.phase), info) match {
case t: Match => goMatch(t, info.nx.nxTransMatch(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -672,7 +691,7 @@ object TreeTransforms {
final private[TreeTransforms] def goCaseDef(tree: CaseDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformCaseDef(tree)(ctx.withPhase(trans), info) match {
+ trans.transformCaseDef(tree)(ctx.withPhase(trans.phase), info) match {
case t: CaseDef => goCaseDef(t, info.nx.nxTransCaseDef(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -683,7 +702,7 @@ object TreeTransforms {
final private[TreeTransforms] def goReturn(tree: Return, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformReturn(tree)(ctx.withPhase(trans), info) match {
+ trans.transformReturn(tree)(ctx.withPhase(trans.phase), info) match {
case t: Return => goReturn(t, info.nx.nxTransReturn(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -694,7 +713,7 @@ object TreeTransforms {
final private[TreeTransforms] def goTry(tree: Try, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformTry(tree)(ctx.withPhase(trans), info) match {
+ trans.transformTry(tree)(ctx.withPhase(trans.phase), info) match {
case t: Try => goTry(t, info.nx.nxTransTry(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -705,7 +724,7 @@ object TreeTransforms {
final private[TreeTransforms] def goThrow(tree: Throw, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformThrow(tree)(ctx.withPhase(trans), info) match {
+ trans.transformThrow(tree)(ctx.withPhase(trans.phase), info) match {
case t: Throw => goThrow(t, info.nx.nxTransThrow(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -716,7 +735,7 @@ object TreeTransforms {
final private[TreeTransforms] def goSeqLiteral(tree: SeqLiteral, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformSeqLiteral(tree)(ctx.withPhase(trans), info) match {
+ trans.transformSeqLiteral(tree)(ctx.withPhase(trans.phase), info) match {
case t: SeqLiteral => goSeqLiteral(t, info.nx.nxTransSeqLiteral(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -727,7 +746,7 @@ object TreeTransforms {
final private[TreeTransforms] def goTypeTree(tree: TypeTree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformTypeTree(tree)(ctx.withPhase(trans), info) match {
+ trans.transformTypeTree(tree)(ctx.withPhase(trans.phase), info) match {
case t: TypeTree => goTypeTree(t, info.nx.nxTransTypeTree(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -738,7 +757,7 @@ object TreeTransforms {
final private[TreeTransforms] def goSelectFromTypeTree(tree: SelectFromTypeTree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformSelectFromTypeTree(tree)(ctx.withPhase(trans), info) match {
+ trans.transformSelectFromTypeTree(tree)(ctx.withPhase(trans.phase), info) match {
case t: SelectFromTypeTree => goSelectFromTypeTree(t, info.nx.nxTransSelectFromTypeTree(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -749,7 +768,7 @@ object TreeTransforms {
final private[TreeTransforms] def goBind(tree: Bind, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformBind(tree)(ctx.withPhase(trans), info) match {
+ trans.transformBind(tree)(ctx.withPhase(trans.phase), info) match {
case t: Bind => goBind(t, info.nx.nxTransBind(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -760,7 +779,7 @@ object TreeTransforms {
final private[TreeTransforms] def goAlternative(tree: Alternative, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformAlternative(tree)(ctx.withPhase(trans), info) match {
+ trans.transformAlternative(tree)(ctx.withPhase(trans.phase), info) match {
case t: Alternative => goAlternative(t, info.nx.nxTransAlternative(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -771,7 +790,7 @@ object TreeTransforms {
final private[TreeTransforms] def goValDef(tree: ValDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformValDef(tree)(ctx.withPhase(trans), info) match {
+ trans.transformValDef(tree)(ctx.withPhase(trans.phase), info) match {
case t: ValDef => goValDef(t, info.nx.nxTransValDef(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -782,7 +801,7 @@ object TreeTransforms {
final private[TreeTransforms] def goDefDef(tree: DefDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformDefDef(tree)(ctx.withPhase(trans), info) match {
+ trans.transformDefDef(tree)(ctx.withPhase(trans.phase), info) match {
case t: DefDef => goDefDef(t, info.nx.nxTransDefDef(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -793,7 +812,7 @@ object TreeTransforms {
final private[TreeTransforms] def goUnApply(tree: UnApply, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformUnApply(tree)(ctx.withPhase(trans), info) match {
+ trans.transformUnApply(tree)(ctx.withPhase(trans.phase), info) match {
case t: UnApply => goUnApply(t, info.nx.nxTransUnApply(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -804,7 +823,7 @@ object TreeTransforms {
final private[TreeTransforms] def goTypeDef(tree: TypeDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformTypeDef(tree)(ctx.withPhase(trans), info) match {
+ trans.transformTypeDef(tree)(ctx.withPhase(trans.phase), info) match {
case t: TypeDef => goTypeDef(t, info.nx.nxTransTypeDef(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -815,7 +834,7 @@ object TreeTransforms {
final private[TreeTransforms] def goTemplate(tree: Template, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformTemplate(tree)(ctx.withPhase(trans), info) match {
+ trans.transformTemplate(tree)(ctx.withPhase(trans.phase), info) match {
case t: Template => goTemplate(t, info.nx.nxTransTemplate(cur + 1))
case t => transformSingle(t, cur + 1)
}
@@ -826,13 +845,21 @@ object TreeTransforms {
final private[TreeTransforms] def goPackageDef(tree: PackageDef, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
if (cur < info.transformers.length) {
val trans = info.transformers(cur)
- trans.transformPackageDef(tree)(ctx.withPhase(trans), info) match {
+ trans.transformPackageDef(tree)(ctx.withPhase(trans.phase), info) match {
case t: PackageDef => goPackageDef(t, info.nx.nxTransPackageDef(cur + 1))
case t => transformSingle(t, cur + 1)
}
} else tree
}
+ final private[TreeTransforms] def goOther(tree: Tree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree = {
+ if (cur < info.transformers.length) {
+ val trans = info.transformers(cur)
+ val t = trans.transformOther(tree)(ctx.withPhase(trans.phase), info)
+ transformSingle(t, cur + 1)
+ } else tree
+ }
+
final private[TreeTransforms] def goNamed(tree: NameTree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree =
tree match {
case tree: Ident => goIdent(tree, info.nx.nxTransIdent(cur))
@@ -872,7 +899,7 @@ object TreeTransforms {
case tree: Template => goTemplate(tree, info.nx.nxTransTemplate(cur))
case tree: PackageDef => goPackageDef(tree, info.nx.nxTransPackageDef(cur))
case Thicket(trees) => cpy.Thicket(tree, transformTrees(trees, info, cur))
- case tree => tree
+ case tree => goOther(tree, info.nx.nxTransOther(cur))
}
final private[TreeTransforms] def transformSingle(tree: Tree, cur: Int)(implicit ctx: Context, info: TransformerInfo): Tree =
@@ -1052,7 +1079,7 @@ object TreeTransforms {
implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForCaseDef, info.nx.nxPrepCaseDef, tree, cur)
if (mutatedInfo eq null) tree
else {
- val pat = transform(tree.pat, mutatedInfo, cur)
+ val pat = transform(tree.pat, mutatedInfo, cur)(ctx.withMode(Mode.Pattern))
val guard = transform(tree.guard, mutatedInfo, cur)
val body = transform(tree.body, mutatedInfo, cur)
goCaseDef(cpy.CaseDef(tree, pat, guard, body), mutatedInfo.nx.nxTransCaseDef(cur))
@@ -1130,19 +1157,17 @@ object TreeTransforms {
val stats = transformStats(tree.stats, tree.symbol, mutatedInfo, cur)(nestedCtx)
goPackageDef(cpy.PackageDef(tree, pid, stats), mutatedInfo.nx.nxTransPackageDef(cur))
}
- case tree: Import => EmptyTree
- case tree: NamedArg => transform(tree.arg, info, cur)
case Thicket(trees) => cpy.Thicket(tree, transformTrees(trees, info, cur))
case tree =>
- if (tree.isType) transform(TypeTree(tree.tpe).withPos(tree.pos), info, cur)
- else tree
+ implicit val originalInfo: TransformerInfo = info
+ goOther(tree, info.nx.nxTransOther(cur))
}
def transform(tree: Tree, info: TransformerInfo, cur: Int)(implicit ctx: Context): Tree = ctx.traceIndented(s"transforming ${tree.show} at ${ctx.phase}", transforms, show = true) {
if (cur < info.transformers.length) {
// if cur > 0 then some of the symbols can be created by already performed transformations
- // this means that their denotations could not exists in previous periods
- val pctx = ctx.withPhase(info.transformers(cur))
+ // this means that their denotations could not exists in previous period
+ val pctx = ctx.withPhase(info.transformers(cur).phase)
tree match {
//split one big match into 2 smaller ones
case tree: NameTree => transformNamed(tree, info, cur)(pctx)
@@ -1164,9 +1189,9 @@ object TreeTransforms {
val newInfo = mutateTransformers(info, prepForStats, info.nx.nxPrepStats, trees, current)
val exprCtx = ctx.withOwner(exprOwner)
def transformStat(stat: Tree): Tree = stat match {
- case _: Import | _: DefTree => transform(stat, info, current)
+ case _: Import | _: DefTree => transform(stat, newInfo, current)
case Thicket(stats) => cpy.Thicket(stat, stats mapConserve transformStat)
- case _ => transform(stat, info, current)(exprCtx)
+ case _ => transform(stat, newInfo, current)(exprCtx)
}
val newTrees = flatten(trees.mapconserve(transformStat))
goStats(newTrees, newInfo.nx.nxTransStats(current))(ctx, newInfo)
diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
index b209f7647ecf..ef0359136028 100644
--- a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
+++ b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala
@@ -21,7 +21,7 @@ import Erasure.Boxing.box
* - have a reference type as receiver
* - can be translated directly to machine instructions
*/
-class TypeTestsCasts extends TreeTransform {
+class TypeTestsCasts extends MiniPhaseTransform {
import ast.tpd._
override def name: String = "typeTestsCasts"
@@ -59,7 +59,7 @@ class TypeTestsCasts extends TreeTransform {
case _ =>
erased2 match {
case Literal(Constant(true)) => erased1
- case _ => mkAnd(erased1, erased2)
+ case _ => erased1 and erased2
}
}
}
@@ -68,7 +68,7 @@ class TypeTestsCasts extends TreeTransform {
runtimeCall(nme.isArray, arg :: Literal(Constant(ndims)) :: Nil)
if (ndims == 1) isArrayTest(qual)
else evalOnce(qual) { qual1 =>
- mkAnd(derivedTree(qual1, defn.Any_isInstanceOf, qual1.tpe), isArrayTest(qual1))
+ derivedTree(qual1, defn.Any_isInstanceOf, qual1.tpe) and isArrayTest(qual1)
}
case _ =>
derivedTree(expr, defn.Any_isInstanceOf, argType)
diff --git a/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala b/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala
index ccfaaa0dcd96..f2d8d4d4a075 100644
--- a/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala
+++ b/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala
@@ -11,7 +11,7 @@ import core.Symbols._
import ast.Trees._
import ast.tpd.{Apply, Tree, cpy}
-class UncurryTreeTransform extends TreeTransform with InfoTransformer {
+class UncurryTreeTransform extends MiniPhaseTransform with InfoTransformer {
override def name: String = "uncurry"
override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree =
diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala
index 242985b57ffc..0991bf4a8b4f 100644
--- a/src/dotty/tools/dotc/typer/Applications.scala
+++ b/src/dotty/tools/dotc/typer/Applications.scala
@@ -433,8 +433,12 @@ trait Applications extends Compatibility { self: Typer =>
/** Subclass of Application for type checking an Apply node with typed arguments. */
class ApplyToTyped(app: untpd.Apply, fun: Tree, methRef: TermRef, args: List[Tree], resultType: Type)(implicit ctx: Context)
- extends TypedApply(app, fun, methRef, args, resultType) {
- def typedArg(arg: Tree, formal: Type): TypedArg = arg
+ extends TypedApply[Type](app, fun, methRef, args, resultType) {
+ // Dotty deviation: Dotc infers Untyped for the supercall. This seems to be according to the rules
+ // (of both Scala and Dotty). Untyped is legal, and a subtype of Typed, whereas TypeApply
+ // is invariant in the type parameter, so the minimal type should be inferred. But then typedArg does
+ // not match the abstract method in Application and an abstract class error results.
+ def typedArg(arg: tpd.Tree, formal: Type): TypedArg = arg
def treeToArg(arg: Tree): Tree = arg
}
@@ -555,18 +559,32 @@ trait Applications extends Compatibility { self: Typer =>
/** A typed qual.unappy or qual.unappySeq tree, if this typechecks.
* Otherwise fallBack with (maltyped) qual.unapply as argument
+ * Note: requires special handling for overloaded occurrences of
+ * unapply or unapplySeq. We first try to find a non-overloaded
+ * method which matches any type. If that fails, we try to find an
+ * overloaded variant which matches one of the argument types.
+ * In fact, overloaded unapply's are problematic because a non-
+ * overloaded unapply does *not* need to be applicable to its argument
+ * whereas overloaded variants need to have a conforming variant.
*/
def trySelectUnapply(qual: untpd.Tree)(fallBack: Tree => Tree): Tree = {
- val unappProto = new UnapplyFunProto(this)
- tryEither {
- implicit ctx => typedExpr(untpd.Select(qual, nme.unapply), unappProto)
- } {
- (sel, _) =>
- tryEither {
- implicit ctx => typedExpr(untpd.Select(qual, nme.unapplySeq), unappProto) // for backwards compatibility; will be dropped
- } {
- (_, _) => fallBack(sel)
- }
+ val genericProto = new UnapplyFunProto(WildcardType, this)
+ def specificProto = new UnapplyFunProto(pt, this)
+ // try first for non-overloaded, then for overloaded ocurrences
+ def tryWithName(name: TermName)(fallBack: Tree => Tree)(implicit ctx: Context): Tree =
+ tryEither {
+ implicit ctx => typedExpr(untpd.Select(qual, name), genericProto)
+ } {
+ (sel, _) =>
+ tryEither {
+ implicit ctx => typedExpr(untpd.Select(qual, name), specificProto)
+ } {
+ (_, _) => fallBack(sel)
+ }
+ }
+ // try first for unapply, then for unapplySeq
+ tryWithName(nme.unapply) {
+ sel => tryWithName(nme.unapplySeq)(_ => fallBack(sel)) // for backwards compatibility; will be dropped
}
}
diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala
index 7da00e051367..eb202cc159d1 100644
--- a/src/dotty/tools/dotc/typer/Checking.scala
+++ b/src/dotty/tools/dotc/typer/Checking.scala
@@ -4,8 +4,16 @@ package typer
import core._
import ast._
-import Contexts._, Types._, Flags._, Denotations._, Names._, StdNames._, NameOps._, Symbols._
-import Trees._, ProtoTypes._
+import Contexts._
+import Types._
+import Flags._
+import Denotations._
+import Names._
+import StdNames._
+import NameOps._
+import Symbols._
+import Trees._
+import ProtoTypes._
import Constants._
import Scopes._
import annotation.unchecked
@@ -14,14 +22,158 @@ import util.{Stats, SimpleMap}
import util.common._
import Decorators._
import Uniques._
-import ErrorReporting.{errorType, DiagnosticString}
+import ErrorReporting.{err, errorType, DiagnosticString}
import config.Printers._
import collection.mutable
+import SymDenotations.NoCompleter
+
+object Checking {
+ import tpd._
+
+ /** A general checkBounds method that can be used for TypeApply nodes as
+ * well as for AppliedTypeTree nodes.
+ */
+ def checkBounds(args: List[tpd.Tree], bounds: List[TypeBounds], instantiate: (Type, List[Type]) => Type)(implicit ctx: Context) = {
+ val argTypes = args.tpes
+ for ((arg, bounds) <- args zip bounds) {
+ def notConforms(which: String, bound: Type) = {
+ ctx.error(
+ d"Type argument ${arg.tpe} does not conform to $which bound $bound ${err.whyNoMatchStr(arg.tpe, bound)}",
+ arg.pos)
+ }
+ def checkOverlapsBounds(lo: Type, hi: Type): Unit = {
+ //println(i"instantiating ${bounds.hi} with $argTypes")
+ //println(i" = ${instantiate(bounds.hi, argTypes)}")
+ val hiBound = instantiate(bounds.hi, argTypes)
+ if (!(lo <:< hiBound)) notConforms("upper", hiBound)
+ if (!(bounds.lo <:< hi)) notConforms("lower", bounds.lo)
+ }
+ arg.tpe match {
+ case TypeBounds(lo, hi) => checkOverlapsBounds(lo, hi)
+ case tp => checkOverlapsBounds(tp, tp)
+ }
+ }
+ }
+
+ /** A type map which checks that the only cycles in a type are F-bounds
+ * and that protects all F-bounded references by LazyRefs.
+ */
+ class CheckNonCyclicMap(sym: Symbol, reportErrors: Boolean)(implicit ctx: Context) extends TypeMap {
+
+ /** Are cycles allowed within nested refinedInfos of currently checked type? */
+ private var nestedCycleOK = false
+
+ /** Are cycles allwoed within currently checked type? */
+ private var cycleOK = false
+
+ /** A diagnostic output string that indicates the position of the last
+ * part of a type bounds checked by checkInfo. Possible choices:
+ * alias, lower bound, upper bound.
+ */
+ var where: String = ""
+
+ /** The last type top-level type checked when a CyclicReference occurs. */
+ var lastChecked: Type = NoType
+
+ /** Check info `tp` for cycles. Throw CyclicReference for illegal cycles,
+ * break direct cycle with a LazyRef for legal, F-bounded cycles.
+ */
+ def checkInfo(tp: Type): Type = tp match {
+ case tp @ TypeBounds(lo, hi) =>
+ if (lo eq hi)
+ try tp.derivedTypeAlias(apply(lo))
+ finally {
+ where = "alias"
+ lastChecked = lo
+ }
+ else {
+ val lo1 = try apply(lo) finally {
+ where = "lower bound"
+ lastChecked = lo
+ }
+ val saved = nestedCycleOK
+ nestedCycleOK = true
+ try tp.derivedTypeBounds(lo1, apply(hi))
+ finally {
+ nestedCycleOK = saved
+ where = "upper bound"
+ lastChecked = hi
+ }
+ }
+ case _ =>
+ tp
+ }
+
+ def apply(tp: Type) = tp match {
+ case tp @ RefinedType(parent, name) =>
+ val parent1 = this(parent)
+ val saved = cycleOK
+ cycleOK = nestedCycleOK
+ try tp.derivedRefinedType(parent1, name, this(tp.refinedInfo))
+ finally cycleOK = saved
+ case tp @ TypeRef(pre, name) =>
+ try {
+ // A prefix is interesting if it might contain (transitively) a reference
+ // to symbol `sym` itself. We only check references with interesting
+ // prefixes for cycles. This pruning is done in order not to force
+ // global symbols when doing the cyclicity check.
+ def isInteresting(prefix: Type): Boolean = prefix.stripTypeVar match {
+ case NoPrefix => true
+ case ThisType(cls) => sym.owner.isClass && cls.isContainedIn(sym.owner)
+ case prefix: NamedType => !prefix.symbol.isStaticOwner && isInteresting(prefix.prefix)
+ case SuperType(thistp, _) => isInteresting(thistp)
+ case AndType(tp1, tp2) => isInteresting(tp1) || isInteresting(tp2)
+ case OrType(tp1, tp2) => isInteresting(tp1) && isInteresting(tp2)
+ case _ => false
+ }
+ // If prefix is interesting, check info of typeref recursively, marking the referred symbol
+ // with NoCompleter. This provokes a CyclicReference when the symbol
+ // is hit again. Without this precaution we could stackoverflow here.
+ if (isInteresting(pre)) {
+ val info = tp.info
+ val symInfo = tp.symbol.info
+ if (tp.symbol.exists) tp.symbol.info = SymDenotations.NoCompleter
+ try checkInfo(info)
+ finally if (tp.symbol.exists) tp.symbol.info = symInfo
+ }
+ tp
+ } catch {
+ case ex: CyclicReference =>
+ ctx.debuglog(i"cycle detected for $tp, $nestedCycleOK, $cycleOK")
+ if (cycleOK) LazyRef(() => tp)
+ else if (reportErrors) throw ex
+ else tp
+ }
+ case _ => mapOver(tp)
+ }
+ }
+
+ /** Check that `info` of symbol `sym` is not cyclic.
+ * @pre sym is not yet initialized (i.e. its type is a Completer).
+ * @return `info` where every legal F-bounded reference is proctected
+ * by a `LazyRef`, or `ErrorType` if a cycle was detected and reported.
+ */
+ def checkNonCyclic(sym: Symbol, info: Type, reportErrors: Boolean)(implicit ctx: Context): Type = {
+ val checker = new CheckNonCyclicMap(sym, reportErrors)(ctx.withMode(Mode.CheckCyclic))
+ try checker.checkInfo(info)
+ catch {
+ case ex: CyclicReference =>
+ if (reportErrors) {
+ ctx.error(i"illegal cyclic reference: ${checker.where} ${checker.lastChecked} of $sym refers back to the type itself", sym.pos)
+ ErrorType
+ }
+ else info
+ }
+ }
+}
trait Checking {
import tpd._
+ def checkNonCyclic(sym: Symbol, info: TypeBounds, reportErrors: Boolean)(implicit ctx: Context): Type =
+ Checking.checkNonCyclic(sym, info, reportErrors)
+
/** Check that Java statics and packages can only be used in selections.
*/
def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = {
@@ -32,17 +184,13 @@ trait Checking {
tree
}
- /** Check that type arguments `args` conform to corresponding bounds in `poly` */
- def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = {
- val argTypes = args.tpes
- def substituted(tp: Type) = tp.substParams(poly, argTypes)
- for ((arg, bounds) <- args zip poly.paramBounds) {
- def notConforms(which: String, bound: Type) =
- ctx.error(d"Type argument ${arg.tpe} does not conform to $which bound $bound", arg.pos)
- if (!(arg.tpe <:< substituted(bounds.hi))) notConforms("upper", bounds.hi)
- if (!(bounds.lo <:< arg.tpe)) notConforms("lower", bounds.lo)
- }
- }
+ /** Check that type arguments `args` conform to corresponding bounds in `poly`
+ * Note: This does not check the bounds of AppliedTypeTrees. These
+ * are handled by method checkBounds in FirstTransform
+ * TODO: remove pos parameter
+ */
+ def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = Checking.checkBounds(
+ args, poly.paramBounds, (tp, argTypes) => tp.substParams(poly, argTypes))
/** Check that type `tp` is stable. */
def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit =
@@ -136,6 +284,7 @@ trait Checking {
trait NoChecking extends Checking {
import tpd._
+ override def checkNonCyclic(sym: Symbol, info: TypeBounds, reportErrors: Boolean)(implicit ctx: Context): Type = info
override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree
override def checkBounds(args: List[tpd.Tree], poly: PolyType, pos: Position)(implicit ctx: Context): Unit = ()
override def checkStable(tp: Type, pos: Position)(implicit ctx: Context): Unit = ()
diff --git a/src/dotty/tools/dotc/typer/ErrorReporting.scala b/src/dotty/tools/dotc/typer/ErrorReporting.scala
index f20d25792bb7..1f55df2bcb64 100644
--- a/src/dotty/tools/dotc/typer/ErrorReporting.scala
+++ b/src/dotty/tools/dotc/typer/ErrorReporting.scala
@@ -97,19 +97,21 @@ object ErrorReporting {
errorTree(tree, typeMismatchStr(tree.tpe, pt) + implicitFailure.postscript)
}
+ /** A subtype log explaining why `found` does not conform to `expected` */
+ def whyNoMatchStr(found: Type, expected: Type) =
+ if (ctx.settings.explaintypes.value)
+ "\n" + ctx.typerState.show + "\n" + TypeComparer.explained((found <:< expected)(_))
+ else
+ ""
+
def typeMismatchStr(found: Type, expected: Type) = disambiguated { implicit ctx =>
- val (typerStateStr, explanationStr) =
- if (ctx.settings.explaintypes.value)
- ("\n" + ctx.typerState.show, "\n" + TypeComparer.explained((found <:< expected)(_)))
- else
- ("", "")
def infoStr = found match { // DEBUG
case tp: TypeRef => s"with info ${tp.info} / ${tp.prefix.toString} / ${tp.prefix.dealias.toString}"
case _ => ""
}
d"""type mismatch:
| found : $found
- | required: $expected""".stripMargin + typerStateStr + explanationStr
+ | required: $expected""".stripMargin + whyNoMatchStr(found, expected)
}
}
diff --git a/src/dotty/tools/dotc/typer/Mode.scala b/src/dotty/tools/dotc/typer/Mode.scala
index 55baa6bc5ea2..54487722a641 100644
--- a/src/dotty/tools/dotc/typer/Mode.scala
+++ b/src/dotty/tools/dotc/typer/Mode.scala
@@ -33,6 +33,7 @@ object Mode {
val TypevarsMissContext = newMode(4, "TypevarsMissContext")
val InSuperCall = newMode(5, "InSuperCall")
+ val CheckCyclic = newMode(6, "CheckCyclic")
val PatternOrType = Pattern | Type
}
\ No newline at end of file
diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala
index 14404e220038..1002abe4d35a 100644
--- a/src/dotty/tools/dotc/typer/Namer.scala
+++ b/src/dotty/tools/dotc/typer/Namer.scala
@@ -674,9 +674,11 @@ class Namer { typer: Typer =>
if (needsLambda) rhsType.LambdaAbstract(tparamSyms)
else if (toParameterize) rhsType.parameterizeWith(tparamSyms)
else rhsType
- rhsType match {
- case _: TypeBounds => abstractedRhsType
+ val unsafeInfo = rhsType match {
+ case _: TypeBounds => abstractedRhsType.asInstanceOf[TypeBounds]
case _ => TypeAlias(abstractedRhsType, if (sym is Local) sym.variance else 0)
}
+ sym.info = NoCompleter
+ checkNonCyclic(sym, unsafeInfo, reportErrors = true)
}
}
\ No newline at end of file
diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala
index 19d8d68953ae..0aa0aa53814f 100644
--- a/src/dotty/tools/dotc/typer/ProtoTypes.scala
+++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala
@@ -257,8 +257,8 @@ object ProtoTypes {
unique(new CachedViewProto(argType, resultType))
}
- class UnapplyFunProto(typer: Typer)(implicit ctx: Context) extends FunProto(
- untpd.TypedSplice(dummyTreeOfType(WildcardType)) :: Nil, WildcardType, typer)
+ class UnapplyFunProto(argType: Type, typer: Typer)(implicit ctx: Context) extends FunProto(
+ untpd.TypedSplice(dummyTreeOfType(argType)) :: Nil, WildcardType, typer)
/** A prototype for expressions [] that are type-parameterized:
*
diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala
new file mode 100644
index 000000000000..67cb1745f6cc
--- /dev/null
+++ b/src/dotty/tools/dotc/typer/RefChecks.scala
@@ -0,0 +1,1402 @@
+package dotty.tools.dotc
+package typer
+
+import transform._
+import core._
+import config._
+import Symbols._, SymDenotations._, Types._, Contexts._, Decorators._, Flags._, Names._, NameOps._
+import StdNames._, Denotations._, Scopes._, Constants.Constant
+import Annotations._
+import util.Positions._
+import scala.collection.{ mutable, immutable }
+import ast._
+import Trees._
+import TreeTransforms._
+import util.DotClass
+import scala.util.{Try, Success, Failure}
+import config.{ScalaVersion, NoScalaVersion}
+import typer.ErrorReporting._
+import DenotTransformers._
+import ValueClasses.isDerivedValueClass
+
+object RefChecks {
+ import tpd._
+
+ private def isDefaultGetter(name: Name): Boolean =
+ name.isTermName && name.asTermName.defaultGetterIndex >= 0
+
+ private val defaultMethodFilter = new NameFilter {
+ def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = isDefaultGetter(name)
+ }
+
+ private val AnyOverride = Override | AbsOverride
+ private val AnyOverrideOrSynthetic = AnyOverride | Synthetic
+
+ /** Only one overloaded alternative is allowed to define default arguments */
+ private def checkOverloadedRestrictions(clazz: Symbol)(implicit ctx: Context): Unit = {
+ // Using the default getters (such as methodName$default$1) as a cheap way of
+ // finding methods with default parameters. This way, we can limit the members to
+ // those with the DEFAULTPARAM flag, and infer the methods. Looking for the methods
+ // directly requires inspecting the parameter list of every one. That modification
+ // shaved 95% off the time spent in this method.
+
+ for (
+ defaultGetterClass <- List(clazz, clazz.companionModule.moduleClass);
+ if defaultGetterClass.isClass
+ ) {
+ val defaultGetterNames = defaultGetterClass.asClass.memberNames(defaultMethodFilter)
+ val defaultMethodNames = defaultGetterNames map (_.asTermName.defaultGetterToMethod)
+
+ for (name <- defaultMethodNames) {
+ val methods = clazz.info.member(name).alternatives.map(_.symbol)
+ val haveDefaults = methods.filter(_.hasDefaultParams)
+ if (haveDefaults.length > 1) {
+ val owners = haveDefaults map (_.owner)
+ // constructors of different classes are allowed to have defaults
+ if (haveDefaults.exists(x => !x.isConstructor) || owners.distinct.size < haveDefaults.size)
+ ctx.error(
+ "in " + clazz +
+ ", multiple overloaded alternatives of " + haveDefaults.head +
+ " define default arguments" + (
+ if (owners.forall(_ == clazz)) "."
+ else ".\nThe members with defaults are defined in " + owners.map(_.showLocated).mkString("", " and ", ".")),
+ clazz.pos)
+ }
+ }
+ }
+
+ // Check for doomed attempt to overload applyDynamic
+ if (clazz derivesFrom defn.DynamicClass) {
+ for ((_, m1 :: m2 :: _) <- (clazz.info member nme.applyDynamic).alternatives groupBy (_.symbol.typeParams.length)) {
+ ctx.error("implementation restriction: applyDynamic cannot be overloaded except by methods with different numbers of type parameters, e.g. applyDynamic[T1](method: String)(arg: T1) and applyDynamic[T1, T2](method: String)(arg1: T1, arg2: T2)",
+ m1.symbol.pos)
+ }
+ }
+ }
+
+ // Override checking ------------------------------------------------------------
+
+ /** 1. Check all members of class `clazz` for overriding conditions.
+ * That is for overriding member M and overridden member O:
+ *
+ * 1.1. M must have the same or stronger access privileges as O.
+ * 1.2. O must not be final.
+ * 1.3. O is deferred, or M has `override` modifier.
+ * 1.4. If O is stable, then so is M.
+ * // @M: LIFTED 1.5. Neither M nor O are a parameterized type alias
+ * 1.6. If O is a type alias, then M is an alias of O.
+ * 1.7. If O is an abstract type then
+ * 1.7.1 either M is an abstract type, and M's bounds are sharper than O's bounds.
+ * or M is a type alias or class which conforms to O's bounds.
+ * 1.7.2 higher-order type arguments must respect bounds on higher-order type parameters -- @M
+ * (explicit bounds and those implied by variance annotations) -- @see checkKindBounds
+ * 1.8. If O and M are values, then
+ * 1.8.1 M's type is a subtype of O's type, or
+ * 1.8.2 M is of type []S, O is of type ()T and S <: T, or
+ * 1.8.3 M is of type ()S, O is of type []T and S <: T, or
+ * 1.9. If M is a macro def, O cannot be deferred unless there's a concrete method overriding O.
+ * 1.10. If M is not a macro def, O cannot be a macro def.
+ * 2. Check that only abstract classes have deferred members
+ * 3. Check that concrete classes do not have deferred definitions
+ * that are not implemented in a subclass.
+ * 4. Check that every member with an `override` modifier
+ * overrides some other member.
+ * TODO check that classes are not overridden
+ * TODO This still needs to be cleaned up; the current version is a staright port of what was there
+ * before, but it looks too complicated and method bodies are far too large.
+ */
+ private def checkAllOverrides(clazz: Symbol)(implicit ctx: Context): Unit = {
+ val self = clazz.thisType
+
+ case class MixinOverrideError(member: Symbol, msg: String)
+
+ val mixinOverrideErrors = new mutable.ListBuffer[MixinOverrideError]()
+
+ def printMixinOverrideErrors(): Unit = {
+ mixinOverrideErrors.toList match {
+ case List() =>
+ case List(MixinOverrideError(_, msg)) =>
+ ctx.error(msg, clazz.pos)
+ case MixinOverrideError(member, msg) :: others =>
+ val others1 = others.map(_.member).filter(_.name != member.name).distinct
+ def othersMsg = {
+ val others1 = others.map(_.member)
+ .filter(_.name != member.name)
+ .map(_.show).distinct
+ if (others1.isEmpty) ""
+ else i";\n other members with override errors are:: $others1%, %"
+ }
+ ctx.error(msg + othersMsg, clazz.pos)
+ }
+ }
+
+ def infoString(sym: Symbol) = infoString0(sym, sym.owner != clazz)
+ def infoStringWithLocation(sym: Symbol) = infoString0(sym, true)
+
+ def infoString0(sym: Symbol, showLocation: Boolean) = {
+ val sym1 = sym.underlyingSymbol
+ if (showLocation) sym1.showLocated
+ else
+ sym1.show +
+ (if (sym1.isAliasType) ", which equals " + self.memberInfo(sym1)
+ else if (sym1.isAbstractType) " with bounds" + self.memberInfo(sym1)
+ else if (sym1.is(Module)) ""
+ else if (sym1.isTerm) " of type " + self.memberInfo(sym1)
+ else "")
+ }
+
+ /* Check that all conditions for overriding `other` by `member`
+ * of class `clazz` are met.
+ */
+ def checkOverride(member: Symbol, other: Symbol): Unit = {
+ def memberTp = self.memberInfo(member)
+ def otherTp = self.memberInfo(other)
+
+ ctx.debuglog("Checking validity of %s overriding %s".format(member.showLocated, other.showLocated))
+
+ def noErrorType = !memberTp.isErroneous && !otherTp.isErroneous
+
+ def overrideErrorMsg(msg: String): String = {
+ val isConcreteOverAbstract =
+ (other.owner isSubClass member.owner) && other.is(Deferred) && !member.is(Deferred)
+ val addendum =
+ if (isConcreteOverAbstract)
+ ";\n (Note that %s is abstract,\n and is therefore overridden by concrete %s)".format(
+ infoStringWithLocation(other),
+ infoStringWithLocation(member))
+ else if (ctx.settings.debug.value)
+ err.typeMismatchStr(memberTp, otherTp)
+ else ""
+
+ "overriding %s;\n %s %s%s".format(
+ infoStringWithLocation(other), infoString(member), msg, addendum)
+ }
+ def emitOverrideError(fullmsg: String) = {
+ if (member.owner == clazz) ctx.error(fullmsg, member.pos)
+ else mixinOverrideErrors += new MixinOverrideError(member, fullmsg)
+ }
+
+ def overrideError(msg: String) = {
+ if (noErrorType)
+ emitOverrideError(overrideErrorMsg(msg))
+ }
+
+ def overrideTypeError() = {
+ if (noErrorType) {
+ emitOverrideError(overrideErrorMsg("has incompatible type"))
+ }
+ }
+
+ def overrideAccessError() = {
+ ctx.log(i"member: ${member.showLocated} ${member.flags}") // DEBUG
+ ctx.log(i"other: ${other.showLocated} ${other.flags}") // DEBUG
+ val otherAccess = (other.flags & AccessFlags).toString
+ overrideError("has weaker access privileges; it should be " +
+ (if (otherAccess == "") "public" else "at least " + otherAccess))
+ }
+
+ //Console.println(infoString(member) + " overrides " + infoString(other) + " in " + clazz);//DEBUG
+
+ // return if we already checked this combination elsewhere
+ if (member.owner != clazz) {
+ def deferredCheck = member.is(Deferred) || !other.is(Deferred)
+ def subOther(s: Symbol) = s derivesFrom other.owner
+ def subMember(s: Symbol) = s derivesFrom member.owner
+
+ if (subOther(member.owner) && deferredCheck) {
+ //Console.println(infoString(member) + " shadows1 " + infoString(other) " in " + clazz);//DEBUG
+ return
+ }
+ val parentSymbols = clazz.info.parents.map(_.typeSymbol)
+ if (parentSymbols exists (p => subOther(p) && subMember(p) && deferredCheck)) {
+ //Console.println(infoString(member) + " shadows2 " + infoString(other) + " in " + clazz);//DEBUG
+ return
+ }
+ if (parentSymbols forall (p => subOther(p) == subMember(p))) {
+ //Console.println(infoString(member) + " shadows " + infoString(other) + " in " + clazz);//DEBUG
+ return
+ }
+ }
+
+ /* Is the intersection between given two lists of overridden symbols empty? */
+ def intersectionIsEmpty(syms1: Iterator[Symbol], syms2: Iterator[Symbol]) =
+ !(syms1 exists (syms2 contains _))
+
+ // o: public | protected | package-protected (aka java's default access)
+ // ^-may be overridden by member with access privileges-v
+ // m: public | public/protected | public/protected/package-protected-in-same-package-as-o
+
+ if (member.is(Private)) // (1.1)
+ overrideError("has weaker access privileges; it should not be private")
+
+ // todo: align accessibility implication checking with isAccessible in Contexts
+ val ob = other.accessBoundary(member.owner)
+ val mb = member.accessBoundary(member.owner)
+ def isOverrideAccessOK = (
+ (member.flags & AccessFlags).isEmpty // member is public
+ || // - or -
+ (!other.is(Protected) || member.is(Protected)) && // if o is protected, so is m, and
+ (ob.isContainedIn(mb) || other.is(JavaProtected)) // m relaxes o's access boundary,
+ // or o is Java defined and protected (see #3946)
+ )
+ if (!isOverrideAccessOK) {
+ overrideAccessError()
+ } else if (other.isClass) {
+ overrideError("cannot be used here - class definitions cannot be overridden")
+ } else if (!other.is(Deferred) && member.isClass) {
+ overrideError("cannot be used here - classes can only override abstract types")
+ } else if (other.isEffectivelyFinal) { // (1.2)
+ overrideError("cannot override final member")
+ } else if (!other.is(Deferred) && !isDefaultGetter(other.name) && !member.is(AnyOverrideOrSynthetic)) {
+ // (*) Synthetic exclusion for (at least) default getters, fixes SI-5178. We cannot assign the OVERRIDE flag to
+ // the default getter: one default getter might sometimes override, sometimes not. Example in comment on ticket.
+ if (member.owner != clazz && other.owner != clazz && !(other.owner derivesFrom member.owner))
+ emitOverrideError(
+ clazz + " inherits conflicting members:\n "
+ + infoStringWithLocation(other) + " and\n " + infoStringWithLocation(member)
+ + "\n(Note: this can be resolved by declaring an override in " + clazz + ".)")
+ else
+ overrideError("needs `override' modifier")
+ } else if (other.is(AbsOverride) && other.isIncompleteIn(clazz) && !member.is(AbsOverride)) {
+ overrideError("needs `abstract override' modifiers")
+ } else if (member.is(AnyOverride) && other.is(Accessor) &&
+ other.accessedFieldOrGetter.is(Mutable, butNot = Lazy)) {
+ // !?! this is not covered by the spec. We need to resolve this either by changing the spec or removing the test here.
+ // !!! is there a !?! convention? I'm !!!ing this to make sure it turns up on my searches.
+ if (!ctx.settings.overrideVars.value)
+ overrideError("cannot override a mutable variable")
+ } else if (member.is(AnyOverride) &&
+ !(member.owner.thisType.baseClasses exists (_ isSubClass other.owner)) &&
+ !member.is(Deferred) && !other.is(Deferred) &&
+ intersectionIsEmpty(member.extendedOverriddenSymbols, other.extendedOverriddenSymbols)) {
+ overrideError("cannot override a concrete member without a third member that's overridden by both " +
+ "(this rule is designed to prevent ``accidental overrides'')")
+ } else if (other.isStable && !member.isStable) { // (1.4)
+ overrideError("needs to be a stable, immutable value")
+ } else if (member.is(Lazy) && !other.isSourceMethod && !other.is(Deferred | Lazy)) {
+ overrideError("cannot override a concrete non-lazy value")
+ } else if (other.is(Lazy, butNot = Deferred) && !other.isSourceMethod && !member.is(Lazy)) {
+ overrideError("must be declared lazy to override a concrete lazy value")
+ } else if (other.is(Deferred) && member.is(Macro) && member.extendedOverriddenSymbols.forall(_.is(Deferred))) { // (1.9)
+ overrideError("cannot be used here - term macros cannot override abstract methods")
+ } else if (other.is(Macro) && !member.is(Macro)) { // (1.10)
+ overrideError("cannot be used here - only term macros can override term macros")
+ } else {
+ checkOverrideDeprecated()
+ }
+ }
+
+ /* TODO enable; right now the annotation is scala-private, so cannot be seen
+ * here.
+ */
+ def checkOverrideDeprecated() = { /*
+ if (other.hasDeprecatedOverridingAnnotation) {
+ val suffix = other.deprecatedOverridingMessage map (": " + _) getOrElse ""
+ val msg = s"overriding ${other.fullLocationString} is deprecated$suffix"
+ unit.deprecationWarning(member.pos, msg)
+ }*/
+ }
+
+ val opc = new OverridingPairs.Cursor(clazz)
+ while (opc.hasNext) {
+ checkOverride(opc.overriding, opc.overridden)
+ opc.next()
+ }
+ printMixinOverrideErrors()
+
+ // Verifying a concrete class has nothing unimplemented.
+ if (!clazz.is(AbstractOrTrait)) {
+ val abstractErrors = new mutable.ListBuffer[String]
+ def abstractErrorMessage =
+ // a little formatting polish
+ if (abstractErrors.size <= 2) abstractErrors mkString " "
+ else abstractErrors.tail.mkString(abstractErrors.head + ":\n", "\n", "")
+
+ def abstractClassError(mustBeMixin: Boolean, msg: String): Unit = {
+ def prelude = (
+ if (clazz.isAnonymousClass || clazz.is(Module)) "object creation impossible"
+ else if (mustBeMixin) clazz + " needs to be a mixin"
+ else clazz + " needs to be abstract") + ", since"
+
+ if (abstractErrors.isEmpty) abstractErrors ++= List(prelude, msg)
+ else abstractErrors += msg
+ }
+
+ def hasJavaErasedOverriding(sym: Symbol): Boolean =
+ !ctx.erasurePhase.exists || // can't do the test, assume the best
+ ctx.atPhase(ctx.erasurePhase.next) { implicit ctx =>
+ clazz.info.nonPrivateMember(sym.name).hasAltWith { alt =>
+ alt.symbol.is(JavaDefined, butNot = Deferred) &&
+ !sym.owner.derivesFrom(alt.symbol.owner) &&
+ alt.signature.matches(sym.signature)
+ }
+ }
+
+ def ignoreDeferred(member: SingleDenotation) =
+ member.isType ||
+ member.symbol.is(JavaDefined) && hasJavaErasedOverriding(member.symbol)
+
+ // 2. Check that only abstract classes have deferred members
+ def checkNoAbstractMembers(): Unit = {
+ // Avoid spurious duplicates: first gather any missing members.
+ val missing = clazz.thisType.abstractTermMembers.filterNot(ignoreDeferred)
+ // Group missing members by the name of the underlying symbol,
+ // to consolidate getters and setters.
+ val grouped: Map[Name, Seq[SingleDenotation]] = missing groupBy (_.symbol.underlyingSymbol.name)
+ // Dotty deviation: Added type annotation for `grouped`.
+ // The inferred type is Map[Symbol#ThisName, Seq[SingleDenotation]]
+ // but then the definition of isMultiple fails with an error:
+ // RefChecks.scala:379: error: type mismatch:
+ // found : underlying.ThisName
+ // required: dotty.tools.dotc.core.Symbols.Symbol#ThisName
+ //
+ // val isMultiple = grouped.getOrElse(underlying.name(ctx), Nil).size > 1
+ // ^
+ // As far as I can see, the complaint is correct, even under the
+ // old reading where Symbol#ThisName means x.ThisName forSome { val x }
+
+ val missingMethods = grouped.toList flatMap {
+ case (name, syms) =>
+ val withoutSetters = syms filterNot (_.symbol.isSetter)
+ if (withoutSetters.nonEmpty) withoutSetters else syms
+ }
+
+ def stubImplementations: List[String] = {
+ // Grouping missing methods by the declaring class
+ val regrouped = missingMethods.groupBy(_.symbol.owner).toList
+ def membersStrings(members: List[SingleDenotation]) =
+ members.sortBy(_.symbol.name.toString).map(_.showDcl + " = ???")
+
+ if (regrouped.tail.isEmpty)
+ membersStrings(regrouped.head._2)
+ else (regrouped.sortBy("" + _._1.name) flatMap {
+ case (owner, members) =>
+ ("// Members declared in " + owner.fullName) +: membersStrings(members) :+ ""
+ }).init
+ }
+
+ // If there are numerous missing methods, we presume they are aware of it and
+ // give them a nicely formatted set of method signatures for implementing.
+ if (missingMethods.size > 1) {
+ abstractClassError(false, "it has " + missingMethods.size + " unimplemented members.")
+ val preface =
+ """|/** As seen from %s, the missing signatures are as follows.
+ | * For convenience, these are usable as stub implementations.
+ | */
+ |""".stripMargin.format(clazz)
+ abstractErrors += stubImplementations.map(" " + _ + "\n").mkString(preface, "", "")
+ return
+ }
+
+ for (member <- missing) {
+ val memberSym = member.symbol
+ def undefined(msg: String) = abstractClassError(false, member.showDcl + " is not defined" + msg)
+ val underlying = memberSym.underlyingSymbol
+
+ // Give a specific error message for abstract vars based on why it fails:
+ // It could be unimplemented, have only one accessor, or be uninitialized.
+ if (underlying.is(Mutable)) {
+ val isMultiple = grouped.getOrElse(underlying.name(ctx), Nil).size > 1
+
+ // If both getter and setter are missing, squelch the setter error.
+ if (memberSym.isSetter && isMultiple) ()
+ else undefined(
+ if (memberSym.isSetter) "\n(Note that an abstract var requires a setter in addition to the getter)"
+ else if (memberSym.isGetter && !isMultiple) "\n(Note that an abstract var requires a getter in addition to the setter)"
+ else err.abstractVarMessage(memberSym))
+ } else if (underlying.is(Method)) {
+ // If there is a concrete method whose name matches the unimplemented
+ // abstract method, and a cursory examination of the difference reveals
+ // something obvious to us, let's make it more obvious to them.
+ val abstractParams = underlying.info.firstParamTypes
+ val matchingName = clazz.info.member(underlying.name).alternatives
+ val matchingArity = matchingName filter { m =>
+ !m.symbol.is(Deferred) &&
+ m.info.firstParamTypes.length == abstractParams.length
+ }
+
+ matchingArity match {
+ // So far so good: only one candidate method
+ case concrete :: Nil =>
+ val mismatches =
+ abstractParams.zip(concrete.info.firstParamTypes)
+ .filterNot { case (x, y) => x =:= y }
+ mismatches match {
+ // Only one mismatched parameter: say something useful.
+ case (pa, pc) :: Nil =>
+ val abstractSym = pa.typeSymbol
+ val concreteSym = pc.typeSymbol
+ def subclassMsg(c1: Symbol, c2: Symbol) = (
+ ": %s is a subclass of %s, but method parameter types must match exactly.".format(
+ c1.showLocated, c2.showLocated))
+ val addendum =
+ if (abstractSym == concreteSym) {
+ val paArgs = pa.argInfos
+ val pcArgs = pc.argInfos
+ val paConstr = pa.withoutArgs(paArgs)
+ val pcConstr = pc.withoutArgs(pcArgs)
+ (paConstr, pcConstr) match {
+ case (TypeRef(pre1, _), TypeRef(pre2, _)) =>
+ if (pre1 =:= pre2) ": their type parameters differ"
+ else ": their prefixes (i.e. enclosing instances) differ"
+ case _ =>
+ ""
+ }
+ } else if (abstractSym isSubClass concreteSym)
+ subclassMsg(abstractSym, concreteSym)
+ else if (concreteSym isSubClass abstractSym)
+ subclassMsg(concreteSym, abstractSym)
+ else ""
+
+ undefined("\n(Note that %s does not match %s%s)".format(pa, pc, addendum))
+ case xs =>
+ undefined("")
+ }
+ case _ =>
+ undefined("")
+ }
+ } else undefined("")
+ }
+ }
+
+ // 3. Check that concrete classes do not have deferred definitions
+ // that are not implemented in a subclass.
+ // Note that this is not the same as (2); In a situation like
+ //
+ // class C { def m: Int = 0}
+ // class D extends C { def m: Int }
+ //
+ // (3) is violated but not (2).
+ def checkNoAbstractDecls(bc: Symbol): Unit = {
+ for (decl <- bc.info.decls) {
+ if (decl.is(Deferred) && !ignoreDeferred(decl)) {
+ val impl = decl.matchingMember(clazz.thisType)
+ if (impl == NoSymbol || (decl.owner isSubClass impl.owner)) {
+ val impl1 = clazz.thisType.nonPrivateMember(decl.name) // DEBUG
+ ctx.log(i"${impl1}: ${impl1.info}") // DEBUG
+ ctx.log(i"${clazz.thisType.memberInfo(decl)}") // DEBUG
+ abstractClassError(false, "there is a deferred declaration of " + infoString(decl) +
+ " which is not implemented in a subclass" + err.abstractVarMessage(decl))
+ }
+ }
+ }
+ if (bc.asClass.superClass.is(Abstract))
+ checkNoAbstractDecls(bc.asClass.superClass)
+ }
+
+ checkNoAbstractMembers()
+ if (abstractErrors.isEmpty)
+ checkNoAbstractDecls(clazz)
+
+ if (abstractErrors.nonEmpty)
+ ctx.error(abstractErrorMessage, clazz.pos)
+ } else if (clazz.is(Trait) && !(clazz derivesFrom defn.AnyValClass)) {
+ // For non-AnyVal classes, prevent abstract methods in interfaces that override
+ // final members in Object; see #4431
+ for (decl <- clazz.info.decls) {
+ // Have to use matchingSymbol, not a method involving overridden symbols,
+ // because the scala type system understands that an abstract method here does not
+ // override a concrete method in Object. The jvm, however, does not.
+ val overridden = decl.matchingDecl(defn.ObjectClass, defn.ObjectType)
+ if (overridden.is(Final))
+ ctx.error("trait cannot redefine final method from class AnyRef", decl.pos)
+ }
+ }
+
+ /* Returns whether there is a symbol declared in class `inclazz`
+ * (which must be different from `clazz`) whose name and type
+ * seen as a member of `class.thisType` matches `member`'s.
+ */
+ def hasMatchingSym(inclazz: Symbol, member: Symbol): Boolean = {
+
+ def isSignatureMatch(sym: Symbol) = !sym.isTerm ||
+ clazz.thisType.memberInfo(sym).matchesLoosely(member.info)
+
+ /* The rules for accessing members which have an access boundary are more
+ * restrictive in java than scala. Since java has no concept of package nesting,
+ * a member with "default" (package-level) access can only be accessed by members
+ * in the exact same package. Example:
+ *
+ * package a.b;
+ * public class JavaClass { void foo() { } }
+ *
+ * The member foo() can be accessed only from members of package a.b, and not
+ * nested packages like a.b.c. In the analogous scala class:
+ *
+ * package a.b
+ * class ScalaClass { private[b] def foo() = () }
+ *
+ * The member IS accessible to classes in package a.b.c. The javaAccessCheck logic
+ * is restricting the set of matching signatures according to the above semantics.
+ */
+ def javaAccessCheck(sym: Symbol) = (
+ !inclazz.is(JavaDefined) // not a java defined member
+ || !sym.privateWithin.exists // no access boundary
+ || sym.is(Protected) // marked protected in java, thus accessible to subclasses
+ || sym.privateWithin == member.enclosingPackageClass // exact package match
+ )
+ def classDecls = inclazz.info.nonPrivateDecl(member.name)
+
+ (inclazz != clazz) &&
+ classDecls.hasAltWith(d => isSignatureMatch(d.symbol) && javaAccessCheck(d.symbol))
+ }
+
+ // 4. Check that every defined member with an `override` modifier overrides some other member.
+ for (member <- clazz.info.decls)
+ if (member.is(AnyOverride) && !(clazz.thisType.baseClasses exists (hasMatchingSym(_, member)))) {
+ // for (bc <- clazz.info.baseClasses.tail) Console.println("" + bc + " has " + bc.info.decl(member.name) + ":" + bc.info.decl(member.name).tpe);//DEBUG
+
+ val nonMatching = clazz.info.member(member.name).altsWith(alt => alt.owner != clazz && !alt.is(Final))
+ def issueError(suffix: String) =
+ ctx.error(i"$member overrides nothing$suffix", member.pos)
+ nonMatching match {
+ case Nil =>
+ issueError("")
+ case ms =>
+ val superSigs = ms.map(_.showDcl).mkString("\n")
+ issueError(s".\nNote: the super classes of ${member.owner} contain the following, non final members named ${member.name}:\n${superSigs}")
+ }
+ member.resetFlag(AnyOverride)
+ }
+ }
+
+ // Note: if a symbol has both @deprecated and @migration annotations and both
+ // warnings are enabled, only the first one checked here will be emitted.
+ // I assume that's a consequence of some code trying to avoid noise by suppressing
+ // warnings after the first, but I think it'd be better if we didn't have to
+ // arbitrarily choose one as more important than the other.
+ private def checkUndesiredProperties(sym: Symbol, pos: Position)(implicit ctx: Context): Unit = {
+ // If symbol is deprecated, and the point of reference is not enclosed
+ // in either a deprecated member or a scala bridge method, issue a warning.
+ if (sym.isDeprecated && !ctx.owner.ownersIterator.exists(_.isDeprecated)) {
+ ctx.deprecationWarning("%s%s is deprecated%s".format(
+ sym, sym.showLocated, sym.deprecationMessage map (": " + _) getOrElse "", pos))
+ }
+ // Similar to deprecation: check if the symbol is marked with @migration
+ // indicating it has changed semantics between versions.
+ if (sym.hasAnnotation(defn.MigrationAnnot) && ctx.settings.Xmigration.value != NoScalaVersion) {
+ val symVersion: scala.util.Try[ScalaVersion] = sym.migrationVersion.get
+ val changed = symVersion match {
+ case scala.util.Success(v) =>
+ ctx.settings.Xmigration.value < v
+ case Failure(ex) =>
+ ctx.warning(s"${sym.showLocated} has an unparsable version number: ${ex.getMessage()}", pos)
+ false
+ }
+ if (changed)
+ ctx.warning(s"${sym.showLocated} has changed semantics in version $symVersion:\n${sym.migrationMessage.get}")
+ }
+ /* (Not enabled yet)
+ * See an explanation of compileTimeOnly in its scaladoc at scala.annotation.compileTimeOnly.
+ *
+ if (sym.isCompileTimeOnly) {
+ def defaultMsg =
+ sm"""Reference to ${sym.fullLocationString} should not have survived past type checking,
+ |it should have been processed and eliminated during expansion of an enclosing macro."""
+ // The getOrElse part should never happen, it's just here as a backstop.
+ ctx.error(sym.compileTimeOnlyMessage getOrElse defaultMsg, pos)
+ }*/
+ }
+
+ /** Check that a deprecated val or def does not override a
+ * concrete, non-deprecated method. If it does, then
+ * deprecation is meaningless.
+ */
+ private def checkDeprecatedOvers(tree: Tree)(implicit ctx: Context): Unit = {
+ val symbol = tree.symbol
+ if (symbol.isDeprecated) {
+ val concrOvers =
+ symbol.allOverriddenSymbols.filter(sym =>
+ !sym.isDeprecated && !sym.is(Deferred))
+ if (!concrOvers.isEmpty)
+ ctx.deprecationWarning(
+ symbol.toString + " overrides concrete, non-deprecated symbol(s):" +
+ concrOvers.map(_.name.decode).mkString(" ", ", ", ""), tree.pos)
+ }
+ }
+
+ /** Verify classes extending AnyVal meet the requirements */
+ private def checkAnyValSubclass(clazz: Symbol)(implicit ctx: Context) =
+ if (isDerivedValueClass(clazz)) {
+ if (clazz.is(Trait))
+ ctx.error("Only classes (not traits) are allowed to extend AnyVal", clazz.pos)
+ else if (clazz.is(Abstract))
+ ctx.error("`abstract' modifier cannot be used with value classes", clazz.pos)
+ }
+
+ type LevelAndIndex = immutable.Map[Symbol, (LevelInfo, Int)]
+
+ class OptLevelInfo extends DotClass {
+ def levelAndIndex: LevelAndIndex = Map()
+ def enterReference(sym: Symbol, pos: Position): Unit = ()
+ }
+
+ /** A class to help in forward reference checking */
+ class LevelInfo(outerLevelAndIndex: LevelAndIndex, stats: List[Tree])(implicit ctx: Context)
+ extends OptLevelInfo {
+ override val levelAndIndex: LevelAndIndex =
+ ((outerLevelAndIndex, 0) /: stats) {(mi, stat) =>
+ val (m, idx) = mi
+ val m1 = stat match {
+ case stat: MemberDef => m.updated(stat.symbol, (this, idx))
+ case _ => m
+ }
+ (m1, idx + 1)
+ }._1
+ var maxIndex: Int = Int.MinValue
+ var refPos: Position = _
+ var refSym: Symbol = _
+
+ override def enterReference(sym: Symbol, pos: Position): Unit =
+ if (sym.exists && sym.owner.isTerm)
+ levelAndIndex.get(sym) match {
+ case Some((level, idx)) if (level.maxIndex < idx) =>
+ level.maxIndex = idx
+ level.refPos = pos
+ level.refSym = sym
+ case _ =>
+ }
+ }
+
+ val NoLevelInfo = new OptLevelInfo()
+}
+import RefChecks._
+
+/** Post-attribution checking and transformation.
+ *
+ * This phase performs the following checks.
+ *
+ * - only one overloaded alternative defines default arguments
+ * - applyDynamic methods are not overloaded
+ * - all overrides conform to rules laid down by `checkAllOverrides`.
+ * - any value classes conform to rules laid down by `checkAnyValSubClass`.
+ * - this(...) constructor calls do not forward reference other definitions in their block (not even lazy vals).
+ * - no forward reference in a local block jumps over a non-lazy val definition.
+ *
+ * It warns about references to symbols labeled deprecated or migration.
+ *
+ * It performs the following transformations:
+ *
+ * - if (true) A else B --> A
+ * if (false) A else B --> B
+ * - macro definitions are eliminated.
+ */
+class RefChecks extends MiniPhase with IdentityDenotTransformer { thisTransformer =>
+
+ import tpd._
+
+ val name: String = "refchecks"
+
+ val treeTransform = new Transform(NoLevelInfo)
+
+ class Transform(currentLevel: RefChecks.OptLevelInfo = RefChecks.NoLevelInfo) extends TreeTransform {
+ def phase = thisTransformer
+ override def prepareForStats(trees: List[Tree])(implicit ctx: Context) = {
+ // println(i"preparing for $trees%; %, owner = ${ctx.owner}")
+ if (ctx.owner.isTerm) new Transform(new LevelInfo(currentLevel.levelAndIndex, trees))
+ else this
+ }
+
+ override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = trees
+
+ override def transformValDef(tree: ValDef)(implicit ctx: Context, info: TransformerInfo) = {
+ checkDeprecatedOvers(tree)
+ val sym = tree.symbol
+ if (sym.exists && sym.owner.isTerm && !sym.is(Lazy))
+ currentLevel.levelAndIndex.get(sym) match {
+ case Some((level, symIdx)) if symIdx < level.maxIndex =>
+ ctx.debuglog("refsym = " + level.refSym)
+ ctx.error(s"forward reference extends over definition of $sym", level.refPos)
+ case _ =>
+ }
+ tree
+ }
+
+ override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo) = {
+ checkDeprecatedOvers(tree)
+ if (tree.symbol is Macro) EmptyTree else tree
+ }
+
+ override def transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = {
+ val cls = ctx.owner
+ checkOverloadedRestrictions(cls)
+ checkAllOverrides(cls)
+ checkAnyValSubclass(cls)
+ if (isDerivedValueClass(cls))
+ cls.primaryConstructor.makeNotPrivateAfter(NoSymbol, thisTransformer) // SI-6601, must be done *after* pickler!
+ tree
+ }
+
+ override def transformTypeTree(tree: TypeTree)(implicit ctx: Context, info: TransformerInfo) = {
+ if (!tree.original.isEmpty)
+ tree.tpe.foreachPart {
+ case tp: NamedType => checkUndesiredProperties(tp.symbol, tree.pos)
+ case _ =>
+ }
+ tree
+ }
+
+ override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = {
+ checkUndesiredProperties(tree.symbol, tree.pos)
+ currentLevel.enterReference(tree.symbol, tree.pos)
+ tree
+ }
+
+ override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = {
+ checkUndesiredProperties(tree.symbol, tree.pos)
+ tree
+ }
+
+ override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo) = {
+ if (isSelfConstrCall(tree)) {
+ assert(currentLevel.isInstanceOf[LevelInfo], ctx.owner + "/" + i"$tree")
+ val level = currentLevel.asInstanceOf[LevelInfo]
+ if (level.maxIndex > 0) {
+ // An implementation restriction to avoid VerifyErrors and lazyvals mishaps; see SI-4717
+ ctx.debuglog("refsym = " + level.refSym)
+ ctx.error("forward reference not allowed from self constructor invocation", level.refPos)
+ }
+ }
+ tree
+ }
+
+ override def transformIf(tree: If)(implicit ctx: Context, info: TransformerInfo) =
+ tree.cond.tpe match {
+ case ConstantType(value) => if (value.booleanValue) tree.thenp else tree.elsep
+ case _ => tree
+ }
+
+ override def transformNew(tree: New)(implicit ctx: Context, info: TransformerInfo) = {
+ currentLevel.enterReference(tree.tpe.typeSymbol, tree.pos)
+ tree
+ }
+ }
+}
+
+/* todo: rewrite and re-enable
+
+// Comparison checking -------------------------------------------------------
+
+ object normalizeAll extends TypeMap {
+ def apply(tp: Type) = mapOver(tp).normalize
+ }
+
+ def checkImplicitViewOptionApply(pos: Position, fn: Tree, args: List[Tree]): Unit = if (settings.lint) (fn, args) match {
+ case (tap@TypeApply(fun, targs), List(view: ApplyImplicitView)) if fun.symbol == currentRun.runDefinitions.Option_apply =>
+ unit.warning(pos, s"Suspicious application of an implicit view (${view.fun}) in the argument to Option.apply.") // SI-6567
+ case _ =>
+ }
+
+ private def isObjectOrAnyComparisonMethod(sym: Symbol) = sym match {
+ case Object_eq | Object_ne | Object_== | Object_!= | Any_== | Any_!= => true
+ case _ => false
+ }
+ /** Check the sensibility of using the given `equals` to compare `qual` and `other`. */
+ private def checkSensibleEquals(pos: Position, qual: Tree, name: Name, sym: Symbol, other: Tree) = {
+ def isReferenceOp = sym == Object_eq || sym == Object_ne
+ def isNew(tree: Tree) = tree match {
+ case Function(_, _) | Apply(Select(New(_), nme.CONSTRUCTOR), _) => true
+ case _ => false
+ }
+ def underlyingClass(tp: Type): Symbol = {
+ val sym = tp.widen.typeSymbol
+ if (sym.isAbstractType) underlyingClass(sym.info.bounds.hi)
+ else sym
+ }
+ val actual = underlyingClass(other.tpe)
+ val receiver = underlyingClass(qual.tpe)
+ def onTrees[T](f: List[Tree] => T) = f(List(qual, other))
+ def onSyms[T](f: List[Symbol] => T) = f(List(receiver, actual))
+
+ // @MAT normalize for consistency in error message, otherwise only part is normalized due to use of `typeSymbol`
+ def typesString = normalizeAll(qual.tpe.widen)+" and "+normalizeAll(other.tpe.widen)
+
+ /* Symbols which limit the warnings we can issue since they may be value types */
+ val isMaybeValue = Set[Symbol](AnyClass, AnyRefClass, AnyValClass, ObjectClass, ComparableClass, JavaSerializableClass)
+
+ // Whether def equals(other: Any) has known behavior: it is the default
+ // inherited from java.lang.Object, or it is a synthetically generated
+ // case equals. TODO - more cases are warnable if the target is a synthetic
+ // equals.
+ def isUsingWarnableEquals = {
+ val m = receiver.info.member(nme.equals_)
+ ((m == Object_equals) || (m == Any_equals) || isMethodCaseEquals(m))
+ }
+ def isMethodCaseEquals(m: Symbol) = m.isSynthetic && m.owner.isCase
+ def isCaseEquals = isMethodCaseEquals(receiver.info.member(nme.equals_))
+ // Whether this == or != is one of those defined in Any/AnyRef or an overload from elsewhere.
+ def isUsingDefaultScalaOp = sym == Object_== || sym == Object_!= || sym == Any_== || sym == Any_!=
+ def haveSubclassRelationship = (actual isSubClass receiver) || (receiver isSubClass actual)
+
+ // Whether the operands+operator represent a warnable combo (assuming anyrefs)
+ // Looking for comparisons performed with ==/!= in combination with either an
+ // equals method inherited from Object or a case class synthetic equals (for
+ // which we know the logic.)
+ def isWarnable = isReferenceOp || (isUsingDefaultScalaOp && isUsingWarnableEquals)
+ def isEitherNullable = (NullTpe <:< receiver.info) || (NullTpe <:< actual.info)
+ def isEitherValueClass = actual.isDerivedValueClass || receiver.isDerivedValueClass
+ def isBoolean(s: Symbol) = unboxedValueClass(s) == BooleanClass
+ def isUnit(s: Symbol) = unboxedValueClass(s) == UnitClass
+ def isNumeric(s: Symbol) = isNumericValueClass(unboxedValueClass(s)) || isAnyNumber(s)
+ def isScalaNumber(s: Symbol) = s isSubClass ScalaNumberClass
+ def isJavaNumber(s: Symbol) = s isSubClass JavaNumberClass
+ // includes java.lang.Number if appropriate [SI-5779]
+ def isAnyNumber(s: Symbol) = isScalaNumber(s) || isJavaNumber(s)
+ def isMaybeAnyValue(s: Symbol) = isPrimitiveValueClass(unboxedValueClass(s)) || isMaybeValue(s)
+ // used to short-circuit unrelatedTypes check if both sides are special
+ def isSpecial(s: Symbol) = isMaybeAnyValue(s) || isAnyNumber(s)
+ val nullCount = onSyms(_ filter (_ == NullClass) size)
+ def isNonsenseValueClassCompare = (
+ !haveSubclassRelationship
+ && isUsingDefaultScalaOp
+ && isEitherValueClass
+ && !isCaseEquals
+ )
+
+ // Have we already determined that the comparison is non-sensible? I mean, non-sensical?
+ var isNonSensible = false
+
+ def nonSensibleWarning(what: String, alwaysEqual: Boolean) = {
+ val msg = alwaysEqual == (name == nme.EQ || name == nme.eq)
+ unit.warning(pos, s"comparing $what using `${name.decode}' will always yield $msg")
+ isNonSensible = true
+ }
+ def nonSensible(pre: String, alwaysEqual: Boolean) =
+ nonSensibleWarning(s"${pre}values of types $typesString", alwaysEqual)
+ def nonSensiblyEq() = nonSensible("", alwaysEqual = true)
+ def nonSensiblyNeq() = nonSensible("", alwaysEqual = false)
+ def nonSensiblyNew() = nonSensibleWarning("a fresh object", alwaysEqual = false)
+
+ def unrelatedMsg = name match {
+ case nme.EQ | nme.eq => "never compare equal"
+ case _ => "always compare unequal"
+ }
+ def unrelatedTypes() = if (!isNonSensible) {
+ val weaselWord = if (isEitherValueClass) "" else " most likely"
+ unit.warning(pos, s"$typesString are unrelated: they will$weaselWord $unrelatedMsg")
+ }
+
+ if (nullCount == 2) // null == null
+ nonSensiblyEq()
+ else if (nullCount == 1) {
+ if (onSyms(_ exists isPrimitiveValueClass)) // null == 5
+ nonSensiblyNeq()
+ else if (onTrees( _ exists isNew)) // null == new AnyRef
+ nonSensiblyNew()
+ }
+ else if (isBoolean(receiver)) {
+ if (!isBoolean(actual) && !isMaybeValue(actual)) // true == 5
+ nonSensiblyNeq()
+ }
+ else if (isUnit(receiver)) {
+ if (isUnit(actual)) // () == ()
+ nonSensiblyEq()
+ else if (!isUnit(actual) && !isMaybeValue(actual)) // () == "abc"
+ nonSensiblyNeq()
+ }
+ else if (isNumeric(receiver)) {
+ if (!isNumeric(actual))
+ if (isUnit(actual) || isBoolean(actual) || !isMaybeValue(actual)) // 5 == "abc"
+ nonSensiblyNeq()
+ }
+ else if (isWarnable && !isCaseEquals) {
+ if (isNew(qual)) // new X == y
+ nonSensiblyNew()
+ else if (isNew(other) && (receiver.isEffectivelyFinal || isReferenceOp)) // object X ; X == new Y
+ nonSensiblyNew()
+ else if (receiver.isEffectivelyFinal && !(receiver isSubClass actual) && !actual.isRefinementClass) { // object X, Y; X == Y
+ if (isEitherNullable)
+ nonSensible("non-null ", false)
+ else
+ nonSensiblyNeq()
+ }
+ }
+
+ // warn if one but not the other is a derived value class
+ // this is especially important to enable transitioning from
+ // regular to value classes without silent failures.
+ if (isNonsenseValueClassCompare)
+ unrelatedTypes()
+ // possibleNumericCount is insufficient or this will warn on e.g. Boolean == j.l.Boolean
+ else if (isWarnable && nullCount == 0 && !(isSpecial(receiver) && isSpecial(actual))) {
+ // better to have lubbed and lost
+ def warnIfLubless(): Unit = {
+ val common = global.lub(List(actual.tpe, receiver.tpe))
+ if (ObjectTpe <:< common)
+ unrelatedTypes()
+ }
+ // warn if actual has a case parent that is not same as receiver's;
+ // if actual is not a case, then warn if no common supertype, as below
+ if (isCaseEquals) {
+ def thisCase = receiver.info.member(nme.equals_).owner
+ actual.info.baseClasses.find(_.isCase) match {
+ case Some(p) if p != thisCase => nonSensible("case class ", false)
+ case None =>
+ // stronger message on (Some(1) == None)
+ //if (receiver.isCase && receiver.isEffectivelyFinal && !(receiver isSubClass actual)) nonSensiblyNeq()
+ //else
+ // if a class, it must be super to thisCase (and receiver) since not <: thisCase
+ if (!actual.isTrait && !(receiver isSubClass actual)) nonSensiblyNeq()
+ else if (!haveSubclassRelationship) warnIfLubless()
+ case _ =>
+ }
+ }
+ // warn only if they have no common supertype below Object
+ else if (!haveSubclassRelationship) {
+ warnIfLubless()
+ }
+ }
+ }
+ /** Sensibility check examines flavors of equals. */
+ def checkSensible(pos: Position, fn: Tree, args: List[Tree]) = fn match {
+ case Select(qual, name @ (nme.EQ | nme.NE | nme.eq | nme.ne)) if args.length == 1 && isObjectOrAnyComparisonMethod(fn.symbol) =>
+ checkSensibleEquals(pos, qual, name, fn.symbol, args.head)
+ case _ =>
+ }
+*/
+
+/* --------------- Overflow -------------------------------------------------
+ *
+
+ def accessFlagsToString(sym: Symbol) = flagsToString(
+ sym getFlag (PRIVATE | PROTECTED),
+ if (sym.hasAccessBoundary) "" + sym.privateWithin.name else ""
+ )
+
+ def overridesTypeInPrefix(tp1: Type, tp2: Type, prefix: Type): Boolean = (tp1.dealiasWiden, tp2.dealiasWiden) match {
+ case (MethodType(List(), rtp1), NullaryMethodType(rtp2)) =>
+ rtp1 <:< rtp2
+ case (NullaryMethodType(rtp1), MethodType(List(), rtp2)) =>
+ rtp1 <:< rtp2
+ case (TypeRef(_, sym, _), _) if sym.isModuleClass =>
+ overridesTypeInPrefix(NullaryMethodType(tp1), tp2, prefix)
+ case _ =>
+ def classBoundAsSeen(tp: Type) = tp.typeSymbol.classBound.asSeenFrom(prefix, tp.typeSymbol.owner)
+
+ (tp1 <:< tp2) || ( // object override check
+ tp1.typeSymbol.isModuleClass && tp2.typeSymbol.isModuleClass && {
+ val cb1 = classBoundAsSeen(tp1)
+ val cb2 = classBoundAsSeen(tp2)
+ (cb1 <:< cb2) && {
+ log("Allowing %s to override %s because %s <:< %s".format(tp1, tp2, cb1, cb2))
+ true
+ }
+ }
+ )
+ }
+ private def checkTypeRef(tp: Type, tree: Tree, skipBounds: Boolean)(implicit ctx: Context) = tp match {
+ case TypeRef(pre, sym, args) =>
+ tree match {
+ case tt: TypeTree if tt.original == null => // SI-7783 don't warn about inferred types
+ // FIXME: reconcile this check with one in resetAttrs
+ case _ => checkUndesiredProperties(sym, tree.pos)
+ }
+ if(sym.isJavaDefined)
+ sym.typeParams foreach (_.cookJavaRawInfo())
+ if (!tp.isHigherKinded && !skipBounds)
+ checkBounds(tree, pre, sym.owner, sym.typeParams, args)
+ case _ =>
+ }
+
+ private def checkTypeRefBounds(tp: Type, tree: Tree) = {
+ var skipBounds = false
+ tp match {
+ case AnnotatedType(ann :: Nil, underlying) if ann.symbol == UncheckedBoundsClass =>
+ skipBounds = true
+ underlying
+ case TypeRef(pre, sym, args) =>
+ if (!tp.isHigherKinded && !skipBounds)
+ checkBounds(tree, pre, sym.owner, sym.typeParams, args)
+ tp
+ case _ =>
+ tp
+ }
+ }
+
+ private def checkAnnotations(tpes: List[Type], tree: Tree) = tpes foreach { tp =>
+ checkTypeRef(tp, tree, skipBounds = false)
+ checkTypeRefBounds(tp, tree)
+ }
+ private def doTypeTraversal(tree: Tree)(f: Type => Unit) = if (!inPattern) tree.tpe foreach f
+
+ private def applyRefchecksToAnnotations(tree: Tree)(implicit ctx: Context): Unit = {
+ def applyChecks(annots: List[Annotation]) = {
+ checkAnnotations(annots map (_.atp), tree)
+ transformTrees(annots flatMap (_.args))
+ }
+
+ tree match {
+ case m: MemberDef =>
+ val sym = m.symbol
+ applyChecks(sym.annotations)
+ // validate implicitNotFoundMessage
+ analyzer.ImplicitNotFoundMsg.check(sym) foreach { warn =>
+ unit.warning(tree.pos, f"Invalid implicitNotFound message for ${sym}%s${sym.locationString}%s:%n$warn")
+ }
+
+ case tpt@TypeTree() =>
+ if(tpt.original != null) {
+ tpt.original foreach {
+ case dc@TypeTreeWithDeferredRefCheck() =>
+ applyRefchecksToAnnotations(dc.check()) // #2416
+ case _ =>
+ }
+ }
+
+ doTypeTraversal(tree) {
+ case tp @ AnnotatedType(annots, _) =>
+ applyChecks(annots)
+ case tp =>
+ }
+ case _ =>
+ }
+ }
+
+ private def transformCaseApply(tree: Tree, ifNot: => Unit) = {
+ val sym = tree.symbol
+
+ def isClassTypeAccessible(tree: Tree): Boolean = tree match {
+ case TypeApply(fun, targs) =>
+ isClassTypeAccessible(fun)
+ case Select(module, apply) =>
+ ( // SI-4859 `CaseClass1().InnerCaseClass2()` must not be rewritten to `new InnerCaseClass2()`;
+ // {expr; Outer}.Inner() must not be rewritten to `new Outer.Inner()`.
+ treeInfo.isQualifierSafeToElide(module) &&
+ // SI-5626 Classes in refinement types cannot be constructed with `new`. In this case,
+ // the companion class is actually not a ClassSymbol, but a reference to an abstract type.
+ module.symbol.companionClass.isClass
+ )
+ }
+
+ val doTransform =
+ sym.isSourceMethod &&
+ sym.isCase &&
+ sym.name == nme.apply &&
+ isClassTypeAccessible(tree)
+
+ if (doTransform) {
+ tree foreach {
+ case i@Ident(_) =>
+ enterReference(i.pos, i.symbol) // SI-5390 need to `enterReference` for `a` in `a.B()`
+ case _ =>
+ }
+ toConstructor(tree.pos, tree.tpe)
+ }
+ else {
+ ifNot
+ tree
+ }
+ }
+
+ private def transformApply(tree: Apply): Tree = tree match {
+ case Apply(
+ Select(qual, nme.filter | nme.withFilter),
+ List(Function(
+ List(ValDef(_, pname, tpt, _)),
+ Match(_, CaseDef(pat1, _, _) :: _))))
+ if ((pname startsWith nme.CHECK_IF_REFUTABLE_STRING) &&
+ isIrrefutable(pat1, tpt.tpe) && (qual.tpe <:< tree.tpe)) =>
+
+ transform(qual)
+
+ case Apply(fn, args) =>
+ // sensicality should be subsumed by the unreachability/exhaustivity/irrefutability
+ // analyses in the pattern matcher
+ if (!inPattern) {
+ checkImplicitViewOptionApply(tree.pos, fn, args)
+ checkSensible(tree.pos, fn, args)
+ }
+ currentApplication = tree
+ tree
+ }
+ private def transformSelect(tree: Select): Tree = {
+ val Select(qual, _) = tree
+ val sym = tree.symbol
+
+ checkUndesiredProperties(sym, tree.pos)
+ checkDelayedInitSelect(qual, sym, tree.pos)
+
+ if (!sym.exists)
+ devWarning("Select node has NoSymbol! " + tree + " / " + tree.tpe)
+ else if (sym.isLocalToThis)
+ varianceValidator.checkForEscape(sym, currentClass)
+
+ def checkSuper(mix: Name) =
+ // term should have been eliminated by super accessors
+ assert(!(qual.symbol.isTrait && sym.isTerm && mix == tpnme.EMPTY), (qual.symbol, sym, mix))
+
+ transformCaseApply(tree,
+ qual match {
+ case Super(_, mix) => checkSuper(mix)
+ case _ =>
+ }
+ )
+ }
+ private def transformIf(tree: If): Tree = {
+ val If(cond, thenpart, elsepart) = tree
+ def unitIfEmpty(t: Tree): Tree =
+ if (t == EmptyTree) Literal(Constant(())).setPos(tree.pos).setType(UnitTpe) else t
+
+ cond.tpe match {
+ case ConstantType(value) =>
+ val res = if (value.booleanValue) thenpart else elsepart
+ unitIfEmpty(res)
+ case _ => tree
+ }
+ }
+
+ // Warning about nullary methods returning Unit. TODO: move to lint
+ private def checkNullaryMethodReturnType(sym: Symbol) = sym.tpe match {
+ case NullaryMethodType(restpe) if restpe.typeSymbol == UnitClass =>
+ // this may be the implementation of e.g. a generic method being parameterized
+ // on Unit, in which case we had better let it slide.
+ val isOk = (
+ sym.isGetter
+ || (sym.name containsName nme.DEFAULT_GETTER_STRING)
+ || sym.allOverriddenSymbols.exists(over => !(over.tpe.resultType =:= sym.tpe.resultType))
+ )
+ if (!isOk)
+ unit.warning(sym.pos, s"side-effecting nullary methods are discouraged: suggest defining as `def ${sym.name.decode}()` instead")
+ case _ => ()
+ }
+
+ /* Convert a reference to a case factory of type `tpe` to a new of the class it produces. */
+ def toConstructor(pos: Position, tpe: Type)(implicit ctx: Context): Tree = {
+ val rtpe = tpe.finalResultType
+ assert(rtpe.typeSymbol.is(Case), tpe)
+ New(rtpe).withPos(pos).select(rtpe.typeSymbol.primaryConstructor)
+ }
+ private def isIrrefutable(pat: Tree, seltpe: Type): Boolean = pat match {
+ case Apply(_, args) =>
+ val clazz = pat.tpe.typeSymbol
+ clazz == seltpe.typeSymbol &&
+ clazz.isCaseClass &&
+ (args corresponds clazz.primaryConstructor.tpe.asSeenFrom(seltpe, clazz).paramTypes)(isIrrefutable)
+ case Typed(pat, tpt) =>
+ seltpe <:< tpt.tpe
+ case Ident(tpnme.WILDCARD) =>
+ true
+ case Bind(_, pat) =>
+ isIrrefutable(pat, seltpe)
+ case _ =>
+ false
+ }
+ private def checkDelayedInitSelect(qual: Tree, sym: Symbol, pos: Position) = {
+ def isLikelyUninitialized = (
+ (sym.owner isSubClass DelayedInitClass)
+ && !qual.tpe.isInstanceOf[ThisType]
+ && sym.accessedOrSelf.isVal
+ )
+ if (settings.lint.value && isLikelyUninitialized)
+ unit.warning(pos, s"Selecting ${sym} from ${sym.owner}, which extends scala.DelayedInit, is likely to yield an uninitialized value")
+ }
+ private def lessAccessible(otherSym: Symbol, memberSym: Symbol): Boolean = (
+ (otherSym != NoSymbol)
+ && !otherSym.isProtected
+ && !otherSym.isTypeParameterOrSkolem
+ && !otherSym.isExistentiallyBound
+ && (otherSym isLessAccessibleThan memberSym)
+ && (otherSym isLessAccessibleThan memberSym.enclClass)
+ )
+ private def lessAccessibleSymsInType(other: Type, memberSym: Symbol): List[Symbol] = {
+ val extras = other match {
+ case TypeRef(pre, _, args) =>
+ // checking the prefix here gives us spurious errors on e.g. a private[process]
+ // object which contains a type alias, which normalizes to a visible type.
+ args filterNot (_ eq NoPrefix) flatMap (tp => lessAccessibleSymsInType(tp, memberSym))
+ case _ =>
+ Nil
+ }
+ if (lessAccessible(other.typeSymbol, memberSym)) other.typeSymbol :: extras
+ else extras
+ }
+ private def warnLessAccessible(otherSym: Symbol, memberSym: Symbol) {
+ val comparison = accessFlagsToString(memberSym) match {
+ case "" => ""
+ case acc => " is " + acc + " but"
+ }
+ val cannot =
+ if (memberSym.isDeferred) "may be unable to provide a concrete implementation of"
+ else "may be unable to override"
+
+ unit.warning(memberSym.pos,
+ "%s%s references %s %s.".format(
+ memberSym.fullLocationString, comparison,
+ accessFlagsToString(otherSym), otherSym
+ ) + "\nClasses which cannot access %s %s %s.".format(
+ otherSym.decodedName, cannot, memberSym.decodedName)
+ )
+ }
+
+ /** Warn about situations where a method signature will include a type which
+ * has more restrictive access than the method itself.
+ */
+ private def checkAccessibilityOfReferencedTypes(tree: Tree) {
+ val member = tree.symbol
+
+ def checkAccessibilityOfType(tpe: Type) {
+ val inaccessible = lessAccessibleSymsInType(tpe, member)
+ // if the unnormalized type is accessible, that's good enough
+ if (inaccessible.isEmpty) ()
+ // or if the normalized type is, that's good too
+ else if ((tpe ne tpe.normalize) && lessAccessibleSymsInType(tpe.dealiasWiden, member).isEmpty) ()
+ // otherwise warn about the inaccessible syms in the unnormalized type
+ else inaccessible foreach (sym => warnLessAccessible(sym, member))
+ }
+
+ // types of the value parameters
+ mapParamss(member)(p => checkAccessibilityOfType(p.tpe))
+ // upper bounds of type parameters
+ member.typeParams.map(_.info.bounds.hi.widen) foreach checkAccessibilityOfType
+ }
+
+ private def checkByNameRightAssociativeDef(tree: DefDef) {
+ tree match {
+ case DefDef(_, name, _, params :: _, _, _) =>
+ if (settings.lint && !treeInfo.isLeftAssoc(name.decodedName) && params.exists(p => isByName(p.symbol)))
+ unit.warning(tree.pos,
+ "by-name parameters will be evaluated eagerly when called as a right-associative infix operator. For more details, see SI-1980.")
+ case _ =>
+ }
+ }
+ override def transform(tree: Tree)(implicit ctx: Context): Tree = {
+ //val savedLocalTyper = localTyper
+ try {
+ val sym = tree.symbol
+ checkOverloadedRestrictions(ctx.owner)
+ checkAllOverrides(ctx.owner)
+ checkAnyValSubclass(ctx.owner)
+ if (ctx.owner.isDerivedValueClass)
+ ctx.owner.primaryConstructor.makeNotPrivateAfter(NoSymbol, thisTransformer) // SI-6601, must be done *after* pickler!
+ tree
+
+
+ // Apply RefChecks to annotations. Makes sure the annotations conform to
+ // type bounds (bug #935), issues deprecation warnings for symbols used
+ // inside annotations.
+ // applyRefchecksToAnnotations(tree) ???
+ var result: Tree = tree match {
+ case tree: ValOrDefDef =>
+ // move to lint:
+ // if (settings.warnNullaryUnit)
+ // checkNullaryMethodReturnType(sym)
+ // if (settings.warnInaccessible) {
+ // if (!sym.isConstructor && !sym.isEffectivelyFinal && !sym.isSynthetic)
+ // checkAccessibilityOfReferencedTypes(tree)
+ // }
+ // tree match {
+ // case dd: DefDef => checkByNameRightAssociativeDef(dd)
+ // case _ =>
+ // }
+ tree
+
+ case Template(constr, parents, self, body) =>
+ // localTyper = localTyper.atOwner(tree, currentOwner)
+ checkOverloadedRestrictions(ctx.owner)
+ checkAllOverrides(ctx.owner)
+ checkAnyValSubclass(ctx.owner)
+ if (ctx.owner.isDerivedValueClass)
+ ctx.owner.primaryConstructor.makeNotPrivateAfter(NoSymbol, thisTransformer) // SI-6601, must be done *after* pickler!
+ tree
+
+ case tpt: TypeTree =>
+ transform(tpt.original)
+ tree
+
+ case TypeApply(fn, args) =>
+ checkBounds(tree, NoPrefix, NoSymbol, fn.tpe.typeParams, args map (_.tpe))
+ transformCaseApply(tree, ())
+
+ case x @ Apply(_, _) =>
+ transformApply(x)
+
+ case x @ If(_, _, _) =>
+ transformIf(x)
+
+ case New(tpt) =>
+ enterReference(tree.pos, tpt.tpe.typeSymbol)
+ tree
+
+ case treeInfo.WildcardStarArg(_) if !isRepeatedParamArg(tree) =>
+ unit.error(tree.pos, "no `: _*' annotation allowed here\n"+
+ "(such annotations are only allowed in arguments to *-parameters)")
+ tree
+
+ case Ident(name) =>
+ checkUndesiredProperties(sym, tree.pos)
+ transformCaseApply(tree,
+ if (name != nme.WILDCARD && name != tpnme.WILDCARD_STAR) {
+ assert(sym != NoSymbol, "transformCaseApply: name = " + name.debugString + " tree = " + tree + " / " + tree.getClass) //debug
+ enterReference(tree.pos, sym)
+ }
+ )
+
+ case x @ Select(_, _) =>
+ transformSelect(x)
+
+ case UnApply(fun, args) =>
+ transform(fun) // just make sure we enterReference for unapply symbols, note that super.transform(tree) would not transform(fun)
+ // transformTrees(args) // TODO: is this necessary? could there be forward references in the args??
+ // probably not, until we allow parameterised extractors
+ tree
+
+
+ case _ => tree
+ }
+
+ // skip refchecks in patterns....
+ result = result match {
+ case CaseDef(pat, guard, body) =>
+ val pat1 = savingInPattern {
+ inPattern = true
+ transform(pat)
+ }
+ treeCopy.CaseDef(tree, pat1, transform(guard), transform(body))
+ case LabelDef(_, _, _) if treeInfo.hasSynthCaseSymbol(result) =>
+ savingInPattern {
+ inPattern = true
+ deriveLabelDef(result)(transform)
+ }
+ case Apply(fun, args) if fun.symbol.isLabel && treeInfo.isSynthCaseSymbol(fun.symbol) =>
+ savingInPattern {
+ // SI-7756 If we were in a translated pattern, we can now switch out of pattern mode, as the label apply signals
+ // that we are in the user-supplied code in the case body.
+ //
+ // Relies on the translation of:
+ // (null: Any) match { case x: List[_] => x; x.reverse; case _ => }'
+ // to:
+ // val x2: List[_] = (x1.asInstanceOf[List[_]]: List[_]);
+ // matchEnd4({ x2; x2.reverse}) // case body is an argument to a label apply.
+ inPattern = false
+ super.transform(result)
+ }
+ case ValDef(_, _, _, _) if treeInfo.hasSynthCaseSymbol(result) =>
+ deriveValDef(result)(transform) // SI-7716 Don't refcheck the tpt of the synthetic val that holds the selector.
+ case _ =>
+ super.transform(result)
+ }
+ result match {
+ case ClassDef(_, _, _, _)
+ | TypeDef(_, _, _, _) =>
+ if (result.symbol.isLocalToBlock || result.symbol.isTopLevel)
+ varianceValidator.traverse(result)
+ case tt @ TypeTree() if tt.original != null =>
+ varianceValidator.traverse(tt.original) // See SI-7872
+ case _ =>
+ }
+
+ checkUnexpandedMacro(result)
+
+ result
+ } catch {
+ case ex: TypeError =>
+ if (settings.debug) ex.printStackTrace()
+ unit.error(tree.pos, ex.getMessage())
+ tree
+ } finally {
+ localTyper = savedLocalTyper
+ currentApplication = savedCurrentApplication
+ }
+ }
+*/
+
diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala
index d5153bf1384d..2024a993ebbb 100644
--- a/src/dotty/tools/dotc/typer/Typer.scala
+++ b/src/dotty/tools/dotc/typer/Typer.scala
@@ -735,7 +735,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
def typedAppliedTypeTree(tree: untpd.AppliedTypeTree)(implicit ctx: Context): AppliedTypeTree = track("typedAppliedTypeTree") {
val tpt1 = typed(tree.tpt)
val args1 = tree.args mapconserve (typed(_))
- // todo in later phase: check arguments conform to parameter bounds
+ // check that arguments conform to bounds is done in phase FirstTransform
assignType(cpy.AppliedTypeTree(tree, tpt1, args1), tpt1, args1)
}
@@ -748,9 +748,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
val TypeBoundsTree(lo, hi) = desugar.typeBoundsTree(tree)
val lo1 = typed(lo)
val hi1 = typed(hi)
- // need to do in later phase, as this might cause a cyclic reference error. See pos/t0039.scala
- // if (!(lo1.tpe <:< hi1.tpe))
- // ctx.error(d"lower bound ${lo1.tpe} does not conform to upper bound ${hi1.tpe}", tree.pos)
+ if (!(lo1.tpe <:< hi1.tpe))
+ ctx.error(d"lower bound ${lo1.tpe} does not conform to upper bound ${hi1.tpe}", tree.pos)
assignType(cpy.TypeBoundsTree(tree, lo1, hi1), lo1, hi1)
}
diff --git a/src/dotty/tools/dotc/typer/VarianceChecker.scala b/src/dotty/tools/dotc/typer/VarianceChecker.scala
index 9ce3ca0b776b..5865f0133f67 100644
--- a/src/dotty/tools/dotc/typer/VarianceChecker.scala
+++ b/src/dotty/tools/dotc/typer/VarianceChecker.scala
@@ -6,6 +6,7 @@ import core._
import Types._, Contexts._, Flags._, Symbols._, Annotations._, Trees._, NameOps._
import Decorators._
import Variances._
+import config.Printers.variances
/** Provides `check` method to check that all top-level definitions
* in tree are variance correct. Does not recurse inside methods.
@@ -77,7 +78,7 @@ class VarianceChecker()(implicit ctx: Context) {
* explicitly (their TypeDefs will be passed here.) For MethodTypes, the
* same is true of the parameters (ValDefs).
*/
- def apply(status: Option[VarianceError], tp: Type): Option[VarianceError] = ctx.traceIndented(s"variance checking $tp of $base at $variance") {
+ def apply(status: Option[VarianceError], tp: Type): Option[VarianceError] = ctx.traceIndented(s"variance checking $tp of $base at $variance", variances) {
if (status.isDefined) status
else tp match {
case tp: TypeRef =>
diff --git a/src/scala/Product0.scala b/src/scala/Product0.scala
new file mode 100644
index 000000000000..23e7e52c0256
--- /dev/null
+++ b/src/scala/Product0.scala
@@ -0,0 +1,24 @@
+/* __ *\
+** ________ ___ / / ___ Scala API **
+** / __/ __// _ | / / / _ | (c) 2002-2013, LAMP/EPFL **
+** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
+** /____/\___/_/ |_/____/_/ | | **
+** |/ **
+\* */
+package scala
+
+/** A class for Product0 which was missing from the scala distribution. */
+object Product0 {
+ def unapply(x: Product0): Option[Product0] =
+ Some(x)
+}
+
+trait Product0 extends Any with Product {
+
+ override def productArity = 0
+
+ @throws(classOf[IndexOutOfBoundsException])
+ override def productElement(n: Int) =
+ throw new IndexOutOfBoundsException(n.toString())
+}
+
diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala
index fa577573a2af..12a2a8cb8efd 100644
--- a/test/dotc/tests.scala
+++ b/test/dotc/tests.scala
@@ -14,7 +14,7 @@ class tests extends CompilerTest {
"-pagewidth", "160")
implicit val defaultOptions = noCheckOptions ++ List(
- "-Ycheck:tailrec"
+ "-Ycheck:refchecks,tailrec"
)
val twice = List("#runs", "2", "-YnoDoubleBindings")
@@ -84,6 +84,7 @@ class tests extends CompilerTest {
@Test def neg_tailcall = compileFile(negDir, "tailcall/tailrec", xerrors = 7)
@Test def neg_tailcall2 = compileFile(negDir, "tailcall/tailrec-2", xerrors = 2)
@Test def neg_tailcall3 = compileFile(negDir, "tailcall/tailrec-3", xerrors = 2)
+ @Test def nef_t1279a = compileFile(negDir, "t1279a", xerrors = 1)
@Test def neg_t1843 = compileFile(negDir, "t1843", xerrors = 1)
@Test def neg_t1843_variances = compileFile(negDir, "t1843-variances", xerrors = 1)
@Test def neg_t2994 = compileFile(negDir, "t2994", xerrors = 2)
diff --git a/test/test/transform/TreeTransformerTest.scala b/test/test/transform/TreeTransformerTest.scala
index 06257b48b3a5..aea372bf44b2 100644
--- a/test/test/transform/TreeTransformerTest.scala
+++ b/test/test/transform/TreeTransformerTest.scala
@@ -3,7 +3,7 @@ package test.transform
import org.junit.{Assert, Test}
import test.DottyTest
-import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer}
+import dotty.tools.dotc.transform.TreeTransforms.{TransformerInfo, TreeTransformer, MiniPhaseTransform}
import dotty.tools.dotc.ast.tpd
import dotty.tools.dotc.core.Constants.Constant
import dotty.tools.dotc.core.Contexts.Context
@@ -15,7 +15,7 @@ class TreeTransformerTest extends DottyTest {
def shouldReturnSameTreeIfUnchanged = checkCompile("frontend", "class A{ val d = 1}") {
(tree, context) =>
implicit val ctx = context
- class EmptyTransform extends TreeTransform {
+ class EmptyTransform extends MiniPhaseTransform {
override def name: String = "empty"
init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId)
}
@@ -35,7 +35,7 @@ class TreeTransformerTest extends DottyTest {
def canReplaceConstant = checkCompile("frontend", "class A{ val d = 1}") {
(tree, context) =>
implicit val ctx = context
- class ConstantTransform extends TreeTransform {
+ class ConstantTransform extends MiniPhaseTransform {
override def transformLiteral(tree: tpd.Literal)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = tpd.Literal(Constant(2))
override def name: String = "canReplaceConstant"
@@ -57,7 +57,7 @@ class TreeTransformerTest extends DottyTest {
def canOverwrite = checkCompile("frontend", "class A{ val d = 1}") {
(tree, context) =>
implicit val ctx = context
- class Transformation extends TreeTransform {
+ class Transformation extends MiniPhaseTransform {
override def transformLiteral(tree: tpd.Literal)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = tpd.Literal(Constant(-1))
override def name: String = "canOverwrite"
@@ -88,7 +88,7 @@ class TreeTransformerTest extends DottyTest {
def transformationOrder = checkCompile("frontend", "class A{ val d = 1}") {
(tree, context) =>
implicit val ctx = context
- class Transformation1 extends TreeTransform {
+ class Transformation1 extends MiniPhaseTransform {
override def name: String = "transformationOrder1"
override def transformLiteral(tree: tpd.Literal)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
@@ -107,7 +107,7 @@ class TreeTransformerTest extends DottyTest {
init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId)
}
- class Transformation2 extends TreeTransform {
+ class Transformation2 extends MiniPhaseTransform {
override def name: String = "transformationOrder2"
override def transformValDef(tree: tpd.ValDef)(implicit ctx: Context, info: TransformerInfo): tpd.ValDef = {
Assert.assertTrue("transformation of children succeeded",
@@ -135,7 +135,7 @@ class TreeTransformerTest extends DottyTest {
(tree, context) =>
implicit val ctx = context
var transformed1 = 0
- class Transformation1 extends TreeTransform {
+ class Transformation1 extends MiniPhaseTransform {
override def name: String = "invocationCount1"
override def transformLiteral(tree: tpd.Literal)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
transformed1 += 1
@@ -156,7 +156,7 @@ class TreeTransformerTest extends DottyTest {
init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId)
}
var transformed2 = 0
- class Transformation2 extends TreeTransform {
+ class Transformation2 extends MiniPhaseTransform {
var constantsSeen = 0
override def name: String = "invocationCount2"
override def transformLiteral(tree: tpd.Literal)(implicit ctx: Context, info: TransformerInfo): tpd.Tree = {
diff --git a/tests/neg/cycles.scala b/tests/neg/cycles.scala
new file mode 100644
index 000000000000..4eaec125f4cf
--- /dev/null
+++ b/tests/neg/cycles.scala
@@ -0,0 +1,4 @@
+class Foo[T <: U, U <: T]
+
+class Bar[T >: T]
+
diff --git a/tests/pos/t1279a.scala b/tests/neg/t1279a.scala
similarity index 92%
rename from tests/pos/t1279a.scala
rename to tests/neg/t1279a.scala
index 18b1e53f463e..6d768d43544b 100644
--- a/tests/pos/t1279a.scala
+++ b/tests/neg/t1279a.scala
@@ -1,10 +1,10 @@
// covariant linked list
abstract class M {
- self =>
+ self: M =>
type T
final type selfType = M {type T <: self.T}
- type actualSelfType >: self.type <: selfType
+ type actualSelfType >: self.type <: selfType // this no longer compiles because self.type is not a subtype of selfType
def next: selfType
diff --git a/tests/pos/desugar.scala b/tests/pos/desugar.scala
index f0d8645b7830..0d3b6d8ca624 100644
--- a/tests/pos/desugar.scala
+++ b/tests/pos/desugar.scala
@@ -6,11 +6,11 @@ object desugar {
val list = List(1, 2, 3)
{ var z: Int = y }
-
+
def foo0(first: Int, second: Int = 2, third: Int = 3) = first + second
def foo1(first: Int, second: Int = 2)(third: Int = 3) = first + second
def foo2(first: Int)(second: Int = 2)(third: Int = 3) = first + second
-
+
object caseClasses { self =>
trait List[+T] {
def head: T
@@ -23,34 +23,37 @@ object desugar {
def apply[T](head: T): Cons[T] = apply(head, Nil)
}
- case object Nil extends List[Nothing]
+ case object Nil extends List[Nothing] {
+ def head = throw new Error()
+ def tail = throw new Error()
+ }
}
-
+
object patDefs {
-
+
import caseClasses._
val xs: List[Int] = Cons(1, Cons(2, Nil))
-
- val Cons(y, ys) = xs
+
+ val Cons(y, ys) = xs
val Cons(z, _) = xs
val Cons(_, _) = xs
-
+
val (cons: Cons[Int]) = xs
-
+
val x1, y1, z1: Int = 1
}
-
+
object Binops {
-
+
x :: y :: Nil
-
+
val x :: y :: Nil = list
-
+
}
-
+
object fors {
-
+
for (x <- List(1, 2, 3)) yield 2
for (x <- List(1, 2, 3) if x % 2 == 0) yield x * x
for (x <- List(1, 2, 3); y <- 0 to x) yield x * y
@@ -65,7 +68,7 @@ object desugar {
for (x <- List(1, 2, 3); y = x * x; if x + y % 2 == 0) println(x * y)
for (x <- List(1, 2, 3); y = x * x; z = x * y; u <- 0 to y) println(x * y * z * u)
}
-
+
object misc {
'hello
s"this is a $x + ${x + y} string"
@@ -82,4 +85,4 @@ object desugar {
do x -= 1 while (x > 0)
}
-}
\ No newline at end of file
+}
diff --git a/tests/pos/hk.scala b/tests/pos/hk.scala
index 461c6e386ae1..9fdaf94f6ca8 100644
--- a/tests/pos/hk.scala
+++ b/tests/pos/hk.scala
@@ -2,7 +2,7 @@ import language.higherKinds
object hk0 {
- class Base {
+ abstract class Base {
type Rep[T]
val strRep: Rep[String]
}
@@ -13,7 +13,7 @@ object hk0 {
val sr: Rep[String] = ""
}
- class Functor[F[_]] {
+ abstract class Functor[F[_]] {
def map[A, B](f: A => B): F[A] => F[B]
}
val ml: Functor[List] = ???
@@ -53,4 +53,4 @@ object higherKinded {
tree1: Tree[String]
}
-}
\ No newline at end of file
+}
diff --git a/tests/pos/inferred.scala b/tests/pos/inferred.scala
index 525848541280..87bbd94730b8 100644
--- a/tests/pos/inferred.scala
+++ b/tests/pos/inferred.scala
@@ -1,13 +1,13 @@
-class LIST[+T] {
-
+abstract class LIST[+T] {
+
def isEmpty: Boolean
def head: T
def tail: LIST[T]
-
+
def prepend [U >: T] (x: U): LIST[U] = new CONS(x, this)
-
+
def map[U](f: T => U): LIST[U] = if (isEmpty) NIL else tail.map(f).prepend(f(head))
-
+
}
object NIL extends LIST[Nothing] {
@@ -37,22 +37,22 @@ object Inferred {
val nn = bar(NIL)
val ints: LIST[Int] = NIL prepend 1
-
+
val ints1 = NIL prepend 1 prepend 2
val a = if (1 == 0) NIL else ints
-
+
val n2 = scala.collection.immutable.Nil
-
+
val ss2: scala.collection.immutable.List[String] = "abc" :: n2
-
+
val ss3 = "abc" :: n2
-
+
def cl = ((x: Int) => x + 1)
-
+
val ints2 = ints map (_ + 1)
-
+
val ints3 = new CONS[Int](1, NIL)
-
+
val ints4 = new CONS(1, NIL)
}
\ No newline at end of file
diff --git a/tests/pos/opassign.scala b/tests/pos/opassign.scala
index 7b8fec652038..8f6cad903a27 100644
--- a/tests/pos/opassign.scala
+++ b/tests/pos/opassign.scala
@@ -1,28 +1,28 @@
object opassign {
-
+
var count: Int = 0
def next = { count += 1; count }
-
+
var x: Int = 0
x += 1
-
+
{ var x: Int = 0
x += 1
}
-
+
class Ref {
- var x: Int
+ var x: Int = _
}
val r = new Ref
r.x += 1
-
+
val arr = new Array[Int](10)
arr(0) += 1
-
+
def f(x: Int): Ref = new Ref
f(next).x += 1
-
+
val buf = new collection.mutable.ListBuffer[Int]
buf += 1
-}
\ No newline at end of file
+}
diff --git a/tests/pos/t1832.scala b/tests/pos/t1832.scala
index 9ad9703c2955..c34fe4bfa067 100644
--- a/tests/pos/t1832.scala
+++ b/tests/pos/t1832.scala
@@ -2,7 +2,7 @@ trait Cloning {
trait Foo
def fn(g: Any => Unit): Foo
- class Star { def *(a: Cloning.this.Foo): Cloning.this.Foo }
+ abstract class Star { def *(a: Cloning.this.Foo): Cloning.this.Foo }
implicit def mkStar(i: Int): Star = new Star { def *(a: Foo): Foo = null }
diff --git a/tests/pos/tycons.scala b/tests/pos/tycons.scala
index f138c78be50e..ef16a77922ab 100644
--- a/tests/pos/tycons.scala
+++ b/tests/pos/tycons.scala
@@ -12,11 +12,11 @@ object obj extends List[Number] with Set[Exception] {
val e: Exception = x
}
-class Functor[F <: TypeConstructor] {
+abstract class Functor[F <: TypeConstructor] {
def map[A, B](f: F { type TypeArg <: A }): F { type TypeArg <: B }
}
implicit object ListFunctor extends Functor[List] {
- def map[A, B](f: List[A]): List[B] = ???
+ override def map[A, B](f: List { type TypeArg <: A }): List { type TypeArg <: B } = ???
}
diff --git a/tests/pos/typers.scala b/tests/pos/typers.scala
index b2d786c1c9b1..fe11ca6021be 100644
--- a/tests/pos/typers.scala
+++ b/tests/pos/typers.scala
@@ -88,7 +88,7 @@ object typers {
}
class Refinements {
- val y: C { type T; val key: T; def process(x: T): Int }
+ val y: C { type T; val key: T; def process(x: T): Int } = ???
}
object Accessibility {