From 2020938a77590f8c461041707716eca228f647d2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 3 Aug 2014 20:41:32 +0200 Subject: [PATCH 01/44] Code to handle overloaded unapply/unapplySeq methods These were not handled before. --- src/dotty/tools/dotc/typer/Applications.scala | 34 +++++++++++++------ src/dotty/tools/dotc/typer/ProtoTypes.scala | 4 +-- 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 242985b57ffc..b506e7e33a7a 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -555,18 +555,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/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: * From 168e4f18f0b2f8ac0e3d7ef5128797303dec6a44 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 3 Aug 2014 20:44:27 +0200 Subject: [PATCH 02/44] Added version settings -migration, -source --- .../tools/dotc/config/ScalaSettings.scala | 3 +- .../tools/dotc/config/ScalaVersion.scala | 183 ++++++++++++++++++ src/dotty/tools/dotc/config/Settings.scala | 9 + src/dotty/tools/dotc/core/Definitions.scala | 5 + 4 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 src/dotty/tools/dotc/config/ScalaVersion.scala 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/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 286d1437f553..f20882ce49c6 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. @@ -292,6 +296,7 @@ class Definitions { lazy val ScalaSignatureAnnot = ctx.requiredClass("scala.reflect.ScalaSignature") lazy val ScalaLongSignatureAnnot = ctx.requiredClass("scala.reflect.ScalaLongSignature") lazy val DeprecatedAnnot = ctx.requiredClass("scala.deprecated") + lazy val MigrationAnnot = ctx.requiredClass("scala.migration") lazy val AnnotationDefaultAnnot = ctx.requiredClass("dotty.annotation.internal.AnnotationDefault") lazy val ThrowsAnnot = ctx.requiredClass("scala.throws") lazy val UncheckedAnnot = ctx.requiredClass("scala.unchecked") From e25232d5b4fdc1ae7bc5a44ad06e31a00699dbaf Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 3 Aug 2014 20:50:37 +0200 Subject: [PATCH 03/44] Annotation decorators for symbols Added decorators for symbols that can query specific annotations and annotation arguments (for now, -deprecated and -migration are added) --- src/dotty/tools/dotc/ast/TreeInfo.scala | 12 +++++-- src/dotty/tools/dotc/core/Annotations.scala | 33 +++++++++++++++++++ .../tools/dotc/core/SymDenotations.scala | 9 ++--- 3 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala index cc3e53abcd8a..174de4d460b9 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? */ 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/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 91a8e4345918..fcc01503f9b7 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -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 } From 85044e451ea99ef49fe2348148bdd4296b1db595 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 3 Aug 2014 20:51:12 +0200 Subject: [PATCH 04/44] Type#foreachPart Added method to traverse all parts of a type. --- src/dotty/tools/dotc/core/Types.scala | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 474958b86575..592b7b55fe16 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -208,6 +208,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 +222,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)) @@ -2571,6 +2572,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 From 9748c9bd54e278e65a29dff6c78fba5b1534ac00 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 3 Aug 2014 20:55:01 +0200 Subject: [PATCH 05/44] Changed first phase normalization and improvements to TreeTransform Two improvements to TreeTransform: 1) Added transformOther functionality which handles trees not handled by other parts 2) Passes down Mode.Pattern in the context when in a pattern. TreeTransform no longer normalizes unknown trees but passes them to transformOther. The former Companions phase has been renamed to FirstTransform. It now performs the following optimizations: - adds companion objects (this was done before) - other normalizations that were formally done in TreeTransform, - rewrite native methods to stubs (this was formally done in RefChecks) --- src/dotty/tools/dotc/Compiler.scala | 4 +- ...{Companions.scala => FirstTransform.scala} | 38 +++++- .../tools/dotc/transform/SuperAccessors.scala | 2 +- .../tools/dotc/transform/TreeTransform.scala | 126 ++++++++++-------- 4 files changed, 106 insertions(+), 64 deletions(-) rename src/dotty/tools/dotc/transform/{Companions.scala => FirstTransform.scala} (62%) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index fff4b517b6a4..3864917bac68 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -19,10 +19,10 @@ class Compiler { def phases: List[List[Phase]] = List( List(new FrontEnd), - List(new Companions), + List(new FirstTransform), List(new SuperAccessors), // pickling and refchecks goes here - List(new ElimRepeated, new ElimLocals), + List(/*new RefChecks,*/ new ElimRepeated, new ElimLocals), List(new ExtensionMethods), List(new TailRec), List(new PatternMatcher, diff --git a/src/dotty/tools/dotc/transform/Companions.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala similarity index 62% rename from src/dotty/tools/dotc/transform/Companions.scala rename to src/dotty/tools/dotc/transform/FirstTransform.scala index f6e3dbfdcba4..dbf4f3a2f0e4 100644 --- a/src/dotty/tools/dotc/transform/Companions.scala +++ b/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -4,18 +4,26 @@ package transform import core._ import Names._ import TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer} -import ast.Trees.flatten +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 TreeTransform with IdentityDenotTransformer { thisTransformer => import ast.tpd._ override def name = "companions" @@ -61,6 +69,30 @@ 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 +/* not yet enabled + 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/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index 5720c3bd921b..9516f84a01f5 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)) diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index 8e7c4f6d01f3..5dab44d11810 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -6,6 +6,7 @@ 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 scala.annotation.tailrec @@ -15,47 +16,48 @@ 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. - */ + * 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 { /** 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 */ + * before this phase starts + */ def runsAfterGroupsOf: Set[String] = Set.empty def prepareForIdent(tree: Ident)(implicit ctx: Context) = this @@ -121,6 +123,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,7 +135,7 @@ 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 = {} protected def mkTreeTransformer = new TreeTransformer { override def name: String = TreeTransform.this.name @@ -156,12 +159,11 @@ object TreeTransforms { 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 +172,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 +283,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 +352,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 +422,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 */ @@ -490,12 +494,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) @@ -833,6 +837,14 @@ object TreeTransforms { } 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), 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 +884,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 +1064,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,12 +1142,10 @@ 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) { From f87153bc5d74f66e2fcf22dc7282da31813430da Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 7 Aug 2014 20:25:23 +0200 Subject: [PATCH 06/44] Detect cycles and protected legal ones with LazyRefs Cycles are now detected early, when an info is first completed. Legal, f-bounded cycles are broken by a LazyRef, which will construct its type lazily. This makes checkBounds validation of AppliedTypeTrees work (in FirstTransform). Formerly, this stackoverflowed despite the laziness precautions in findMember. Todo: Do the same for class files coming from Java and Scala 2.x. --- .../tools/dotc/core/SymDenotations.scala | 2 + src/dotty/tools/dotc/core/TypeComparer.scala | 7 +- src/dotty/tools/dotc/core/Types.scala | 31 +++- .../tools/dotc/transform/FirstTransform.scala | 3 - src/dotty/tools/dotc/typer/Checking.scala | 144 ++++++++++++++++-- .../tools/dotc/typer/ErrorReporting.scala | 14 +- src/dotty/tools/dotc/typer/Namer.scala | 6 +- tests/neg/cycles.scala | 4 + 8 files changed, 183 insertions(+), 28 deletions(-) create mode 100644 tests/neg/cycles.scala diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index fcc01503f9b7..fd47ee4ece62 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1471,6 +1471,8 @@ object SymDenotations { def complete(denot: SymDenotation)(implicit ctx: Context): Unit = unsupported("complete") } + object NoCompleter extends NoCompleter + /** A lazy type for modules that points to the module class. * Needed so that `moduleClass` works before completion. * Completion of modules is always completion of the underlying diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 1e1d02be2fd5..7977124598b6 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 } } @@ -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 => diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 592b7b55fe16..a81d200d35b2 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: * @@ -379,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) @@ -436,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 @@ -599,7 +605,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 { @@ -609,6 +617,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 } @@ -643,6 +653,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)) @@ -1469,6 +1487,12 @@ object Types { unique(new CachedConstantType(value)) } + case class LazyRef(refFn: () => Type) extends UncachedProxyType with TermType { + lazy val ref = refFn() + override def underlying(implicit ctx: Context) = ref + override def toString = s"LazyRef($ref)" + } + // --- Refined Type --------------------------------------------------------- /** A refined type parent { refinement } @@ -2408,6 +2432,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) diff --git a/src/dotty/tools/dotc/transform/FirstTransform.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala index dbf4f3a2f0e4..d7010e8210fc 100644 --- a/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -83,14 +83,11 @@ class FirstTransform extends TreeTransform with IdentityDenotTransformer { thisT override def transformOther(tree: Tree)(implicit ctx: Context, info: TransformerInfo) = tree match { case tree: Import => EmptyTree case tree: NamedArg => tree.arg -/* not yet enabled 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/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 7da00e051367..cd2e9b55f13d 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,13 +22,125 @@ 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(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 = "" + + /** 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" + else { + val lo1 = try apply(lo) finally where = "lower bound" + val saved = nestedCycleOK + nestedCycleOK = true + try tp.derivedTypeBounds(lo1, apply(hi)) + finally { + nestedCycleOK = saved + where = "upper bound" + } + } + 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 { + // 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. + 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 throw ex + } + case _ => mapOver(tp) + } + } +} trait Checking { import tpd._ + import Checking._ + + /** 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: TypeBounds)(implicit ctx: Context): Type = { + val checker = new CheckNonCyclicMap + try checker.checkInfo(info) + catch { + case ex: CyclicReference => + ctx.error(i"illegal cyclic reference: ${checker.where} $info of $sym refers back to the type itself", sym.pos) + ErrorType + } + } /** Check that Java statics and packages can only be used in selections. */ @@ -32,17 +152,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 = 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/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 14404e220038..afa270d467fa 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) } } \ No newline at end of file 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] + From 058729ceac3354a2cc34490b528e76afb09ee0ce Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 8 Aug 2014 16:12:01 +0200 Subject: [PATCH 07/44] LazyRefs break cycles for unpickled types Insert LazyRefs to break cycles for F-bounded types that are unpickled or read from Java signatures. --- .../tools/dotc/core/SymDenotations.scala | 6 +- src/dotty/tools/dotc/core/Types.scala | 15 +++- .../dotc/core/pickling/ClassfileParser.scala | 7 +- .../tools/dotc/core/pickling/UnPickler.scala | 7 +- src/dotty/tools/dotc/typer/Checking.scala | 75 +++++++++++++------ src/dotty/tools/dotc/typer/Mode.scala | 1 + src/dotty/tools/dotc/typer/Namer.scala | 2 +- 7 files changed, 82 insertions(+), 31 deletions(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index fd47ee4ece62..c543a5a0cb29 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}") @@ -1034,7 +1034,7 @@ object SymDenotations { } private def computeBases(implicit ctx: Context): Unit = { - if (myBaseClasses eq Nil) throw new CyclicReference(this) + if (myBaseClasses eq Nil) throw CyclicReference(this) myBaseClasses = Nil val seen = new mutable.BitSet val locked = new mutable.BitSet @@ -1294,7 +1294,7 @@ object SymDenotations { basetp = computeBaseTypeRefOf(tp) baseTypeRefCache.put(tp, basetp) } else if (basetp == NoPrefix) { - throw new CyclicReference(this) + throw CyclicReference(this) } basetp case _ => diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index a81d200d35b2..8ec5c7295315 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1236,7 +1236,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 @@ -1487,7 +1487,7 @@ object Types { unique(new CachedConstantType(value)) } - case class LazyRef(refFn: () => Type) extends UncachedProxyType with TermType { + 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)" @@ -2689,10 +2689,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/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index cd2e9b55f13d..eb202cc159d1 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -58,7 +58,7 @@ object Checking { /** 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(implicit ctx: Context) extends TypeMap { + 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 @@ -72,6 +72,9 @@ object Checking { */ 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. */ @@ -79,15 +82,22 @@ object Checking { case tp @ TypeBounds(lo, hi) => if (lo eq hi) try tp.derivedTypeAlias(apply(lo)) - finally where = "alias" + finally { + where = "alias" + lastChecked = lo + } else { - val lo1 = try apply(lo) finally where = "lower bound" + 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 _ => @@ -103,44 +113,66 @@ object Checking { finally cycleOK = saved case tp @ TypeRef(pre, name) => try { - // Check info of typeref recursively, marking the referred symbol + // 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. - 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 + 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 throw ex + if (cycleOK) LazyRef(() => tp) + else if (reportErrors) throw ex + else tp } case _ => mapOver(tp) } } -} - -trait Checking { - - import tpd._ - import Checking._ /** 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: TypeBounds)(implicit ctx: Context): Type = { - val checker = new CheckNonCyclicMap + 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 => - ctx.error(i"illegal cyclic reference: ${checker.where} $info of $sym refers back to the type itself", sym.pos) - ErrorType - } + 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. */ @@ -252,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/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 afa270d467fa..1002abe4d35a 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -679,6 +679,6 @@ class Namer { typer: Typer => case _ => TypeAlias(abstractedRhsType, if (sym is Local) sym.variance else 0) } sym.info = NoCompleter - checkNonCyclic(sym, unsafeInfo) + checkNonCyclic(sym, unsafeInfo, reportErrors = true) } } \ No newline at end of file From 19b6a04486a4f9a2a7803d40d7ef6199cdeaf31c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 8 Aug 2014 21:37:49 +0200 Subject: [PATCH 08/44] Re-enabled checkbounds tests Now that F-bunded types are treated more robustly, we can check bounds for non-emptyness during Typer. This unvealed one wrong test (wonder how that passed scalac?), which got moved to neg. --- src/dotty/tools/dotc/typer/Typer.scala | 7 +++---- test/dotc/tests.scala | 1 + tests/{pos => neg}/t1279a.scala | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) rename tests/{pos => neg}/t1279a.scala (92%) 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/test/dotc/tests.scala b/test/dotc/tests.scala index fa577573a2af..8bbe30ad828f 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -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/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 From 9ec3a4ffa66e8639a1d887b8f6204abdfce8283d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 9 Aug 2014 12:05:26 +0200 Subject: [PATCH 09/44] prepareStats should span all statement transforms Statements are now transformed with the transform returned by prepareStats, analogoys to the other prepare methods. --- src/dotty/tools/dotc/transform/TreeTransform.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index 5dab44d11810..c39ca90cc837 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -1174,9 +1174,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) From 978a714a626886b172ea6c175f588913eb5f067b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 9 Aug 2014 12:11:02 +0200 Subject: [PATCH 10/44] Initial version of RefChecks This is still disabled, because the prepare machinery in transform does not work yet. Concretely, prepare ops need to return a new TreeTransform but that tree transform has an undefined phaase id. We need some architectural changes to disentangle transforms from phases. --- src/dotty/tools/dotc/Compiler.scala | 6 +- src/dotty/tools/dotc/core/Phases.scala | 1 - .../dotc/transform/OverridingPairs.scala | 6 +- src/dotty/tools/dotc/typer/RefChecks.scala | 1383 +++++++++++++++++ 4 files changed, 1389 insertions(+), 7 deletions(-) create mode 100644 src/dotty/tools/dotc/typer/RefChecks.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 3864917bac68..c0ba622ce774 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._ @@ -21,8 +21,8 @@ class Compiler { List(new FrontEnd), List(new FirstTransform), List(new SuperAccessors), - // pickling and refchecks goes here - List(/*new RefChecks,*/ new ElimRepeated, new ElimLocals), + // pickling goes here + List(/*new RefChecks, */new ElimRepeated, new ElimLocals), List(new ExtensionMethods), List(new TailRec), List(new PatternMatcher, diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala index aabde4cf9fb0..5544814f6674 100644 --- a/src/dotty/tools/dotc/core/Phases.scala +++ b/src/dotty/tools/dotc/core/Phases.scala @@ -10,7 +10,6 @@ import config.Printers._ import scala.collection.mutable.{ListBuffer, ArrayBuffer} import dotty.tools.dotc.transform.TreeTransforms.{TreeTransformer, TreeTransform} import dotty.tools.dotc.transform.TreeTransforms -import TreeTransforms.Separator import Periods._ trait Phases { 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/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala new file mode 100644 index 000000000000..a9b0f41ae38d --- /dev/null +++ b/src/dotty/tools/dotc/typer/RefChecks.scala @@ -0,0 +1,1383 @@ +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._ + +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 + */ + 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.name.decode).filter(member.name.decode != _).distinct + ctx.error( + msg + (if (others1.isEmpty) "" + else ";\n other members with override errors are: " + (others1 mkString ", ")), + 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.show + else + sym1.showLocated + + (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() = { + 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(Abstract)) { + 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.info.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) => + if (syms exists (_.symbol.isSetter)) syms filterNot (_.symbol.isGetter) + 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.matchingSymbol(bc, clazz.thisType) + if (impl == NoSymbol || (decl.owner isSubClass impl.owner)) { + 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.matchingSymbol(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).matches(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 (clazz.isDerivedValueClass) { + 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 + (if (stat.symbol.exists) m.updated(stat.symbol, (this, idx)) else m, idx + 1) + }._1 + var maxIndex: Int = Int.MinValue + var refPos: Position = _ + var refSym: Symbol = _ + + override def enterReference(sym: Symbol, pos: Position): Unit = + if (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(currentLevel: RefChecks.OptLevelInfo = RefChecks.NoLevelInfo) extends TreeTransform with IdentityDenotTransformer { thisTransformer => + + import tpd._ + + /** the following two members override abstract members in Transform */ + val name: String = "refchecks" + + override def prepareForStats(trees: List[Tree])(implicit ctx: Context) = { + println(i"preparing for $trees%; %, owner = ${ctx.owner}") + if (ctx.owner.isTerm) new RefChecks(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 (cls.isDerivedValueClass) + 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) = { + assert(ctx.phase.exists) + 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 + } + } +*/ + From 80ee8b1016adafe67cbd628228cff8156360219d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 9 Aug 2014 12:18:37 +0200 Subject: [PATCH 11/44] Made Phase a trait ... so that it can be combined with TreeTransform in a trait composition in a future dientanglement of TreeTransforms and Phases. --- src/dotty/tools/dotc/core/Phases.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala index 5544814f6674..5c569bc7ff8f 100644 --- a/src/dotty/tools/dotc/core/Phases.scala +++ b/src/dotty/tools/dotc/core/Phases.scala @@ -180,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 From bb1972b8d6eea212964b2e4295d0725d6d89e254 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 9 Aug 2014 13:38:18 +0200 Subject: [PATCH 12/44] Fixed fully qualified name of migration annotation --- src/dotty/tools/dotc/core/Definitions.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index f20882ce49c6..ac179ca9ee62 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -296,7 +296,7 @@ class Definitions { lazy val ScalaSignatureAnnot = ctx.requiredClass("scala.reflect.ScalaSignature") lazy val ScalaLongSignatureAnnot = ctx.requiredClass("scala.reflect.ScalaLongSignature") lazy val DeprecatedAnnot = ctx.requiredClass("scala.deprecated") - lazy val MigrationAnnot = ctx.requiredClass("scala.migration") + lazy val 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") From 9024f25e78a9fe5d27dd2c30aa24999d8901dab6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 9 Aug 2014 13:40:54 +0200 Subject: [PATCH 13/44] ParamAccessors are assumed to have definition Would be flagged as unimplemented members in refChecks otherwise --- src/dotty/tools/dotc/ast/TreeInfo.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala index 174de4d460b9..ca225993046c 100644 --- a/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -261,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 } From 57c6c85cbc953a3489ee8d16bb5b7be862183924 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 9 Aug 2014 13:45:29 +0200 Subject: [PATCH 14/44] Disentangle phases from treetransforms TreeTransforms are no longer phases. This allows to generate new transforms in prepare... methods without running into the problem that thee new transforms are undefined as phases. It also makes for a cleaner separation of concerns. --- src/dotty/tools/dotc/ElimLocals.scala | 4 +- src/dotty/tools/dotc/core/Decorators.scala | 2 +- src/dotty/tools/dotc/core/Phases.scala | 8 +- .../dotc/transform/CollectEntryPoints.scala | 4 +- .../tools/dotc/transform/Constructors.scala | 2 +- .../tools/dotc/transform/ElimRepeated.scala | 4 +- .../tools/dotc/transform/FirstTransform.scala | 4 +- .../dotc/transform/InterceptedMethods.scala | 2 +- src/dotty/tools/dotc/transform/LazyVals.scala | 4 +- .../tools/dotc/transform/Literalize.scala | 2 +- .../tools/dotc/transform/Nullarify.scala | 2 +- .../tools/dotc/transform/PatternMatcher.scala | 2 +- src/dotty/tools/dotc/transform/Splitter.scala | 2 +- src/dotty/tools/dotc/transform/TailRec.scala | 4 +- .../tools/dotc/transform/TreeTransform.scala | 107 +++++++------ .../tools/dotc/transform/TypeTestsCasts.scala | 2 +- .../dotc/transform/UncurryTreeTransform.scala | 2 +- src/dotty/tools/dotc/typer/RefChecks.scala | 142 +++++++++--------- test/test/transform/TreeTransformerTest.scala | 16 +- 19 files changed, 167 insertions(+), 148 deletions(-) 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/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/Phases.scala b/src/dotty/tools/dotc/core/Phases.scala index 5c569bc7ff8f..cecc5d1d7e23 100644 --- a/src/dotty/tools/dotc/core/Phases.scala +++ b/src/dotty/tools/dotc/core/Phases.scala @@ -8,7 +8,7 @@ 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 Periods._ @@ -80,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, @@ -90,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 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/FirstTransform.scala b/src/dotty/tools/dotc/transform/FirstTransform.scala index d7010e8210fc..39791918bd37 100644 --- a/src/dotty/tools/dotc/transform/FirstTransform.scala +++ b/src/dotty/tools/dotc/transform/FirstTransform.scala @@ -3,7 +3,7 @@ package transform import core._ import Names._ -import TreeTransforms.{TransformerInfo, TreeTransform, TreeTransformer} +import TreeTransforms.{TransformerInfo, MiniPhaseTransform, TreeTransformer} import ast.Trees._ import Flags._ import Types._ @@ -23,7 +23,7 @@ import NameOps._ * - checks the bounds of AppliedTypeTrees * - stubs out native methods */ -class FirstTransform extends TreeTransform with IdentityDenotTransformer { thisTransformer => +class FirstTransform extends MiniPhaseTransform with IdentityDenotTransformer { thisTransformer => import ast.tpd._ override def name = "companions" 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/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/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala index d3bec6f902c2..e69dd229c8b1 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._ diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index c39ca90cc837..129553264e2e 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -9,6 +9,7 @@ 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 @@ -50,16 +51,13 @@ object TreeTransforms { * (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 { + 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 @@ -136,10 +134,20 @@ object TreeTransforms { /** perform context-dependant initialization */ 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 = { @@ -147,16 +155,23 @@ 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) @@ -446,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() @@ -511,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) } @@ -522,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) } @@ -533,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) } @@ -544,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) } @@ -555,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) } @@ -566,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) } @@ -577,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) } @@ -588,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) } @@ -599,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) } @@ -610,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) } @@ -621,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) } @@ -632,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) } @@ -643,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) } @@ -654,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) } @@ -665,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) } @@ -676,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) } @@ -687,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) } @@ -698,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) } @@ -709,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) } @@ -720,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) } @@ -731,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) } @@ -742,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) } @@ -753,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) } @@ -764,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) } @@ -775,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) } @@ -786,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) } @@ -797,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) } @@ -808,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) } @@ -819,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) } @@ -830,7 +845,7 @@ 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) } @@ -840,7 +855,7 @@ object TreeTransforms { 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), info) + val t = trans.transformOther(tree)(ctx.withPhase(trans.phase), info) transformSingle(t, cur + 1) } else tree } @@ -1151,8 +1166,8 @@ object TreeTransforms { 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) diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index b209f7647ecf..dbdb5c9029b0 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" 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/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala index a9b0f41ae38d..23a810638a9a 100644 --- a/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/src/dotty/tools/dotc/typer/RefChecks.scala @@ -665,92 +665,96 @@ import RefChecks._ * if (false) A else B --> B * - macro definitions are eliminated. */ -class RefChecks(currentLevel: RefChecks.OptLevelInfo = RefChecks.NoLevelInfo) extends TreeTransform with IdentityDenotTransformer { thisTransformer => +class RefChecks extends MiniPhase with IdentityDenotTransformer { thisTransformer => import tpd._ - /** the following two members override abstract members in Transform */ val name: String = "refchecks" - override def prepareForStats(trees: List[Tree])(implicit ctx: Context) = { - println(i"preparing for $trees%; %, owner = ${ctx.owner}") - if (ctx.owner.isTerm) new RefChecks(new LevelInfo(currentLevel.levelAndIndex, trees)) - else this - } + val treeTransform = new Transform(NoLevelInfo) - override def transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = trees + 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 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 transformStats(trees: List[Tree])(implicit ctx: Context, info: TransformerInfo): List[Tree] = trees - override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo) = { - checkDeprecatedOvers(tree) - if (tree.symbol is Macro) EmptyTree else tree - } + 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 transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { - val cls = ctx.owner - checkOverloadedRestrictions(cls) - checkAllOverrides(cls) - checkAnyValSubclass(cls) - if (cls.isDerivedValueClass) - cls.primaryConstructor.makeNotPrivateAfter(NoSymbol, thisTransformer) // SI-6601, must be done *after* pickler! - tree - } + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo) = { + checkDeprecatedOvers(tree) + if (tree.symbol is Macro) EmptyTree else 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 transformTemplate(tree: Template)(implicit ctx: Context, info: TransformerInfo) = { + val cls = ctx.owner + checkOverloadedRestrictions(cls) + checkAllOverrides(cls) + checkAnyValSubclass(cls) + if (cls.isDerivedValueClass) + cls.primaryConstructor.makeNotPrivateAfter(NoSymbol, thisTransformer) // SI-6601, must be done *after* pickler! + tree + } - override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = { - assert(ctx.phase.exists) - checkUndesiredProperties(tree.symbol, tree.pos) - currentLevel.enterReference(tree.symbol, tree.pos) - 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 transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = { - checkUndesiredProperties(tree.symbol, tree.pos) - tree - } + override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = { + assert(ctx.phase.exists) + checkUndesiredProperties(tree.symbol, tree.pos) + currentLevel.enterReference(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) - } + override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo) = { + checkUndesiredProperties(tree.symbol, tree.pos) + tree } - 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 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 transformNew(tree: New)(implicit ctx: Context, info: TransformerInfo) = { - currentLevel.enterReference(tree.tpe.typeSymbol, tree.pos) - 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 + } } } 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 = { From 96cd350429582ab22605064900f5ea6913aba80e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 9 Aug 2014 18:12:40 +0200 Subject: [PATCH 15/44] Module vals are lazy Should have lazy flag set, otherwise forward reference checking would fail for modules. Note: LazyVals needed to be disabled because it also should transform module vals, but didn't do this so far because it only tested the Lazy flag. It turned out the module val transformation exposed some bugs in lazy vals in that LazyVals creates symbols as a side effect and enters them into scopes. Such mutations are allowed onyl in very specific cases (essentially only for local throw-away scopes). --- src/dotty/tools/dotc/Compiler.scala | 2 +- src/dotty/tools/dotc/core/Flags.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index c0ba622ce774..a63a69b9bdd9 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -26,7 +26,7 @@ class Compiler { 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/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index c527cef62209..5931347b3b13 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -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 From 80b866f37014b9f6cfbb61e36231df037570fb95 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 9 Aug 2014 18:16:18 +0200 Subject: [PATCH 16/44] In refined printer, show only source modifier flags. Used to show "mutable var x: T". --- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index acba22afeda5..4768eab69dc9 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -486,7 +486,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 { From bbf777a729e5b6e8c8a75466c42004d3ff4c5d32 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 9 Aug 2014 18:17:59 +0200 Subject: [PATCH 17/44] Narrow assertion for Scope#enter Scopes are also used in overriding pairs, and there multiple types with the same name can be entered in a scope. So the assert to the contrary should be limited to typechecking only. --- src/dotty/tools/dotc/core/Scopes.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 From c2cdd3a3dca2a629923327046b213addd93499fc Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 10 Aug 2014 17:48:52 +0200 Subject: [PATCH 18/44] More targeted eta-lifting Eta-lifting picked some arbitrary base type. It turned out that i94-nada failed once we add a product trait to case classes (in the next commit) because Eta-Kifting picked Product as the base type, even though the target type was bounded by Monad. We now change the scheme so that the target type is included in the lifting, in order to avoid that we lift to useless types. --- src/dotty/tools/dotc/config/Printers.scala | 1 + .../tools/dotc/core/TypeApplications.scala | 23 +++++++++++-------- src/dotty/tools/dotc/core/TypeComparer.scala | 6 ++--- .../tools/dotc/typer/VarianceChecker.scala | 3 ++- 4 files changed, 19 insertions(+), 14 deletions(-) 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/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 7977124598b6..48286fe471f7 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -454,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 @@ -673,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 @@ -768,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/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 => From f606a47894271540bc7b21eb8c7b5d85bd560f57 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 10 Aug 2014 17:51:02 +0200 Subject: [PATCH 19/44] Add Product{1,2} supertrait to case classes Case classes with arity <= 1 now also get a ProductN parent trait. This is necessary because we inherit productArity and Element methods in case classes from the ProdictN trait. Needed to add Product0 for this, which is not defined in Scala2.x. --- src/dotty/tools/dotc/ast/Desugar.scala | 4 ++-- src/dotty/tools/dotc/core/Definitions.scala | 2 +- src/scala/Product0.scala | 24 +++++++++++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 src/scala/Product0.scala diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index c3d0f9c3a8a3..671fbef51f8f 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -312,12 +312,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 diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index ac179ca9ee62..e1808b181ecf 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -394,7 +394,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 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()) +} + From d388e948c0e747362256bf8ab034385a0b3f0f91 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 11 Aug 2014 10:28:51 +0200 Subject: [PATCH 20/44] Fix Object's scope after ersure After erasure, former Any members become Object members. Also, fixed some typos and added some TODOs on addBridges. --- src/dotty/tools/dotc/transform/Erasure.scala | 32 ++++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index e5fd9d85d328..fdc0c168a3bb 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -35,10 +35,22 @@ 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 owner = ref.owner + ref.copySymDenotation( + owner = if (owner eq defn.AnyClass) defn.ObjectClass else owner, + info = transformInfo(ref.symbol, ref.info)) + } case ref => ref.derivedSingleDenotation(ref.symbol, erasure(ref.info)) } @@ -296,9 +308,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 +328,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 } } From b89c4afca19cb77e17c059e75982c537094f1ec8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 11 Aug 2014 10:30:06 +0200 Subject: [PATCH 21/44] Add CaseAccessor flag for case accessors Was missing before. --- src/dotty/tools/dotc/ast/Desugar.scala | 3 ++- src/dotty/tools/dotc/core/Flags.scala | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 671fbef51f8f..8ead43d78217 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -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/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index 5931347b3b13..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 */ From c7f817f80d7a07c5d3a15eac03e4520097d81ecb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 11 Aug 2014 10:32:48 +0200 Subject: [PATCH 22/44] Added some more methods as infix tree operations: asInstance/isInstance/ensureConforms/and/or. They replace some former "mk..." methods. --- src/dotty/tools/dotc/ast/tpd.scala | 24 ++++++++++++------- src/dotty/tools/dotc/transform/Erasure.scala | 2 +- .../tools/dotc/transform/SuperAccessors.scala | 2 +- .../tools/dotc/transform/TypeTestsCasts.scala | 4 ++-- 4 files changed, 19 insertions(+), 13 deletions(-) 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/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index fdc0c168a3bb..c571d836d84d 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -157,7 +157,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) } } diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index 9516f84a01f5..2496cf5a922d 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -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/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index dbdb5c9029b0..ef0359136028 100644 --- a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -59,7 +59,7 @@ class TypeTestsCasts extends MiniPhaseTransform { case _ => erased2 match { case Literal(Constant(true)) => erased1 - case _ => mkAnd(erased1, erased2) + case _ => erased1 and erased2 } } } @@ -68,7 +68,7 @@ class TypeTestsCasts extends MiniPhaseTransform { 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) From 98d258399767fa0577ba243cd7301878ec995508 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 11 Aug 2014 10:42:41 +0200 Subject: [PATCH 23/44] New methods in Definitions Also renamed Boolean_and/or to _&&/||, to make it conform to naming convention for other Definition operators. --- src/dotty/tools/dotc/core/Definitions.scala | 20 ++++++++++++++++---- src/dotty/tools/dotc/transform/TailRec.scala | 2 +- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index e1808b181ecf..123e6e671fb1 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -181,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) @@ -204,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) @@ -273,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 diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala index e69dd229c8b1..fd414a225ee8 100644 --- a/src/dotty/tools/dotc/transform/TailRec.scala +++ b/src/dotty/tools/dotc/transform/TailRec.scala @@ -287,7 +287,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete 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) => From ab63413f2cbd82111aa33e625d22f7e9924776eb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 11 Aug 2014 10:45:11 +0200 Subject: [PATCH 24/44] Sperate matchingDecl and mathingMember. RefChecks needs both methods. --- .../tools/dotc/core/SymDenotations.scala | 19 +++++++++++++----- src/dotty/tools/dotc/typer/RefChecks.scala | 20 +++++++++---------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index c543a5a0cb29..a6fce8eee188 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -714,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. */ @@ -737,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] = @@ -757,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 @@ -767,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 _ => diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala index 23a810638a9a..85e4696efeb9 100644 --- a/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/src/dotty/tools/dotc/typer/RefChecks.scala @@ -17,6 +17,7 @@ import scala.util.{Try, Success, Failure} import config.{ScalaVersion, NoScalaVersion} import typer.ErrorReporting._ import DenotTransformers._ +import ValueClasses.isDerivedValueClass object RefChecks { import tpd._ @@ -296,7 +297,7 @@ object RefChecks { printMixinOverrideErrors() // Verifying a concrete class has nothing unimplemented. - if (!clazz.is(Abstract)) { + if (!clazz.is(AbstractOrTrait)) { val abstractErrors = new mutable.ListBuffer[String] def abstractErrorMessage = // a little formatting polish @@ -348,8 +349,8 @@ object RefChecks { val missingMethods = grouped.toList flatMap { case (name, syms) => - if (syms exists (_.symbol.isSetter)) syms filterNot (_.symbol.isGetter) - else syms + val withoutSetters = syms filterNot (_.symbol.isSetter) + if (withoutSetters.nonEmpty) withoutSetters else syms } def stubImplementations: List[String] = { @@ -461,7 +462,7 @@ object RefChecks { def checkNoAbstractDecls(bc: Symbol): Unit = { for (decl <- bc.info.decls) { if (decl.is(Deferred) && !ignoreDeferred(decl)) { - val impl = decl.matchingSymbol(bc, clazz.thisType) + val impl = decl.matchingMember(clazz.thisType) if (impl == NoSymbol || (decl.owner isSubClass impl.owner)) { abstractClassError(false, "there is a deferred declaration of " + infoString(decl) + " which is not implemented in a subclass" + err.abstractVarMessage(decl)) @@ -485,7 +486,7 @@ object RefChecks { // 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.matchingSymbol(defn.ObjectClass, defn.ObjectType) + val overridden = decl.matchingDecl(defn.ObjectClass, defn.ObjectType) if (overridden.is(Final)) ctx.error("trait cannot redefine final method from class AnyRef", decl.pos) } @@ -605,7 +606,7 @@ object RefChecks { /** Verify classes extending AnyVal meet the requirements */ private def checkAnyValSubclass(clazz: Symbol)(implicit ctx: Context) = - if (clazz.isDerivedValueClass) { + 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)) @@ -632,7 +633,7 @@ object RefChecks { var refSym: Symbol = _ override def enterReference(sym: Symbol, pos: Position): Unit = - if (sym.owner.isTerm) + if (sym.exists && sym.owner.isTerm) levelAndIndex.get(sym) match { case Some((level, idx)) if (level.maxIndex < idx) => level.maxIndex = idx @@ -676,7 +677,7 @@ class RefChecks extends MiniPhase with IdentityDenotTransformer { thisTransforme 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}") + // println(i"preparing for $trees%; %, owner = ${ctx.owner}") if (ctx.owner.isTerm) new Transform(new LevelInfo(currentLevel.levelAndIndex, trees)) else this } @@ -706,7 +707,7 @@ class RefChecks extends MiniPhase with IdentityDenotTransformer { thisTransforme checkOverloadedRestrictions(cls) checkAllOverrides(cls) checkAnyValSubclass(cls) - if (cls.isDerivedValueClass) + if (isDerivedValueClass(cls)) cls.primaryConstructor.makeNotPrivateAfter(NoSymbol, thisTransformer) // SI-6601, must be done *after* pickler! tree } @@ -721,7 +722,6 @@ class RefChecks extends MiniPhase with IdentityDenotTransformer { thisTransforme } override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo) = { - assert(ctx.phase.exists) checkUndesiredProperties(tree.symbol, tree.pos) currentLevel.enterReference(tree.symbol, tree.pos) tree From ce535898e6f9758840db4da7cbc18a57e632a26a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 11 Aug 2014 11:13:56 +0200 Subject: [PATCH 25/44] Add SyntheticMethods miniphase New phase for Synthetic Method generation. Scala 2.x did it in Typer, but it's cleaner to do it in a separate phase. --- src/dotty/tools/dotc/Compiler.scala | 2 +- src/dotty/tools/dotc/core/Definitions.scala | 2 + src/dotty/tools/dotc/core/Types.scala | 6 +- .../dotc/transform/SyntheticMethods.scala | 174 ++++++++++++++++++ test/dotc/tests.scala | 2 +- 5 files changed, 183 insertions(+), 3 deletions(-) create mode 100644 src/dotty/tools/dotc/transform/SyntheticMethods.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index a63a69b9bdd9..13fcc8a70345 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -19,7 +19,7 @@ class Compiler { def phases: List[List[Phase]] = List( List(new FrontEnd), - List(new FirstTransform), + List(new FirstTransform, new SyntheticMethods), List(new SuperAccessors), // pickling goes here List(/*new RefChecks, */new ElimRepeated, new ElimLocals), diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 123e6e671fb1..82ed3ce1d6cd 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -423,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/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 8ec5c7295315..50c729df141f 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -1149,7 +1149,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 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/test/dotc/tests.scala b/test/dotc/tests.scala index 8bbe30ad828f..6035b661a3e2 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:synthetic,tailrec" ) val twice = List("#runs", "2", "-YnoDoubleBindings") From 974f70670f191550e94aeb2c463c73033d0ace4d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 11 Aug 2014 12:08:04 +0200 Subject: [PATCH 26/44] Avoid printing expanded names If a TypeRef with an expanded name is an alias type, print the alias instead. --- src/dotty/tools/dotc/printing/RefinedPrinter.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 4768eab69dc9..d5454679616a 100644 --- a/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -109,7 +109,7 @@ 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) { + if ((tp.symbol is TypeParam | TypeArgument | ExpandedName) && !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) From a3cc9388dc6fcfe92c1a34b079131a26f55e8179 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 11 Aug 2014 12:09:40 +0200 Subject: [PATCH 27/44] Use _1 intstead of get for accessing unary case class parameters Now that caes classes always inherit from ProductX, we can avoid the special case. (We need to define _1 anyway to implement Product1). --- src/dotty/tools/dotc/ast/Desugar.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 8ead43d78217..855269a2ff0d 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 { From 81745e2779fe13076e73538e66e3352cb9cd9cf3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 12 Aug 2014 16:48:29 +0200 Subject: [PATCH 28/44] Fix a problem due to different type inference --- src/dotty/tools/dotc/typer/Applications.scala | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index b506e7e33a7a..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 } From 843f9769f5fc5572f06e058af65e43f81b8d432d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 12 Aug 2014 18:02:26 +0200 Subject: [PATCH 29/44] Two fixes in desugar (1) set position of companion object def (2) companions of case classes taking multiple parameter lists do not inherit from a function type (reason: we can't straightforwardly converyt a curried method with multiple parameter lists to a function value). --- src/dotty/tools/dotc/ast/Desugar.scala | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 855269a2ff0d..705d14f03203 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -324,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: @@ -338,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 From 472d7114da29c869e3fa2e97b7045b797937737b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 12 Aug 2014 18:03:35 +0200 Subject: [PATCH 30/44] Add matchesLoosely For overriding checks we need a concept where a val can match a def. Normal matches does not provide this. --- src/dotty/tools/dotc/core/Denotations.scala | 2 +- src/dotty/tools/dotc/core/Types.scala | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 022230ff81a5..f4e9746c7130 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 diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 50c729df141f..e5c86ef44c2f 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -544,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 */ From ab8ee535fc2f16f3ece55326e58dec6171614829 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 12 Aug 2014 18:06:35 +0200 Subject: [PATCH 31/44] mergeDenot should prefer concerete over deferred. Previously it did this only sometimes. Now it always prefers concrete over deferred unless the deferred info is a proper subtype of the concrete info. --- src/dotty/tools/dotc/core/Denotations.scala | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index f4e9746c7130..dceec47a2659 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -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 { From deaa0d8bdcd5592e124acfbca1a1414365b667d7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 13 Aug 2014 11:21:37 +0200 Subject: [PATCH 32/44] Package denotations are never transformed Packages should always have a single denotation, which is invariant for all transformations. Package members should always be entered in the first phase, and should never be entered after a given phase. This reflects the fact that package members correspond to classfiles. Once you create a classfile, it stays around and is available from the start of the next run. Also, we need to prevent multiple denotation versions of packages from hanging on to stale symbols. It would not be enough to replace a package member by a newly compiled one; if packages had multiple denotations we'd have to do this for all of them. --- src/dotty/tools/dotc/core/Denotations.scala | 4 +++- src/dotty/tools/dotc/core/Symbols.scala | 6 +++++- src/dotty/tools/dotc/core/transform/Erasure.scala | 15 +++++++++------ .../tools/dotc/printing/RefinedPrinter.scala | 3 ++- 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index dceec47a2659..fa2292c60b71 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -553,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/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/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 d5454679616a..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 | ExpandedName) && !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) From ece76567bc230bdbcea21f57a4e6ebf0d8d6434d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 13 Aug 2014 11:25:29 +0200 Subject: [PATCH 33/44] Erasure should copy denotations only if they are changed. --- src/dotty/tools/dotc/transform/Erasure.scala | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index c571d836d84d..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" @@ -46,10 +46,15 @@ class Erasure extends Phase with DenotTransformer { ) } else { - val owner = ref.owner - ref.copySymDenotation( - owner = if (owner eq defn.AnyClass) defn.ObjectClass else owner, - info = transformInfo(ref.symbol, ref.info)) + 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)) @@ -359,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) From 3558e07b8f3a604bfd67c721cdec3eb9db29e7eb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 13 Aug 2014 11:27:16 +0200 Subject: [PATCH 34/44] Change access boundary of protected[this] Should be `base`, was the class enclosing the definition. --- src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index a6fce8eee188..a19f824e54ed 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -805,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 From 34f73ded3519a1df7d278685f3f33facd00f1c58 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 13 Aug 2014 11:31:39 +0200 Subject: [PATCH 35/44] Fix and enable RefChecks RefChecks is now enabled. Some of the tests had to be fixed to be refchecks-correct. --- src/dotty/tools/dotc/Compiler.scala | 2 +- src/dotty/tools/dotc/typer/RefChecks.scala | 35 ++++++++++++++------ test/dotc/tests.scala | 2 +- tests/pos/desugar.scala | 37 ++++++++++++---------- tests/pos/hk.scala | 6 ++-- tests/pos/inferred.scala | 26 +++++++-------- tests/pos/opassign.scala | 18 +++++------ tests/pos/t1832.scala | 2 +- tests/pos/tycons.scala | 4 +-- tests/pos/typers.scala | 2 +- 10 files changed, 76 insertions(+), 58 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 13fcc8a70345..09e58d186de4 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -22,7 +22,7 @@ class Compiler { List(new FirstTransform, new SyntheticMethods), List(new SuperAccessors), // pickling goes here - List(/*new RefChecks, */new ElimRepeated, new ElimLocals), + List(new RefChecks, new ElimRepeated, new ElimLocals), List(new ExtensionMethods), List(new TailRec), List(new PatternMatcher, diff --git a/src/dotty/tools/dotc/typer/RefChecks.scala b/src/dotty/tools/dotc/typer/RefChecks.scala index 85e4696efeb9..67cb1745f6cc 100644 --- a/src/dotty/tools/dotc/typer/RefChecks.scala +++ b/src/dotty/tools/dotc/typer/RefChecks.scala @@ -102,6 +102,8 @@ object RefChecks { * 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 @@ -116,11 +118,15 @@ object RefChecks { case List(MixinOverrideError(_, msg)) => ctx.error(msg, clazz.pos) case MixinOverrideError(member, msg) :: others => - val others1 = others.map(_.member.name.decode).filter(member.name.decode != _).distinct - ctx.error( - msg + (if (others1.isEmpty) "" - else ";\n other members with override errors are: " + (others1 mkString ", ")), - clazz.pos) + 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) } } @@ -129,9 +135,9 @@ object RefChecks { def infoString0(sym: Symbol, showLocation: Boolean) = { val sym1 = sym.underlyingSymbol - if (showLocation) sym1.show + if (showLocation) sym1.showLocated else - sym1.showLocated + + sym1.show + (if (sym1.isAliasType) ", which equals " + self.memberInfo(sym1) else if (sym1.isAbstractType) " with bounds" + self.memberInfo(sym1) else if (sym1.is(Module)) "" @@ -182,6 +188,8 @@ object RefChecks { } 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)) @@ -331,7 +339,7 @@ object RefChecks { // 2. Check that only abstract classes have deferred members def checkNoAbstractMembers(): Unit = { // Avoid spurious duplicates: first gather any missing members. - val missing = clazz.info.abstractTermMembers.filterNot(ignoreDeferred) + 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) @@ -464,6 +472,9 @@ object RefChecks { 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)) } @@ -499,7 +510,7 @@ object RefChecks { def hasMatchingSym(inclazz: Symbol, member: Symbol): Boolean = { def isSignatureMatch(sym: Symbol) = !sym.isTerm || - clazz.thisType.memberInfo(sym).matches(member.info) + 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, @@ -626,7 +637,11 @@ object RefChecks { override val levelAndIndex: LevelAndIndex = ((outerLevelAndIndex, 0) /: stats) {(mi, stat) => val (m, idx) = mi - (if (stat.symbol.exists) m.updated(stat.symbol, (this, idx)) else m, idx + 1) + 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 = _ diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 6035b661a3e2..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:synthetic,tailrec" + "-Ycheck:refchecks,tailrec" ) val twice = List("#runs", "2", "-YnoDoubleBindings") 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 { From a878d19e48455ca600f3fbe6e36c6ddd687e14ff Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 6 Jul 2014 18:26:24 +0200 Subject: [PATCH 36/44] Changes to tree copying 1) Add copiers with default arguments, to avoid boilerplate 2) All copiers are now curried wrt first argument (which is the original tree). We already make use of the new features in cpy.DefDef, but not yet elsewhere. --- src/dotty/tools/dotc/ast/Desugar.scala | 71 +++-- src/dotty/tools/dotc/ast/Trees.scala | 246 +++++++++++------- src/dotty/tools/dotc/ast/tpd.scala | 24 +- src/dotty/tools/dotc/ast/untpd.scala | 76 +++--- src/dotty/tools/dotc/parsing/Parsers.scala | 16 +- .../tools/dotc/transform/Constructors.scala | 3 +- src/dotty/tools/dotc/transform/Erasure.scala | 8 +- .../dotc/transform/ExtensionMethods.scala | 4 +- .../tools/dotc/transform/MacroTransform.scala | 6 +- .../tools/dotc/transform/Nullarify.scala | 11 +- .../tools/dotc/transform/PatternMatcher.scala | 2 +- .../tools/dotc/transform/SuperAccessors.scala | 12 +- .../dotc/transform/SyntheticMethods.scala | 4 +- src/dotty/tools/dotc/transform/TailRec.scala | 25 +- .../tools/dotc/transform/TreeTransform.scala | 60 ++--- .../tools/dotc/transform/TypeTestsCasts.scala | 2 +- .../dotc/transform/UncurryTreeTransform.scala | 2 +- src/dotty/tools/dotc/typer/Applications.scala | 12 +- src/dotty/tools/dotc/typer/EtaExpansion.scala | 10 +- src/dotty/tools/dotc/typer/Namer.scala | 6 +- src/dotty/tools/dotc/typer/ReTyper.scala | 6 +- src/dotty/tools/dotc/typer/Typer.scala | 96 +++---- test/test/DeSugarTest.scala | 24 +- test/test/transform/TreeTransformerTest.scala | 16 +- 24 files changed, 398 insertions(+), 344 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 705d14f03203..5e947fc07c17 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -72,13 +72,11 @@ object desugar { /** A type definition copied from `tdef` with a rhs typetree derived from it */ def derivedTypeParam(tdef: TypeDef) = - cpy.TypeDef(tdef, tdef.mods, tdef.name, - new DerivedFromParamTree() withPos tdef.rhs.pos watching tdef, tdef.tparams) // todo: copy type params + cpy.TypeDef(tdef)(rhs = new DerivedFromParamTree() withPos tdef.rhs.pos watching tdef) /** A value definition copied from `vdef` with a tpt typetree derived from it */ def derivedTermParam(vdef: ValDef) = - cpy.ValDef(vdef, vdef.mods, vdef.name, - new DerivedFromParamTree() withPos vdef.tpt.pos watching vdef, vdef.rhs) + cpy.ValDef(vdef)(tpt = new DerivedFromParamTree() withPos vdef.tpt.pos watching vdef) // ----- Desugar methods ------------------------------------------------- @@ -98,7 +96,7 @@ object desugar { // I don't see a problem with that but if there is one we can avoid it by making a copy here. val setterParam = makeSyntheticParameter(tpt = (new SetterParamTree).watching(vdef)) val setterRhs = if (vdef.rhs.isEmpty) EmptyTree else unitLiteral - val setter = cpy.DefDef(vdef, + val setter = cpy.DefDef(vdef)( mods | Accessor, name.setterName, Nil, (setterParam :: Nil) :: Nil, TypeTree(defn.UnitType), setterRhs) // rhs gets filled in later, when field is generated and getter has parameters Thicket(vdef, setter) @@ -124,14 +122,14 @@ object desugar { val DefDef(mods, name, tparams, vparamss, tpt, rhs) = meth val epbuf = new ListBuffer[ValDef] val tparams1 = tparams mapConserve { - case tparam @ TypeDef(mods, name, ContextBounds(tbounds, cxbounds)) => + case tparam @ TypeDef(_, _, ContextBounds(tbounds, cxbounds)) => for (cxbound <- cxbounds) { val paramFlags: FlagSet = if (isPrimaryConstructor) PrivateLocalParamAccessor else Param val epname = (nme.EVIDENCE_PARAM_PREFIX.toString + epbuf.length).toTermName epbuf += ValDef(Modifiers(paramFlags | Implicit), epname, cxbound, EmptyTree) } - cpy.TypeDef(tparam, mods, name, tbounds, tparam.tparams) + cpy.TypeDef(tparam)(rhs = tbounds) case tparam => tparam } @@ -146,7 +144,7 @@ object desugar { case _ => vparamss :+ evidenceParams } - cpy.DefDef(meth, mods, name, tparams1, vparamss1, tpt, rhs) + cpy.DefDef(meth)(tparams = tparams1, vparamss = vparamss1) } /** The longest prefix of parameter lists in vparamss whose total length does not exceed `n` */ @@ -159,7 +157,7 @@ object desugar { } def normalizedVparamss = vparamss map (_ map (vparam => - cpy.ValDef(vparam, vparam.mods, vparam.name, vparam.tpt, EmptyTree))) + cpy.ValDef(vparam)(vparam.mods, vparam.name, vparam.tpt, EmptyTree))) def defaultGetters(vparamss: List[List[ValDef]], n: Int): List[DefDef] = vparamss match { case (vparam :: vparams) :: vparamss1 => @@ -182,9 +180,9 @@ object desugar { val defGetters = defaultGetters(vparamss, 0) if (defGetters.isEmpty) meth1 else { - val mods1 = meth1.mods | DefaultParameterized - val meth2 = cpy.DefDef(meth1, meth1.mods | DefaultParameterized, - meth1.name, meth1.tparams, normalizedVparamss, meth1.tpt, meth1.rhs) + val meth2 = cpy.DefDef(meth1)( + mods = meth1.mods | DefaultParameterized, + vparamss = normalizedVparamss) Thicket(meth2 :: defGetters) } } @@ -198,23 +196,25 @@ object desugar { def typeDef(tdef: TypeDef)(implicit ctx: Context): Tree = { val TypeDef(mods, name, rhs) = tdef if (mods is PrivateLocalParam) { - val tparam = cpy.TypeDef(tdef, - mods &~ PrivateLocal | ExpandedName, name.expandedName(ctx.owner), rhs, tdef.tparams) - val alias = cpy.TypeDef(tdef, - Modifiers(PrivateLocalParamAccessor | Synthetic | mods.flags & VarianceFlags), - name, refOfDef(tparam)) + val tparam = cpy.TypeDef(tdef)( + mods = mods &~ PrivateLocal | ExpandedName, + name = name.expandedName(ctx.owner)) + val alias = cpy.TypeDef(tdef)( + mods = Modifiers(PrivateLocalParamAccessor | Synthetic | mods.flags & VarianceFlags), + rhs = refOfDef(tparam), + tparams = Nil) Thicket(tparam, alias) } - else cpy.TypeDef(tdef, mods, name, rhs, tdef.tparams) + else cpy.TypeDef(tdef)(mods, name, rhs, tdef.tparams) // TODO: why copy? } private val synthetic = Modifiers(Synthetic) private def toDefParam(tparam: TypeDef) = - cpy.TypeDef(tparam, Modifiers(Param), tparam.name, tparam.rhs, tparam.tparams) + cpy.TypeDef(tparam)(mods = Modifiers(Param)) private def toDefParam(vparam: ValDef) = - cpy.ValDef(vparam, Modifiers(Param | vparam.mods.flags & Implicit), vparam.name, vparam.tpt, vparam.rhs) + cpy.ValDef(vparam)(Modifiers(Param | vparam.mods.flags & Implicit), vparam.name, vparam.tpt, vparam.rhs) /** The expansion of a class definition. See inline comments for what is involved */ def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = { @@ -239,13 +239,12 @@ object desugar { ListOfNil } else constr1.vparamss.nestedMap(toDefParam) - val constr = cpy.DefDef(constr1, - constr1.mods, constr1.name, constrTparams, constrVparamss, constr1.tpt, constr1.rhs) + val constr = cpy.DefDef(constr1)(tparams = constrTparams, vparamss = constrVparamss) // Add constructor type parameters to auxiliary constructors val normalizedBody = body map { case ddef: DefDef if ddef.name.isConstructorName => - cpy.DefDef(ddef, ddef.mods, ddef.name, constrTparams, ddef.vparamss, ddef.tpt, ddef.rhs) + cpy.DefDef(ddef)(tparams = constrTparams) case stat => stat } @@ -297,9 +296,9 @@ object desugar { def copyDefault(vparam: ValDef) = makeAnnotated(defn.UncheckedVarianceAnnot, refOfDef(vparam)) val copyFirstParams = derivedVparamss.head.map(vparam => - cpy.ValDef(vparam, vparam.mods, vparam.name, vparam.tpt, copyDefault(vparam))) + cpy.ValDef(vparam)(vparam.mods, vparam.name, vparam.tpt, copyDefault(vparam))) val copyRestParamss = derivedVparamss.tail.nestedMap(vparam => - cpy.ValDef(vparam, vparam.mods, vparam.name, vparam.tpt, EmptyTree)) + cpy.ValDef(vparam)(vparam.mods, vparam.name, vparam.tpt, EmptyTree)) DefDef(synthetic, nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr) :: Nil } copyMeths ::: isDefinedMeth :: productArityMeth :: productElemMeths.toList @@ -379,22 +378,22 @@ object desugar { val self1 = { val selfType = if (self.tpt.isEmpty) classTypeRef else self.tpt if (self.isEmpty) self - else cpy.ValDef(self, self.mods | SelfName, self.name, selfType, self.rhs) + else cpy.ValDef(self)(self.mods | SelfName, self.name, selfType, self.rhs) } val cdef1 = { val originalTparams = constr1.tparams.toIterator val originalVparams = constr1.vparamss.toIterator.flatten val tparamAccessors = derivedTparams map { tdef => - cpy.TypeDef(tdef, originalTparams.next.mods, tdef.name, tdef.rhs, tdef.tparams) + 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 | caseAccessor, 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, - tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths)) + cpy.TypeDef(cdef)(mods, name, + cpy.Template(impl)(constr, parents1, self1, + tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths), Nil) } // install the watch on classTycon @@ -417,7 +416,7 @@ object desugar { def moduleDef(mdef: ModuleDef)(implicit ctx: Context): Tree = { val ModuleDef(mods, name, tmpl @ Template(constr, parents, self, body)) = mdef if (mods is Package) - PackageDef(Ident(name), cpy.ModuleDef(mdef, mods &~ Package, nme.PACKAGE, tmpl) :: Nil) + PackageDef(Ident(name), cpy.ModuleDef(mdef)(mods &~ Package, nme.PACKAGE, tmpl) :: Nil) else { val clsName = name.moduleClassName val clsRef = Ident(clsName) @@ -426,7 +425,7 @@ object desugar { if (!selfTpt.isEmpty) ctx.error("object definition may not have a self type", self.pos) val clsSelf = ValDef(selfMods, selfName, SingletonTypeTree(Ident(name)), selfRhs) .withPos(self.pos orElse tmpl.pos.startPos) - val clsTmpl = cpy.Template(tmpl, constr, parents, clsSelf, body) + val clsTmpl = cpy.Template(tmpl)(constr, parents, clsSelf, body) val cls = TypeDef(mods.toTypeFlags & AccessFlags | ModuleClassCreationFlags, clsName, clsTmpl) Thicket(modul, classDef(cls)) } @@ -503,7 +502,7 @@ object desugar { */ def block(tree: Block)(implicit ctx: Context): Block = tree.expr match { case EmptyTree => - cpy.Block(tree, tree.stats, + cpy.Block(tree)(tree.stats, unitLiteral withPos (if (tree.stats.isEmpty) tree.pos else tree.pos.endPos)) case _ => tree @@ -516,7 +515,7 @@ object desugar { val TypeBoundsTree(lo, hi) = tree val lo1 = if (lo.isEmpty) untpd.TypeTree(defn.NothingType) else lo val hi1 = if (hi.isEmpty) untpd.TypeTree(defn.AnyType) else hi - cpy.TypeBoundsTree(tree, lo1, hi1) + cpy.TypeBoundsTree(tree)(lo1, hi1) } /** Make closure corresponding to function. @@ -563,7 +562,7 @@ object desugar { */ def makeBinop(left: Tree, op: Name, right: Tree): Tree = { def assignToNamedArg(arg: Tree) = arg match { - case Assign(Ident(name), rhs) => cpy.NamedArg(arg, name, rhs) + case Assign(Ident(name), rhs) => cpy.NamedArg(arg)(name, rhs) case _ => arg } if (isLeftAssoc(op)) { diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index e19221841dd0..41ef88b54611 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -422,7 +422,7 @@ object Trees { case class Ident[-T >: Untyped] private[ast] (name: Name) extends RefTree[T] { type ThisTree[-T >: Untyped] = Ident[T] - def withName(name: Name)(implicit ctx: Context): untpd.Ident = untpd.cpy.Ident(this, name) + def withName(name: Name)(implicit ctx: Context): untpd.Ident = untpd.cpy.Ident(this)(name) def qualifier: Tree[T] = genericEmptyTree } @@ -433,7 +433,7 @@ object Trees { case class Select[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name) extends RefTree[T] { type ThisTree[-T >: Untyped] = Select[T] - def withName(name: Name)(implicit ctx: Context): untpd.Select = untpd.cpy.Select(this, qualifier, name) + def withName(name: Name)(implicit ctx: Context): untpd.Select = untpd.cpy.Select(this)(qualifier, name) } class SelectWithSig[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name, val sig: Signature) @@ -620,7 +620,7 @@ object Trees { case class SelectFromTypeTree[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name) extends RefTree[T] { type ThisTree[-T >: Untyped] = SelectFromTypeTree[T] - def withName(name: Name)(implicit ctx: Context): untpd.SelectFromTypeTree = untpd.cpy.SelectFromTypeTree(this, qualifier, name) + def withName(name: Name)(implicit ctx: Context): untpd.SelectFromTypeTree = untpd.cpy.SelectFromTypeTree(this)(qualifier, name) } /** left & right */ @@ -666,7 +666,7 @@ object Trees { extends NameTree[T] with DefTree[T] with PatternTree[T] { type ThisTree[-T >: Untyped] = Bind[T] override def envelope: Position = pos union initialPos - def withName(name: Name)(implicit ctx: Context): untpd.Bind = untpd.cpy.Bind(this, name, body) + def withName(name: Name)(implicit ctx: Context): untpd.Bind = untpd.cpy.Bind(this)(name, body) } /** tree_1 | ... | tree_n */ @@ -697,7 +697,7 @@ object Trees { case class ValDef[-T >: Untyped] private[ast] (mods: Modifiers[T], name: TermName, tpt: Tree[T], rhs: Tree[T]) extends ValOrDefDef[T] { type ThisTree[-T >: Untyped] = ValDef[T] - def withName(name: Name)(implicit ctx: Context): untpd.ValDef = untpd.cpy.ValDef(this, mods, name.toTermName, tpt, rhs) + def withName(name: Name)(implicit ctx: Context): untpd.ValDef = untpd.cpy.ValDef(this)(mods, name.toTermName, tpt, rhs) assert(isEmpty || tpt != genericEmptyTree) } @@ -705,7 +705,7 @@ object Trees { case class DefDef[-T >: Untyped] private[ast] (mods: Modifiers[T], name: TermName, tparams: List[TypeDef[T]], vparamss: List[List[ValDef[T]]], tpt: Tree[T], rhs: Tree[T]) extends ValOrDefDef[T] { type ThisTree[-T >: Untyped] = DefDef[T] - def withName(name: Name)(implicit ctx: Context): untpd.DefDef = untpd.cpy.DefDef(this, mods, name.toTermName, tparams, vparamss, tpt, rhs) + def withName(name: Name)(implicit ctx: Context): untpd.DefDef = untpd.cpy.DefDef(this)(name = name.toTermName) assert(tpt != genericEmptyTree) } @@ -717,7 +717,7 @@ object Trees { case class TypeDef[-T >: Untyped] private[ast] (mods: Modifiers[T], name: TypeName, rhs: Tree[T]) extends MemberDef[T] { type ThisTree[-T >: Untyped] = TypeDef[T] - def withName(name: Name)(implicit ctx: Context): untpd.TypeDef = untpd.cpy.TypeDef(this, mods, name.toTypeName, rhs, tparams) + def withName(name: Name)(implicit ctx: Context): untpd.TypeDef = untpd.cpy.TypeDef(this)(mods, name.toTypeName, rhs, tparams) /** Is this a definition of a class? */ def isClassDef = rhs.isInstanceOf[Template[_]] @@ -919,179 +919,237 @@ object Trees { def finalize(tree: Tree, copied: untpd.Tree): copied.ThisTree[T] = postProcess(tree, copied withPos tree.pos) - def Ident(tree: Tree, name: Name): Ident = tree match { + def Ident(tree: Tree)(name: Name): Ident = tree match { case tree: BackquotedIdent => if (name == tree.name) tree else finalize(tree, new BackquotedIdent(name)) case tree: Ident if (name == tree.name) => tree case _ => finalize(tree, untpd.Ident(name)) } - def Select(tree: Tree, qualifier: Tree, name: Name): Select = tree match { + def Select(tree: Tree)(qualifier: Tree, name: Name): Select = tree match { case tree: SelectWithSig => if ((qualifier eq tree.qualifier) && (name == tree.name)) tree else finalize(tree, new SelectWithSig(qualifier, name, tree.sig)) case tree: Select if (qualifier eq tree.qualifier) && (name == tree.name) => tree case _ => finalize(tree, untpd.Select(qualifier, name)) } - def This(tree: Tree, qual: TypeName): This = tree match { + def This(tree: Tree)(qual: TypeName): This = tree match { case tree: This if (qual == tree.qual) => tree case _ => finalize(tree, untpd.This(qual)) } - def Super(tree: Tree, qual: Tree, mix: TypeName): Super = tree match { + def Super(tree: Tree)(qual: Tree, mix: TypeName): Super = tree match { case tree: Super if (qual eq tree.qual) && (mix == tree.mix) => tree case _ => finalize(tree, untpd.Super(qual, mix)) } - def Apply(tree: Tree, fun: Tree, args: List[Tree]): Apply = tree match { + def Apply(tree: Tree)(fun: Tree, args: List[Tree]): Apply = tree match { case tree: Apply if (fun eq tree.fun) && (args eq tree.args) => tree case _ => finalize(tree, untpd.Apply(fun, args)) } - def TypeApply(tree: Tree, fun: Tree, args: List[Tree]): TypeApply = tree match { + def TypeApply(tree: Tree)(fun: Tree, args: List[Tree]): TypeApply = tree match { case tree: TypeApply if (fun eq tree.fun) && (args eq tree.args) => tree case _ => finalize(tree, untpd.TypeApply(fun, args)) } - def Literal(tree: Tree, const: Constant): Literal = tree match { + def Literal(tree: Tree)(const: Constant): Literal = tree match { case tree: Literal if (const == tree.const) => tree case _ => finalize(tree, untpd.Literal(const)) } - def New(tree: Tree, tpt: Tree): New = tree match { + def New(tree: Tree)(tpt: Tree): New = tree match { case tree: New if (tpt eq tree.tpt) => tree case _ => finalize(tree, untpd.New(tpt)) } - def Pair(tree: Tree, left: Tree, right: Tree): Pair = tree match { + def Pair(tree: Tree)(left: Tree, right: Tree): Pair = tree match { case tree: Pair if (left eq tree.left) && (right eq tree.right) => tree case _ => finalize(tree, untpd.Pair(left, right)) } - def Typed(tree: Tree, expr: Tree, tpt: Tree): Typed = tree match { + def Typed(tree: Tree)(expr: Tree, tpt: Tree): Typed = tree match { case tree: Typed if (expr eq tree.expr) && (tpt eq tree.tpt) => tree case _ => finalize(tree, untpd.Typed(expr, tpt)) } - def NamedArg(tree: Tree, name: Name, arg: Tree): NamedArg = tree match { + def NamedArg(tree: Tree)(name: Name, arg: Tree): NamedArg = tree match { case tree: NamedArg if (name == tree.name) && (arg eq tree.arg) => tree case _ => finalize(tree, untpd.NamedArg(name, arg)) } - def Assign(tree: Tree, lhs: Tree, rhs: Tree): Assign = tree match { + def Assign(tree: Tree)(lhs: Tree, rhs: Tree): Assign = tree match { case tree: Assign if (lhs eq tree.lhs) && (rhs eq tree.rhs) => tree case _ => finalize(tree, untpd.Assign(lhs, rhs)) } - def Block(tree: Tree, stats: List[Tree], expr: Tree): Block = tree match { + def Block(tree: Tree)(stats: List[Tree], expr: Tree): Block = tree match { case tree: Block if (stats eq tree.stats) && (expr eq tree.expr) => tree case _ => finalize(tree, untpd.Block(stats, expr)) } - def If(tree: Tree, cond: Tree, thenp: Tree, elsep: Tree): If = tree match { + def If(tree: Tree)(cond: Tree, thenp: Tree, elsep: Tree): If = tree match { case tree: If if (cond eq tree.cond) && (thenp eq tree.thenp) && (elsep eq tree.elsep) => tree case _ => finalize(tree, untpd.If(cond, thenp, elsep)) } - def Closure(tree: Tree, env: List[Tree], meth: Tree, tpt: Tree): Closure = tree match { + def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree): Closure = tree match { case tree: Closure if (env eq tree.env) && (meth eq tree.meth) && (tpt eq tree.tpt) => tree case _ => finalize(tree, untpd.Closure(env, meth, tpt)) } - def Match(tree: Tree, selector: Tree, cases: List[CaseDef]): Match = tree match { + def Match(tree: Tree)(selector: Tree, cases: List[CaseDef]): Match = tree match { case tree: Match if (selector eq tree.selector) && (cases eq tree.cases) => tree case _ => finalize(tree, untpd.Match(selector, cases)) } - def CaseDef(tree: Tree, pat: Tree, guard: Tree, body: Tree): CaseDef = tree match { + def CaseDef(tree: Tree)(pat: Tree, guard: Tree, body: Tree): CaseDef = tree match { case tree: CaseDef if (pat eq tree.pat) && (guard eq tree.guard) && (body eq tree.body) => tree case _ => finalize(tree, untpd.CaseDef(pat, guard, body)) } - def Return(tree: Tree, expr: Tree, from: Tree): Return = tree match { + def Return(tree: Tree)(expr: Tree, from: Tree): Return = tree match { case tree: Return if (expr eq tree.expr) && (from eq tree.from) => tree case _ => finalize(tree, untpd.Return(expr, from)) } - def Try(tree: Tree, expr: Tree, handler: Tree, finalizer: Tree): Try = tree match { + def Try(tree: Tree)(expr: Tree, handler: Tree, finalizer: Tree): Try = tree match { case tree: Try if (expr eq tree.expr) && (handler eq tree.handler) && (finalizer eq tree.finalizer) => tree case _ => finalize(tree, untpd.Try(expr, handler, finalizer)) } - def Throw(tree: Tree, expr: Tree): Throw = tree match { + def Throw(tree: Tree)(expr: Tree): Throw = tree match { case tree: Throw if (expr eq tree.expr) => tree case _ => finalize(tree, untpd.Throw(expr)) } - def SeqLiteral(tree: Tree, elems: List[Tree]): SeqLiteral = tree match { + def SeqLiteral(tree: Tree)(elems: List[Tree]): SeqLiteral = tree match { case tree: JavaSeqLiteral => if (elems eq tree.elems) tree else finalize(tree, new JavaSeqLiteral(elems)) case tree: SeqLiteral if (elems eq tree.elems) => tree case _ => finalize(tree, untpd.SeqLiteral(elems)) } - def TypeTree(tree: Tree, original: Tree): TypeTree = tree match { + def TypeTree(tree: Tree)(original: Tree): TypeTree = tree match { case tree: TypeTree if original eq tree.original => tree case _ => finalize(tree, untpd.TypeTree(original)) } - def SingletonTypeTree(tree: Tree, ref: Tree): SingletonTypeTree = tree match { + def SingletonTypeTree(tree: Tree)(ref: Tree): SingletonTypeTree = tree match { case tree: SingletonTypeTree if (ref eq tree.ref) => tree case _ => finalize(tree, untpd.SingletonTypeTree(ref)) } - def SelectFromTypeTree(tree: Tree, qualifier: Tree, name: Name): SelectFromTypeTree = tree match { + def SelectFromTypeTree(tree: Tree)(qualifier: Tree, name: Name): SelectFromTypeTree = tree match { case tree: SelectFromTypeTree if (qualifier eq tree.qualifier) && (name == tree.name) => tree case _ => finalize(tree, untpd.SelectFromTypeTree(qualifier, name)) } - def AndTypeTree(tree: Tree, left: Tree, right: Tree): AndTypeTree = tree match { + def AndTypeTree(tree: Tree)(left: Tree, right: Tree): AndTypeTree = tree match { case tree: AndTypeTree if (left eq tree.left) && (right eq tree.right) => tree case _ => finalize(tree, untpd.AndTypeTree(left, right)) } - def OrTypeTree(tree: Tree, left: Tree, right: Tree): OrTypeTree = tree match { + def OrTypeTree(tree: Tree)(left: Tree, right: Tree): OrTypeTree = tree match { case tree: OrTypeTree if (left eq tree.left) && (right eq tree.right) => tree case _ => finalize(tree, untpd.OrTypeTree(left, right)) } - def RefinedTypeTree(tree: Tree, tpt: Tree, refinements: List[Tree]): RefinedTypeTree = tree match { + def RefinedTypeTree(tree: Tree)(tpt: Tree, refinements: List[Tree]): RefinedTypeTree = tree match { case tree: RefinedTypeTree if (tpt eq tree.tpt) && (refinements eq tree.refinements) => tree case _ => finalize(tree, untpd.RefinedTypeTree(tpt, refinements)) } - def AppliedTypeTree(tree: Tree, tpt: Tree, args: List[Tree]): AppliedTypeTree = tree match { + def AppliedTypeTree(tree: Tree)(tpt: Tree, args: List[Tree]): AppliedTypeTree = tree match { case tree: AppliedTypeTree if (tpt eq tree.tpt) && (args eq tree.args) => tree case _ => finalize(tree, untpd.AppliedTypeTree(tpt, args)) } - def ByNameTypeTree(tree: Tree, result: Tree): ByNameTypeTree = tree match { + def ByNameTypeTree(tree: Tree)(result: Tree): ByNameTypeTree = tree match { case tree: ByNameTypeTree if (result eq tree.result) => tree case _ => finalize(tree, untpd.ByNameTypeTree(result)) } - def TypeBoundsTree(tree: Tree, lo: Tree, hi: Tree): TypeBoundsTree = tree match { + def TypeBoundsTree(tree: Tree)(lo: Tree, hi: Tree): TypeBoundsTree = tree match { case tree: TypeBoundsTree if (lo eq tree.lo) && (hi eq tree.hi) => tree case _ => finalize(tree, untpd.TypeBoundsTree(lo, hi)) } - def Bind(tree: Tree, name: Name, body: Tree): Bind = tree match { + def Bind(tree: Tree)(name: Name, body: Tree): Bind = tree match { case tree: Bind if (name eq tree.name) && (body eq tree.body) => tree case _ => finalize(tree, untpd.Bind(name, body)) } - def Alternative(tree: Tree, trees: List[Tree]): Alternative = tree match { + def Alternative(tree: Tree)(trees: List[Tree]): Alternative = tree match { case tree: Alternative if (trees eq tree.trees) => tree case _ => finalize(tree, untpd.Alternative(trees)) } - def UnApply(tree: Tree, fun: Tree, implicits: List[Tree], patterns: List[Tree]): UnApply = tree match { + def UnApply(tree: Tree)(fun: Tree, implicits: List[Tree], patterns: List[Tree]): UnApply = tree match { case tree: UnApply if (fun eq tree.fun) && (implicits eq tree.implicits) && (patterns eq tree.patterns) => tree case _ => finalize(tree, untpd.UnApply(fun, implicits, patterns)) } - def ValDef(tree: Tree, mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree): ValDef = tree match { + def ValDef(tree: Tree)(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree): ValDef = tree match { case tree: ValDef if (mods == tree.mods) && (name == tree.name) && (tpt eq tree.tpt) && (rhs eq tree.rhs) => tree case _ => finalize(tree, untpd.ValDef(mods, name, tpt, rhs)) } - def DefDef(tree: Tree, mods: Modifiers, name: TermName, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree): DefDef = tree match { + def DefDef(tree: Tree)(mods: Modifiers, name: TermName, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: Tree): DefDef = tree match { case tree: DefDef if (mods == tree.mods) && (name == tree.name) && (tparams eq tree.tparams) && (vparamss eq tree.vparamss) && (tpt eq tree.tpt) && (rhs eq tree.rhs) => tree case _ => finalize(tree, untpd.DefDef(mods, name, tparams, vparamss, tpt, rhs)) } - def TypeDef(tree: Tree, mods: Modifiers, name: TypeName, rhs: Tree, tparams: List[untpd.TypeDef] = Nil): TypeDef = tree match { + def TypeDef(tree: Tree)(mods: Modifiers, name: TypeName, rhs: Tree, tparams: List[untpd.TypeDef]): TypeDef = tree match { case tree: TypeDef if (mods == tree.mods) && (name == tree.name) && (rhs eq tree.rhs) && (tparams eq tree.tparams) => tree case _ => finalize(tree, untpd.TypeDef(mods, name, tparams, rhs)) } - def Template(tree: Tree, constr: DefDef, parents: List[Tree], self: ValDef, body: List[Tree]): Template = tree match { + def Template(tree: Tree)(constr: DefDef, parents: List[Tree], self: ValDef, body: List[Tree]): Template = tree match { case tree: Template if (constr eq tree.constr) && (parents eq tree.parents) && (self eq tree.self) && (body eq tree.body) => tree case _ => finalize(tree, untpd.Template(constr, parents, self, body)) } - def Import(tree: Tree, expr: Tree, selectors: List[untpd.Tree]): Import = tree match { + def Import(tree: Tree)(expr: Tree, selectors: List[untpd.Tree]): Import = tree match { case tree: Import if (expr eq tree.expr) && (selectors eq tree.selectors) => tree case _ => finalize(tree, untpd.Import(expr, selectors)) } - def PackageDef(tree: Tree, pid: RefTree, stats: List[Tree]): PackageDef = tree match { + def PackageDef(tree: Tree)(pid: RefTree, stats: List[Tree]): PackageDef = tree match { case tree: PackageDef if (pid eq tree.pid) && (stats eq tree.stats) => tree case _ => finalize(tree, untpd.PackageDef(pid, stats)) } - def Annotated(tree: Tree, annot: Tree, arg: Tree): Annotated = tree match { + def Annotated(tree: Tree)(annot: Tree, arg: Tree): Annotated = tree match { case tree: Annotated if (annot eq tree.annot) && (arg eq tree.arg) => tree case _ => finalize(tree, untpd.Annotated(annot, arg)) } - def Thicket(tree: Tree, trees: List[Tree]): Thicket = tree match { + def Thicket(tree: Tree)(trees: List[Tree]): Thicket = tree match { case tree: Thicket if (trees eq tree.trees) => tree case _ => finalize(tree, untpd.Thicket(trees)) } + + // Copier methods with default arguments; these demand that the original tree + // is of the same class as the copy + + def Select(tree: Select)(qualifier: Tree = tree.qualifier, name: Name = tree.name): Select = + Select(tree: Tree)(qualifier, name) + def Super(tree: Super)(qual: Tree = tree.qual, mix: TypeName = tree.mix): Super = + Super(tree: Tree)(qual, mix) + def Apply(tree: Apply)(fun: Tree = tree.fun, args: List[Tree] = tree.args): Apply = + Apply(tree: Tree)(fun, args) + def TypeApply(tree: TypeApply)(fun: Tree = tree.fun, args: List[Tree] = tree.args): TypeApply = + TypeApply(tree: Tree)(fun, args) + def Typed(tree: Typed)(expr: Tree = tree.expr, tpt: Tree = tree.tpt): Typed = + Typed(tree: Tree)(expr, tpt) + def NamedArg(tree: NamedArg)(name: Name = tree.name, arg: Tree = tree.arg): NamedArg = + NamedArg(tree: Tree)(name, arg) + def Assign(tree: Assign)(lhs: Tree = tree.lhs, rhs: Tree = tree.rhs): Assign = + Assign(tree: Tree)(lhs, rhs) + def Block(tree: Block)(stats: List[Tree] = tree.stats, expr: Tree = tree.expr): Block = + Block(tree: Tree)(stats, expr) + def If(tree: If)(cond: Tree = tree.cond, thenp: Tree = tree.thenp, elsep: Tree = tree.elsep): If = + If(tree: Tree)(cond, thenp, elsep) + def Closure(tree: Closure)(env: List[Tree] = tree.env, meth: Tree = tree.meth, tpt: Tree = tree.tpt): Closure = + Closure(tree: Tree)(env, meth, tpt) + def Match(tree: Match)(selector: Tree = tree.selector, cases: List[CaseDef] = tree.cases): Match = + Match(tree: Tree)(selector, cases) + def CaseDef(tree: CaseDef)(pat: Tree = tree.pat, guard: Tree = tree.guard, body: Tree = tree.body): CaseDef = + CaseDef(tree: Tree)(pat, guard, body) + def Return(tree: Return)(expr: Tree = tree.expr, from: Tree = tree.from): Return = + Return(tree: Tree)(expr, from) + def Try(tree: Try)(expr: Tree = tree.expr, handler: Tree = tree.handler, finalizer: Tree = tree.finalizer): Try = + Try(tree: Tree)(expr, handler, finalizer) + def SelectFromTypeTree(tree: SelectFromTypeTree)(qualifier: Tree = tree.qualifier, name: Name = tree.name): + SelectFromTypeTree = SelectFromTypeTree(tree: Tree)(qualifier, name) + def RefinedTypeTree(tree: RefinedTypeTree)(tpt: Tree = tree.tpt, refinements: List[Tree] = tree.refinements): + RefinedTypeTree = RefinedTypeTree(tree: Tree)(tpt, refinements) + def AppliedTypeTree(tree: AppliedTypeTree)(tpt: Tree = tree.tpt, args: List[Tree] = tree.args): AppliedTypeTree = + AppliedTypeTree(tree: Tree)(tpt, args) + def TypeBoundsTree(tree: TypeBoundsTree)(lo: Tree = tree.lo, hi: Tree = tree.hi): TypeBoundsTree = + TypeBoundsTree(tree: Tree)(lo, hi) + def Bind(tree: Bind)(name: Name = tree.name, body: Tree = tree.body): Bind = + Bind(tree: Tree)(name, body) + def UnApply(tree: UnApply)(fun: Tree = tree.fun, implicits: List[Tree] = tree.implicits, patterns: List[Tree] = tree.patterns): UnApply = + UnApply(tree: Tree)(fun, implicits, patterns) + def ValDef(tree: ValDef)(mods: Modifiers = tree.mods, name: TermName = tree.name, tpt: Tree = tree.tpt, rhs: Tree = tree.rhs): ValDef = + ValDef(tree: Tree)(mods, name, tpt, rhs) + def DefDef(tree: DefDef)(mods: Modifiers = tree.mods, name: TermName = tree.name, tparams: List[TypeDef] = tree.tparams, vparamss: List[List[ValDef]] = tree.vparamss, tpt: Tree = tree.tpt, rhs: Tree = tree.rhs): DefDef = + DefDef(tree: Tree)(mods, name, tparams, vparamss, tpt, rhs) + def TypeDef(tree: TypeDef)(mods: Modifiers = tree.mods, name: TypeName = tree.name, rhs: Tree = tree.rhs, tparams: List[untpd.TypeDef] = tree.tparams): TypeDef = + TypeDef(tree: Tree)(mods, name, rhs, tparams) + def Template(tree: Template)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, self: ValDef = tree.self, body: List[Tree] = tree.body): Template = + Template(tree: Tree)(constr, parents, self, body) + def Import(tree: Import)(expr: Tree = tree.expr, selectors: List[untpd.Tree] = tree.selectors): Import = + Import(tree: Tree)(expr, selectors) + def PackageDef(tree: PackageDef)(pid: RefTree = tree.pid, stats: List[Tree] = tree.stats): PackageDef = + PackageDef(tree: Tree)(pid, stats) + def Annotated(tree: Annotated)(annot: Tree = tree.annot, arg: Tree = tree.arg): Annotated = + Annotated(tree: Tree)(annot, arg) } abstract class TreeMap(val cpy: TreeCopier = inst.cpy) { @@ -1100,88 +1158,88 @@ object Trees { case Ident(name) => tree case Select(qualifier, name) => - cpy.Select(tree, transform(qualifier), name) + cpy.Select(tree)(transform(qualifier), name) case This(qual) => tree case Super(qual, mix) => - cpy.Super(tree, transform(qual), mix) + cpy.Super(tree)(transform(qual), mix) case Apply(fun, args) => - cpy.Apply(tree, transform(fun), transform(args)) + cpy.Apply(tree)(transform(fun), transform(args)) case TypeApply(fun, args) => - cpy.TypeApply(tree, transform(fun), transform(args)) + cpy.TypeApply(tree)(transform(fun), transform(args)) + case Literal(const) => + tree case New(tpt) => - cpy.New(tree, transform(tpt)) + cpy.New(tree)(transform(tpt)) + case Pair(left, right) => + cpy.Pair(tree)(transform(left), transform(right)) case Typed(expr, tpt) => - cpy.Typed(tree, transform(expr), transform(tpt)) + cpy.Typed(tree)(transform(expr), transform(tpt)) case NamedArg(name, arg) => - cpy.NamedArg(tree, name, transform(arg)) + cpy.NamedArg(tree)(name, transform(arg)) case Assign(lhs, rhs) => - cpy.Assign(tree, transform(lhs), transform(rhs)) + cpy.Assign(tree)(transform(lhs), transform(rhs)) + case Block(stats, expr) => + cpy.Block(tree)(transformStats(stats), transform(expr)) + case If(cond, thenp, elsep) => + cpy.If(tree)(transform(cond), transform(thenp), transform(elsep)) case Closure(env, meth, tpt) => - cpy.Closure(tree, transform(env), transform(meth), transform(tpt)) + cpy.Closure(tree)(transform(env), transform(meth), transform(tpt)) + case Match(selector, cases) => + cpy.Match(tree)(transform(selector), transformSub(cases)) + case CaseDef(pat, guard, body) => + cpy.CaseDef(tree)(transform(pat), transform(guard), transform(body)) case Return(expr, from) => - cpy.Return(tree, transform(expr), transformSub(from)) + cpy.Return(tree)(transform(expr), transformSub(from)) + case Try(block, handler, finalizer) => + cpy.Try(tree)(transform(block), transform(handler), transform(finalizer)) case Throw(expr) => - cpy.Throw(tree, transform(expr)) + cpy.Throw(tree)(transform(expr)) + case SeqLiteral(elems) => + cpy.SeqLiteral(tree)(transform(elems)) case TypeTree(original) => tree case SingletonTypeTree(ref) => - cpy.SingletonTypeTree(tree, transform(ref)) + cpy.SingletonTypeTree(tree)(transform(ref)) case SelectFromTypeTree(qualifier, name) => - cpy.SelectFromTypeTree(tree, transform(qualifier), name) + cpy.SelectFromTypeTree(tree)(transform(qualifier), name) case AndTypeTree(left, right) => - cpy.AndTypeTree(tree, transform(left), transform(right)) + cpy.AndTypeTree(tree)(transform(left), transform(right)) case OrTypeTree(left, right) => - cpy.OrTypeTree(tree, transform(left), transform(right)) + cpy.OrTypeTree(tree)(transform(left), transform(right)) case RefinedTypeTree(tpt, refinements) => - cpy.RefinedTypeTree(tree, transform(tpt), transformSub(refinements)) + cpy.RefinedTypeTree(tree)(transform(tpt), transformSub(refinements)) case AppliedTypeTree(tpt, args) => - cpy.AppliedTypeTree(tree, transform(tpt), transform(args)) + cpy.AppliedTypeTree(tree)(transform(tpt), transform(args)) case ByNameTypeTree(result) => - cpy.ByNameTypeTree(tree, transform(result)) + cpy.ByNameTypeTree(tree)(transform(result)) case TypeBoundsTree(lo, hi) => - cpy.TypeBoundsTree(tree, transform(lo), transform(hi)) + cpy.TypeBoundsTree(tree)(transform(lo), transform(hi)) case Bind(name, body) => - cpy.Bind(tree, name, transform(body)) + cpy.Bind(tree)(name, transform(body)) case Alternative(trees) => - cpy.Alternative(tree, transform(trees)) + cpy.Alternative(tree)(transform(trees)) case UnApply(fun, implicits, patterns) => - cpy.UnApply(tree, transform(fun), transform(implicits), transform(patterns)) + cpy.UnApply(tree)(transform(fun), transform(implicits), transform(patterns)) case EmptyValDef => tree case ValDef(mods, name, tpt, rhs) => - cpy.ValDef(tree, mods, name, transform(tpt), transform(rhs)) + cpy.ValDef(tree)(mods, name, transform(tpt), transform(rhs)) case DefDef(mods, name, tparams, vparamss, tpt, rhs) => - cpy.DefDef(tree, mods, name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt), transform(rhs)) + cpy.DefDef(tree)(mods, name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt), transform(rhs)) case tree @ TypeDef(mods, name, rhs) => - cpy.TypeDef(tree, mods, name, transform(rhs), tree.tparams) + cpy.TypeDef(tree)(mods, name, transform(rhs), tree.tparams) case Template(constr, parents, self, body) => - cpy.Template(tree, transformSub(constr), transform(parents), transformSub(self), transformStats(body)) + cpy.Template(tree)(transformSub(constr), transform(parents), transformSub(self), transformStats(body)) case Import(expr, selectors) => - cpy.Import(tree, transform(expr), selectors) + cpy.Import(tree)(transform(expr), selectors) case PackageDef(pid, stats) => - cpy.PackageDef(tree, transformSub(pid), transformStats(stats)) + cpy.PackageDef(tree)(transformSub(pid), transformStats(stats)) + case Annotated(annot, arg) => + cpy.Annotated(tree)(transform(annot), transform(arg)) case Thicket(trees) => val trees1 = transform(trees) if (trees1 eq trees) tree else Thicket(trees1) - case Literal(const) => - tree - case Pair(left, right) => - cpy.Pair(tree, transform(left), transform(right)) - case Block(stats, expr) => - cpy.Block(tree, transformStats(stats), transform(expr)) - case If(cond, thenp, elsep) => - cpy.If(tree, transform(cond), transform(thenp), transform(elsep)) - case Match(selector, cases) => - cpy.Match(tree, transform(selector), transformSub(cases)) - case CaseDef(pat, guard, body) => - cpy.CaseDef(tree, transform(pat), transform(guard), transform(body)) - case Try(block, handler, finalizer) => - cpy.Try(tree, transform(block), transform(handler), transform(finalizer)) - case SeqLiteral(elems) => - cpy.SeqLiteral(tree, transform(elems)) - case Annotated(annot, arg) => - cpy.Annotated(tree, transform(annot), transform(arg)) } def transformStats(trees: List[Tree])(implicit ctx: Context): List[Tree] = diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 81f48cd37d38..1ba3f223a627 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -440,49 +440,49 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { case tree@Select(qualifier, name) => - val tree1 = cpy.Select(tree, transform(qualifier), name) + val tree1 = cpy.Select(tree)(transform(qualifier), name) propagateType(tree, tree1) case tree@Pair(left, right) => val left1 = transform(left) val right1 = transform(right) - val tree1 = cpy.Pair(tree, left1, right1) + val tree1 = cpy.Pair(tree)(left1, right1) propagateType(tree, tree1) case tree@Block(stats, expr) => val stats1 = transform(stats) val expr1 = transform(expr) - val tree1 = cpy.Block(tree, stats1, expr1) + val tree1 = cpy.Block(tree)(stats1, expr1) propagateType(tree, tree1) case tree@If(cond, thenp, elsep) => val cond1 = transform(cond) val thenp1 = transform(thenp) val elsep1 = transform(elsep) - val tree1 = cpy.If(tree, cond1, thenp1, elsep1) + val tree1 = cpy.If(tree)(cond1, thenp1, elsep1) propagateType(tree, tree1) case tree@Match(selector, cases) => val selector1 = transform(selector) val cases1 = transformSub(cases) - val tree1 = cpy.Match(tree, selector1, cases1) + val tree1 = cpy.Match(tree)(selector1, cases1) propagateType(tree, tree1) case tree@CaseDef(pat, guard, body) => val pat1 = transform(pat) val guard1 = transform(guard) val body1 = transform(body) - val tree1 = cpy.CaseDef(tree, pat1, guard1, body1) + val tree1 = cpy.CaseDef(tree)(pat1, guard1, body1) propagateType(tree, tree1) case tree@Try(block, handler, finalizer) => val expr1 = transform(block) val handler1 = transform(handler) val finalizer1 = transform(finalizer) - val tree1 = cpy.Try(tree, expr1, handler1, finalizer1) + val tree1 = cpy.Try(tree)(expr1, handler1, finalizer1) propagateType(tree, tree1) case tree@SeqLiteral(elems) => val elems1 = transform(elems) - val tree1 = cpy.SeqLiteral(tree, elems1) + val tree1 = cpy.SeqLiteral(tree)(elems1) propagateType(tree, tree1) case tree@Annotated(annot, arg) => val annot1 = transform(annot) val arg1 = transform(arg) - val tree1 = cpy.Annotated(tree, annot1, arg1) + val tree1 = cpy.Annotated(tree)(annot1, arg1) propagateType(tree, tree1) case _ => super.transform(tree) } @@ -508,18 +508,18 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case ddef @ DefDef(mods, name, tparams, vparamss, tpt, rhs) => val (tmap1, tparams1) = transformDefs(ddef.tparams) val (tmap2, vparamss1) = tmap1.transformVParamss(vparamss) - cpy.DefDef(ddef, mods, name, tparams1, vparamss1, tmap2.transform(tpt), tmap2.transform(rhs)) + cpy.DefDef(ddef)(mods, name, tparams1, vparamss1, tmap2.transform(tpt), tmap2.transform(rhs)) case blk @ Block(stats, expr) => val (tmap1, stats1) = transformDefs(stats) val expr1 = tmap1.transform(expr) - val tree1 = cpy.Block(blk, stats1, expr1) + val tree1 = cpy.Block(blk)(stats1, expr1) propagateType(blk, tree1) case cdef @ CaseDef(pat, guard, rhs) => val tmap = withMappedSyms(patVars(pat)) val pat1 = tmap.transform(pat) val guard1 = tmap.transform(guard) val rhs1 = tmap.transform(rhs) - val tree1 = cpy.CaseDef(tree, pat1, guard1, rhs1) + val tree1 = cpy.CaseDef(tree)(pat1, guard1, rhs1) propagateType(cdef, tree1) case tree1 => super.transform(tree1) diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala index 44f34093235d..edade142001f 100644 --- a/src/dotty/tools/dotc/ast/untpd.scala +++ b/src/dotty/tools/dotc/ast/untpd.scala @@ -29,7 +29,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case class ModuleDef(mods: Modifiers, name: TermName, impl: Template) extends MemberDef { type ThisTree[-T >: Untyped] <: Trees.NameTree[T] with Trees.MemberDef[T] with ModuleDef - def withName(name: Name)(implicit ctx: Context) = cpy.ModuleDef(this, mods, name.toTermName, impl) + def withName(name: Name)(implicit ctx: Context) = cpy.ModuleDef(this)(mods, name.toTermName, impl) } case class SymbolLit(str: String) extends TermTree @@ -59,7 +59,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { class PolyTypeDef(mods: Modifiers, name: TypeName, override val tparams: List[TypeDef], rhs: Tree) extends TypeDef(mods, name, rhs) { - override def withName(name: Name)(implicit ctx: Context) = cpy.PolyTypeDef(this, mods, name.toTypeName, tparams, rhs) + override def withName(name: Name)(implicit ctx: Context) = cpy.PolyTypeDef(this)(mods, name.toTypeName, tparams, rhs) } // ----- TypeTrees that refer to other tree's symbols ------------------- @@ -253,75 +253,75 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def postProcess(tree: Tree, copied: Tree): copied.ThisTree[Untyped] = copied.asInstanceOf[copied.ThisTree[Untyped]] - def ModuleDef(tree: Tree, mods: Modifiers, name: TermName, impl: Template) = tree match { + def ModuleDef(tree: Tree)(mods: Modifiers, name: TermName, impl: Template) = tree match { case tree: ModuleDef if (mods eq tree.mods) && (name eq tree.name) && (impl eq tree.impl) => tree case _ => untpd.ModuleDef(mods, name, impl).withPos(tree.pos) } - def PolyTypeDef(tree: Tree, mods: Modifiers, name: TypeName, tparams: List[TypeDef], rhs: Tree) = tree match { + def PolyTypeDef(tree: Tree)(mods: Modifiers, name: TypeName, tparams: List[TypeDef], rhs: Tree) = tree match { case tree: PolyTypeDef if (mods eq tree.mods) && (name eq tree.name) && (tparams eq tree.tparams) && (rhs eq tree.rhs) => tree case _ => new PolyTypeDef(mods, name, tparams, rhs).withPos(tree.pos) } - def SymbolLit(tree: Tree, str: String) = tree match { + def SymbolLit(tree: Tree)(str: String) = tree match { case tree: SymbolLit if (str == tree.str) => tree case _ => untpd.SymbolLit(str).withPos(tree.pos) } - def InterpolatedString(tree: Tree, id: TermName, strings: List[Literal], elems: List[Tree]) = tree match { + def InterpolatedString(tree: Tree)(id: TermName, strings: List[Literal], elems: List[Tree]) = tree match { case tree: InterpolatedString if (id eq tree.id) && (strings eq tree.strings) && (elems eq tree.elems) => tree case _ => untpd.InterpolatedString(id, strings, elems).withPos(tree.pos) } - def Function(tree: Tree, args: List[Tree], body: Tree) = tree match { + def Function(tree: Tree)(args: List[Tree], body: Tree) = tree match { case tree: Function if (args eq tree.args) && (body eq tree.body) => tree case _ => untpd.Function(args, body).withPos(tree.pos) } - def InfixOp(tree: Tree, left: Tree, op: Name, right: Tree) = tree match { + def InfixOp(tree: Tree)(left: Tree, op: Name, right: Tree) = tree match { case tree: InfixOp if (left eq tree.left) && (op eq tree.op) && (right eq tree.right) => tree case _ => untpd.InfixOp(left, op, right).withPos(tree.pos) } - def PostfixOp(tree: Tree, od: Tree, op: Name) = tree match { + def PostfixOp(tree: Tree)(od: Tree, op: Name) = tree match { case tree: PostfixOp if (od eq tree.od) && (op eq tree.op) => tree case _ => untpd.PostfixOp(od, op).withPos(tree.pos) } - def PrefixOp(tree: Tree, op: Name, od: Tree) = tree match { + def PrefixOp(tree: Tree)(op: Name, od: Tree) = tree match { case tree: PrefixOp if (op eq tree.op) && (od eq tree.od) => tree case _ => untpd.PrefixOp(op, od).withPos(tree.pos) } - def Parens(tree: Tree, t: Tree) = tree match { + def Parens(tree: Tree)(t: Tree) = tree match { case tree: Parens if (t eq tree.t) => tree case _ => untpd.Parens(t).withPos(tree.pos) } - def Tuple(tree: Tree, trees: List[Tree]) = tree match { + def Tuple(tree: Tree)(trees: List[Tree]) = tree match { case tree: Tuple if (trees eq tree.trees) => tree case _ => untpd.Tuple(trees).withPos(tree.pos) } - def WhileDo(tree: Tree, cond: Tree, body: Tree) = tree match { + def WhileDo(tree: Tree)(cond: Tree, body: Tree) = tree match { case tree: WhileDo if (cond eq tree.cond) && (body eq tree.body) => tree case _ => untpd.WhileDo(cond, body).withPos(tree.pos) } - def DoWhile(tree: Tree, body: Tree, cond: Tree) = tree match { + def DoWhile(tree: Tree)(body: Tree, cond: Tree) = tree match { case tree: DoWhile if (body eq tree.body) && (cond eq tree.cond) => tree case _ => untpd.DoWhile(body, cond).withPos(tree.pos) } - def ForYield(tree: Tree, enums: List[Tree], expr: Tree) = tree match { + def ForYield(tree: Tree)(enums: List[Tree], expr: Tree) = tree match { case tree: ForYield if (enums eq tree.enums) && (expr eq tree.expr) => tree case _ => untpd.ForYield(enums, expr).withPos(tree.pos) } - def ForDo(tree: Tree, enums: List[Tree], body: Tree) = tree match { + def ForDo(tree: Tree)(enums: List[Tree], body: Tree) = tree match { case tree: ForDo if (enums eq tree.enums) && (body eq tree.body) => tree case _ => untpd.ForDo(enums, body).withPos(tree.pos) } - def GenFrom(tree: Tree, pat: Tree, expr: Tree) = tree match { + def GenFrom(tree: Tree)(pat: Tree, expr: Tree) = tree match { case tree: GenFrom if (pat eq tree.pat) && (expr eq tree.expr) => tree case _ => untpd.GenFrom(pat, expr).withPos(tree.pos) } - def GenAlias(tree: Tree, pat: Tree, expr: Tree) = tree match { + def GenAlias(tree: Tree)(pat: Tree, expr: Tree) = tree match { case tree: GenAlias if (pat eq tree.pat) && (expr eq tree.expr) => tree case _ => untpd.GenAlias(pat, expr).withPos(tree.pos) } - def ContextBounds(tree: Tree, bounds: TypeBoundsTree, cxBounds: List[Tree]) = tree match { + def ContextBounds(tree: Tree)(bounds: TypeBoundsTree, cxBounds: List[Tree]) = tree match { case tree: ContextBounds if (bounds eq tree.bounds) && (cxBounds eq tree.cxBounds) => tree case _ => untpd.ContextBounds(bounds, cxBounds).withPos(tree.pos) } - def PatDef(tree: Tree, mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree) = tree match { + def PatDef(tree: Tree)(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree) = tree match { case tree: PatDef if (mods eq tree.mods) && (pats eq tree.pats) && (tpt eq tree.tpt) && (rhs eq tree.rhs) => tree case _ => untpd.PatDef(mods, pats, tpt, rhs).withPos(tree.pos) } @@ -330,41 +330,41 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { abstract class UntypedTreeMap(cpy: UntypedTreeCopier = untpd.cpy) extends TreeMap(cpy) { override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { case ModuleDef(mods, name, impl) => - cpy.ModuleDef(tree, mods, name, transformSub(impl)) + cpy.ModuleDef(tree)(mods, name, transformSub(impl)) case SymbolLit(str) => - cpy.SymbolLit(tree, str) + cpy.SymbolLit(tree)(str) case InterpolatedString(id, strings, elems) => - cpy.InterpolatedString(tree, id, transformSub(strings), transform(elems)) + cpy.InterpolatedString(tree)(id, transformSub(strings), transform(elems)) case Function(args, body) => - cpy.Function(tree, transform(args), transform(body)) + cpy.Function(tree)(transform(args), transform(body)) case InfixOp(left, op, right) => - cpy.InfixOp(tree, transform(left), op, transform(right)) + cpy.InfixOp(tree)(transform(left), op, transform(right)) case PostfixOp(od, op) => - cpy.PostfixOp(tree, transform(od), op) + cpy.PostfixOp(tree)(transform(od), op) case PrefixOp(op, od) => - cpy.PrefixOp(tree, op, transform(od)) + cpy.PrefixOp(tree)(op, transform(od)) case Parens(t) => - cpy.Parens(tree, transform(t)) + cpy.Parens(tree)(transform(t)) case Tuple(trees) => - cpy.Tuple(tree, transform(trees)) + cpy.Tuple(tree)(transform(trees)) case WhileDo(cond, body) => - cpy.WhileDo(tree, transform(cond), transform(body)) + cpy.WhileDo(tree)(transform(cond), transform(body)) case DoWhile(body, cond) => - cpy.DoWhile(tree, transform(body), transform(cond)) + cpy.DoWhile(tree)(transform(body), transform(cond)) case ForYield(enums, expr) => - cpy.ForYield(tree, transform(enums), transform(expr)) + cpy.ForYield(tree)(transform(enums), transform(expr)) case ForDo(enums, body) => - cpy.ForDo(tree, transform(enums), transform(body)) + cpy.ForDo(tree)(transform(enums), transform(body)) case GenFrom(pat, expr) => - cpy.GenFrom(tree, transform(pat), transform(expr)) + cpy.GenFrom(tree)(transform(pat), transform(expr)) case GenAlias(pat, expr) => - cpy.GenAlias(tree, transform(pat), transform(expr)) + cpy.GenAlias(tree)(transform(pat), transform(expr)) case ContextBounds(bounds, cxBounds) => - cpy.ContextBounds(tree, transformSub(bounds), transform(cxBounds)) + cpy.ContextBounds(tree)(transformSub(bounds), transform(cxBounds)) case PatDef(mods, pats, tpt, rhs) => - cpy.PatDef(tree, mods, transform(pats), transform(tpt), transform(rhs)) + cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs)) case tree: PolyTypeDef => - cpy.PolyTypeDef(tree, tree.mods, tree.name, transformSub(tree.tparams), transform(tree.rhs)) + cpy.PolyTypeDef(tree)(tree.mods, tree.name, transformSub(tree.tparams), transform(tree.rhs)) case _ => super.transform(tree) } diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index 0ad33a96c341..522d94243e8a 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -274,9 +274,9 @@ object Parsers { */ def convertToTypeId(tree: Tree): Tree = tree match { case id @ Ident(name) => - cpy.Ident(id, name.toTypeName) + cpy.Ident(id)(name.toTypeName) case id @ Select(qual, name) => - cpy.Select(id, qual, name.toTypeName) + cpy.Select(id)(qual, name.toTypeName) case _ => syntaxError("identifier expected", tree.pos) tree @@ -963,7 +963,7 @@ object Parsers { val tpt = typeDependingOn(location) if (isWildcard(t) && location != Location.InPattern) { val vd :: rest = placeholderParams - placeholderParams = cpy.ValDef(vd, vd.mods, vd.name, tpt, vd.rhs) :: rest + placeholderParams = cpy.ValDef(vd)(vd.mods, vd.name, tpt, vd.rhs) :: rest } Typed(t, tpt) } @@ -1096,7 +1096,7 @@ object Parsers { if (in.token == LBRACE) blockExpr() :: Nil else parArgumentExprs() val argumentExpr = () => exprInParens() match { - case a @ Assign(Ident(id), rhs) => cpy.NamedArg(a, id, rhs) + case a @ Assign(Ident(id), rhs) => cpy.NamedArg(a)(id, rhs) case e => e } @@ -1409,8 +1409,8 @@ object Parsers { /** Adjust start of annotation or constructor to position of preceding @ or new */ def adjustStart(start: Offset)(tree: Tree): Tree = { val tree1 = tree match { - case Apply(fn, args) => cpy.Apply(tree, adjustStart(start)(fn), args) - case Select(qual, name) => cpy.Select(tree, adjustStart(start)(qual), name) + case Apply(fn, args) => cpy.Apply(tree)(adjustStart(start)(fn), args) + case Select(qual, name) => cpy.Select(tree)(adjustStart(start)(qual), name) case _ => tree } if (start < tree1.pos.start) tree1.withPos(tree1.pos.withStart(start)) @@ -1605,7 +1605,7 @@ object Parsers { imp case sel @ Select(qual, name) => val selector = atPos(sel.pos.point) { Ident(name) } - cpy.Import(sel, qual, selector :: Nil) + cpy.Import(sel)(qual, selector :: Nil) case t => accept(DOT) Import(t, Ident(nme.WILDCARD) :: Nil) @@ -1687,7 +1687,7 @@ object Parsers { } } else EmptyTree lhs match { - case (id @ Ident(name: TermName)) :: Nil => cpy.ValDef(id, mods, name, tpt, rhs) + case (id @ Ident(name: TermName)) :: Nil => cpy.ValDef(id)(mods, name, tpt, rhs) case _ => PatDef(mods, lhs, tpt, rhs) } } diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index 33d742a17208..890948715794 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -16,12 +16,11 @@ class Constructors extends MiniPhaseTransform { if(tree.symbol.isClassConstructor) { val claz = tree.symbol.enclosingClass.asClass val zuper = claz.info.parents.head.typeSymbol - cpy.DefDef(tree, tree.mods, tree.name, tree.tparams, tree.vparamss, tree.tpt, rhs = { + cpy.DefDef(tree)(rhs = { val parentCall = Super(This(claz), tpnme.EMPTY, true).select(zuper.primaryConstructor).appliedToNone if(tree.rhs.isEmpty) parentCall else Block(List(parentCall), tree.rhs) - }) } else tree } diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 06e3506dfb88..6b2a5a676a95 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -225,7 +225,7 @@ object Erasure { assert(sym.exists, tree.show) def select(qual: Tree, sym: Symbol): Tree = - untpd.cpy.Select(tree, qual, sym.name) withType qual.tpe.select(sym) + untpd.cpy.Select(tree)(qual, sym.name) withType qual.tpe.select(sym) def selectArrayMember(qual: Tree, erasedPre: Type) = if (erasedPre isRef defn.ObjectClass) runtimeCall(tree.name.genericArrayOp, qual :: Nil) @@ -257,7 +257,7 @@ object Erasure { fun1.tpe.widen match { case funTpe: PolyType => val args1 = args.mapconserve(typedType(_)) - untpd.cpy.TypeApply(tree, fun1, args1).withType(funTpe.instantiate(args1.tpes)) + untpd.cpy.TypeApply(tree)(fun1, args1).withType(funTpe.instantiate(args1.tpes)) case _ => fun1 } } @@ -268,7 +268,7 @@ object Erasure { fun1.tpe.widen match { case mt: MethodType => val args1 = args.zipWithConserve(mt.paramTypes)(typedExpr) - untpd.cpy.Apply(tree, fun1, args1) withType mt.resultType + untpd.cpy.Apply(tree)(fun1, args1) withType mt.resultType case _ => throw new MatchError(i"tree $tree has uxpected type of function ${fun1.tpe.widen}, was ${fun.typeOpt.widen}") } @@ -284,7 +284,7 @@ object Erasure { val tpt1 = // keep UnitTypes intact in result position if (ddef.tpt.typeOpt isRef defn.UnitClass) untpd.TypeTree(defn.UnitType) withPos ddef.tpt.pos else ddef.tpt - val ddef1 = untpd.cpy.DefDef(ddef, ddef.mods, ddef.name, Nil, ddef.vparamss, tpt1, ddef.rhs) + val ddef1 = untpd.cpy.DefDef(ddef)(tparams = Nil, tpt = tpt1) super.typedDefDef(ddef1, sym) } diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala index ba5b9fab64ee..e05852dc2494 100644 --- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -165,7 +165,7 @@ class ExtensionMethods extends MacroTransform with DenotTransformer with FullPar val tree1 @ Template(constr, parents, selfType, body) = super.transform(tree) extensionDefs remove tree1.symbol.owner match { case Some(defns) if defns.nonEmpty => - cpy.Template(tree1, constr, parents, selfType, body ++ defns) + cpy.Template(tree1)(constr, parents, selfType, body ++ defns) case _ => tree1 } @@ -178,7 +178,7 @@ class ExtensionMethods extends MacroTransform with DenotTransformer with FullPar val extensionMeth = extensionMethod(origMeth) ctx.log(s"Value class $origClass spawns extension method.\n Old: ${origMeth.showDcl}\n New: ${extensionMeth.showDcl}") extensionDefs(staticClass) += fullyParameterizedDef(extensionMeth, ddef) - cpy.DefDef(tree, ddef.mods, ddef.name, ddef.tparams, ddef.vparamss, ddef.tpt, + cpy.DefDef(tree)(ddef.mods, ddef.name, ddef.tparams, ddef.vparamss, ddef.tpt, forwarder(extensionMeth, ddef)) case _ => super.transform(tree) diff --git a/src/dotty/tools/dotc/transform/MacroTransform.scala b/src/dotty/tools/dotc/transform/MacroTransform.scala index 0ee92bccdce3..6f38c98a909c 100644 --- a/src/dotty/tools/dotc/transform/MacroTransform.scala +++ b/src/dotty/tools/dotc/transform/MacroTransform.scala @@ -47,7 +47,7 @@ abstract class MacroTransform extends Phase { val exprCtx = ctx.withOwner(exprOwner) def transformStat(stat: Tree): Tree = stat match { case _: Import | _: DefTree => transform(stat) - case Thicket(stats) => cpy.Thicket(stat, stats mapConserve transformStat) + case Thicket(stats) => cpy.Thicket(stat)(stats mapConserve transformStat) case _ => transform(stat)(exprCtx) } flatten(trees.mapconserve(transformStat(_))) @@ -60,7 +60,7 @@ abstract class MacroTransform extends Phase { case _: PackageDef | _: MemberDef => super.transform(tree)(localCtx(tree)) case Template(constr, parents, self, body) => - cpy.Template(tree, + cpy.Template(tree)( transformSub(constr), transform(parents), transformSelf(self), @@ -71,6 +71,6 @@ abstract class MacroTransform extends Phase { } def transformSelf(vd: ValDef)(implicit ctx: Context) = - cpy.ValDef(vd, vd.mods, vd.name, transform(vd.tpt), vd.rhs) + cpy.ValDef(vd)(vd.mods, vd.name, transform(vd.tpt), vd.rhs) } } diff --git a/src/dotty/tools/dotc/transform/Nullarify.scala b/src/dotty/tools/dotc/transform/Nullarify.scala index 5756d848ac8a..6ecb095b41fb 100644 --- a/src/dotty/tools/dotc/transform/Nullarify.scala +++ b/src/dotty/tools/dotc/transform/Nullarify.scala @@ -72,7 +72,7 @@ class Nullarify extends MiniPhaseTransform with InfoTransformer { val MethodType(_, formals) = methType(funType, tree.fun) val args1 = tree.args.zipWithConserve(formals)(transformArg) - cpy.Apply(tree, tree.fun, args1) withType nullarify(tree.tpe) + cpy.Apply(tree)(tree.fun, args1) withType nullarify(tree.tpe) } /** Insert () or .apply() if the term refers to something that was converted to a @@ -108,16 +108,15 @@ class Nullarify extends MiniPhaseTransform with InfoTransformer { insertParens(tree) override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { - val DefDef(mods, name, tparams, vparamss, tpt, rhs) = tree val vparamss1 = - if (vparamss.isEmpty) Nil :: Nil - else vparamss nestedMap { vparam => + if (tree.vparamss.isEmpty) Nil :: Nil + else tree.vparamss nestedMap { vparam => val tp = vparam.tpt.tpe val tp1 = nullarifyParam(tp) if (tp eq tp1) vparam - else cpy.ValDef(vparam, vparam.mods, vparam.name, vparam.tpt.withType(tp1), vparam.rhs) + else cpy.ValDef(vparam)(tpt = vparam.tpt.withType(tp1)) } - cpy.DefDef(tree, mods, name, tparams, vparamss1, tpt, rhs) + cpy.DefDef(tree)(vparamss = vparamss1) } def nullarify(tp: Type)(implicit ctx: Context): Type = tp match { diff --git a/src/dotty/tools/dotc/transform/PatternMatcher.scala b/src/dotty/tools/dotc/transform/PatternMatcher.scala index 373fae12f9de..fdda670b1c38 100644 --- a/src/dotty/tools/dotc/transform/PatternMatcher.scala +++ b/src/dotty/tools/dotc/transform/PatternMatcher.scala @@ -22,5 +22,5 @@ class PatternMatcher extends MiniPhaseTransform { override def name: String = "patternMatcher" override def transformCaseDef(tree: CaseDef)(implicit ctx: Context, info: TransformerInfo): Tree = - cpy.CaseDef(tree, Literal(Constant("")), tree.guard, tree.body) + cpy.CaseDef(tree)(Literal(Constant("")), tree.guard, tree.body) } \ No newline at end of file diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index 2496cf5a922d..62dd7f0c0b67 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -222,7 +222,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this // Don't transform patterns or strange trees will reach the matcher (ticket #4062) // TODO Query `ctx.mode is Pattern` instead. case CaseDef(pat, guard, body) => - cpy.CaseDef(tree, pat, transform(guard), transform(body)) + cpy.CaseDef(tree)(pat, transform(guard), transform(body)) case TypeDef(_, _, impl: Template) => val cls = sym.asClass @@ -295,7 +295,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this val body1 = forwardParamAccessors(transformStats(impl.body, tree.symbol)) accDefs -= currentClass ownStats ++= body1 - cpy.Template(tree, impl.constr, impl.parents, impl.self, body1) + cpy.Template(tree)(impl.constr, impl.parents, impl.self, body1) } transformTemplate @@ -368,9 +368,9 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this } transformSelect - case DefDef(mods, name, tparams, vparamss, tpt, rhs) => - val rhs1 = if (isMethodWithExtension(sym)) withInvalidOwner(transform(rhs)) else transform(rhs) - cpy.DefDef(tree, mods, name, tparams, vparamss, tpt, rhs1) + case tree@DefDef(_, _, _, _, _, rhs) => + cpy.DefDef(tree)( + rhs = if (isMethodWithExtension(sym)) withInvalidOwner(transform(rhs)) else transform(rhs)) case TypeApply(sel @ Select(qual, name), args) => mayNeedProtectedAccessor(sel, args, goToSuper = true) @@ -391,7 +391,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this case Apply(fn, args) => val MethodType(_, formals) = fn.tpe.widen - cpy.Apply(tree, transform(fn), transformArgs(formals, args)) + cpy.Apply(tree)(transform(fn), transformArgs(formals, args)) case _ => super.transform(tree) diff --git a/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/src/dotty/tools/dotc/transform/SyntheticMethods.scala index 4f9b2c9fa004..383abc186924 100644 --- a/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/src/dotty/tools/dotc/transform/SyntheticMethods.scala @@ -167,8 +167,8 @@ class SyntheticMethods extends MiniPhaseTransform with IdentityDenotTransformer 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))) + cpy.Template(impl)( + body = 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 fd414a225ee8..359859cec2ab 100644 --- a/src/dotty/tools/dotc/transform/TailRec.scala +++ b/src/dotty/tools/dotc/transform/TailRec.scala @@ -84,7 +84,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete case dd@DefDef(mods, name, tparams, vparamss0, tpt, rhs0) if (dd.symbol.isEffectivelyFinal) && !((dd.symbol is Flags.Accessor) || (rhs0 eq EmptyTree) || (dd.symbol is Flags.Label)) => val mandatory = dd.symbol.hasAnnotation(defn.TailrecAnnotationClass) - cpy.DefDef(tree, mods, name, tparams, vparamss0, tpt, rhs = { + cpy.DefDef(dd)(rhs = { val origMeth = tree.symbol val label = mkLabel(dd.symbol) @@ -105,8 +105,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete } if (rewrote) { - val dummyDefDef = cpy.DefDef(tree, dd.mods, dd.name, dd.tparams, dd.vparamss, dd.tpt, - rhsSemiTransformed) + val dummyDefDef = cpy.DefDef(tree)(rhs = rhsSemiTransformed) val res = fullyParameterizedDef(label, dummyDefDef) val call = forwarder(label, dd) Block(List(res), call) @@ -233,21 +232,21 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete def transformHandlers(t: Tree): Tree = { t match { case Block(List((d: DefDef)), cl@Closure(Nil, _, EmptyTree)) => - val newDef = cpy.DefDef(d, d.mods, d.name, d.tparams, d.vparamss, d.tpt, transform(d.rhs)) + val newDef = cpy.DefDef(d)(rhs = transform(d.rhs)) Block(List(newDef), cl) case _ => assert(false, s"failed to deconstruct try handler ${t.show}"); ??? } } if (tree.finalizer eq EmptyTree) { // SI-1672 Catches are in tail position when there is no finalizer - tpd.cpy.Try(tree, + tpd.cpy.Try(tree)( noTailTransform(tree.expr), transformHandlers(tree.handler), EmptyTree ) } else { - tpd.cpy.Try(tree, + tpd.cpy.Try(tree)( noTailTransform(tree.expr), noTailTransform(tree.handler), noTailTransform(tree.finalizer) @@ -258,18 +257,18 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete val res: Tree = tree match { case tree@Block(stats, expr) => - val tree1 = tpd.cpy.Block(tree, + val tree1 = tpd.cpy.Block(tree)( noTailTransforms(stats), transform(expr) ) propagateType(tree, tree1) - case tree@CaseDef(pat, guard, body) => - val tree1 = cpy.CaseDef(tree, pat, guard, transform(body)) + case tree@CaseDef(_, _, body) => + val tree1 = cpy.CaseDef(tree)(body = transform(body)) propagateType(tree, tree1) case tree@If(cond, thenp, elsep) => - val tree1 = tpd.cpy.If(tree, + val tree1 = tpd.cpy.If(tree)( noTailTransform(cond), transform(thenp), transform(elsep) @@ -277,7 +276,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete propagateType(tree, tree1) case tree@Match(selector, cases) => - val tree1 = tpd.cpy.Match(tree, + val tree1 = tpd.cpy.Match(tree)( noTailTransform(selector), transformSub(cases) ) @@ -288,7 +287,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete propagateType(tree, tree1) case Apply(fun, args) if fun.symbol == defn.Boolean_|| || fun.symbol == defn.Boolean_&& => - tpd.cpy.Apply(tree, fun, transform(args)) + tpd.cpy.Apply(tree)(fun, transform(args)) case Apply(fun, args) => rewriteApply(tree, fun.symbol) @@ -300,7 +299,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete case tree: Select => val sym = tree.symbol if (sym == method && ctx.tailPos) rewriteApply(tree, sym) - else propagateType(tree, tpd.cpy.Select(tree, noTailTransform(tree.qualifier), tree.name)) + else propagateType(tree, tpd.cpy.Select(tree)(noTailTransform(tree.qualifier), tree.name)) case ValDef(_, _, _, _) | EmptyTree | Super(_, _) | This(_) | Literal(_) | TypeTree(_) | DefDef(_, _, _, _, _, _) | TypeDef(_, _, _) => diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index 129553264e2e..4ac6d9aade4c 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -898,7 +898,7 @@ object TreeTransforms { case tree: UnApply => goUnApply(tree, info.nx.nxTransUnApply(cur)) 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 Thicket(trees) => cpy.Thicket(tree)(transformTrees(trees, info, cur)) case tree => goOther(tree, info.nx.nxTransOther(cur)) } @@ -930,21 +930,21 @@ object TreeTransforms { if (mutatedInfo eq null) tree else { val qual = transform(tree.qualifier, mutatedInfo, cur) - goSelect(cpy.Select(tree, qual, tree.name), mutatedInfo.nx.nxTransSelect(cur)) + goSelect(cpy.Select(tree)(qual, tree.name), mutatedInfo.nx.nxTransSelect(cur)) } case tree: SelectFromTypeTree => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForSelectFromTypeTree, info.nx.nxPrepSelectFromTypeTree, tree, cur) if (mutatedInfo eq null) tree else { val qual = transform(tree.qualifier, mutatedInfo, cur) - goSelectFromTypeTree(cpy.SelectFromTypeTree(tree, qual, tree.name), mutatedInfo.nx.nxTransSelectFromTypeTree(cur)) + goSelectFromTypeTree(cpy.SelectFromTypeTree(tree)(qual, tree.name), mutatedInfo.nx.nxTransSelectFromTypeTree(cur)) } case tree: Bind => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForBind, info.nx.nxPrepBind, tree, cur) if (mutatedInfo eq null) tree else { val body = transform(tree.body, mutatedInfo, mutatedInfo.nx.nxTransBind(cur)) - goBind(cpy.Bind(tree, tree.name, body), cur) + goBind(cpy.Bind(tree)(tree.name, body), cur) } case tree: ValDef if !tree.isEmpty => // As a result of discussing with Martin: emptyValDefs shouldn't be copied // NAME implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForValDef, info.nx.nxPrepValDef, tree, cur) @@ -953,7 +953,7 @@ object TreeTransforms { val nestedCtx = if (tree.symbol.exists) localContext(tree.symbol) else ctx val tpt = transform(tree.tpt, mutatedInfo, cur)(nestedCtx) val rhs = transform(tree.rhs, mutatedInfo, cur)(nestedCtx) - goValDef(cpy.ValDef(tree, tree.mods, tree.name, tpt, rhs), mutatedInfo.nx.nxTransValDef(cur)) + goValDef(cpy.ValDef(tree)(tree.mods, tree.name, tpt, rhs), mutatedInfo.nx.nxTransValDef(cur)) } case tree: DefDef => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForDefDef, info.nx.nxPrepDefDef, tree, cur) @@ -964,14 +964,14 @@ object TreeTransforms { val vparams = tree.vparamss.mapConserve(x => transformSubTrees(x, mutatedInfo, cur)(nestedCtx)) val tpt = transform(tree.tpt, mutatedInfo, cur)(nestedCtx) val rhs = transform(tree.rhs, mutatedInfo, cur)(nestedCtx) - goDefDef(cpy.DefDef(tree, tree.mods, tree.name, tparams, vparams, tpt, rhs), mutatedInfo.nx.nxTransDefDef(cur)) + goDefDef(cpy.DefDef(tree)(tree.mods, tree.name, tparams, vparams, tpt, rhs), mutatedInfo.nx.nxTransDefDef(cur)) } case tree: TypeDef => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeDef, info.nx.nxPrepTypeDef, tree, cur) if (mutatedInfo eq null) tree else { val rhs = transform(tree.rhs, mutatedInfo, cur)(localContext(tree.symbol)) - goTypeDef(cpy.TypeDef(tree, tree.mods, tree.name, rhs, tree.tparams), mutatedInfo.nx.nxTransTypeDef(cur)) + goTypeDef(cpy.TypeDef(tree)(tree.mods, tree.name, rhs, tree.tparams), mutatedInfo.nx.nxTransTypeDef(cur)) } case _ => tree @@ -988,7 +988,7 @@ object TreeTransforms { if (mutatedInfo eq null) tree else { val qual = transform(tree.qual, mutatedInfo, cur) - goSuper(cpy.Super(tree, qual, tree.mix), mutatedInfo.nx.nxTransSuper(cur)) + goSuper(cpy.Super(tree)(qual, tree.mix), mutatedInfo.nx.nxTransSuper(cur)) } case tree: Apply => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForApply, info.nx.nxPrepApply, tree, cur) @@ -996,7 +996,7 @@ object TreeTransforms { else { val fun = transform(tree.fun, mutatedInfo, cur) val args = transformSubTrees(tree.args, mutatedInfo, cur) - goApply(cpy.Apply(tree, fun, args), mutatedInfo.nx.nxTransApply(cur)) + goApply(cpy.Apply(tree)(fun, args), mutatedInfo.nx.nxTransApply(cur)) } case tree: TypeApply => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeApply, info.nx.nxPrepTypeApply, tree, cur) @@ -1004,7 +1004,7 @@ object TreeTransforms { else { val fun = transform(tree.fun, mutatedInfo, cur) val args = transformTrees(tree.args, mutatedInfo, cur) - goTypeApply(cpy.TypeApply(tree, fun, args), mutatedInfo.nx.nxTransTypeApply(cur)) + goTypeApply(cpy.TypeApply(tree)(fun, args), mutatedInfo.nx.nxTransTypeApply(cur)) } case tree: Literal => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForLiteral, info.nx.nxPrepLiteral, tree, cur) @@ -1015,7 +1015,7 @@ object TreeTransforms { if (mutatedInfo eq null) tree else { val tpt = transform(tree.tpt, mutatedInfo, cur) - goNew(cpy.New(tree, tpt), mutatedInfo.nx.nxTransNew(cur)) + goNew(cpy.New(tree)(tpt), mutatedInfo.nx.nxTransNew(cur)) } case tree: Pair => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForPair, info.nx.nxPrepPair, tree, cur) @@ -1023,7 +1023,7 @@ object TreeTransforms { else { val left = transform(tree.left, mutatedInfo, cur) val right = transform(tree.right, mutatedInfo, cur) - goPair(cpy.Pair(tree, left, right), mutatedInfo.nx.nxTransPair(cur)) + goPair(cpy.Pair(tree)(left, right), mutatedInfo.nx.nxTransPair(cur)) } case tree: Typed => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTyped, info.nx.nxPrepTyped, tree, cur) @@ -1031,7 +1031,7 @@ object TreeTransforms { else { val expr = transform(tree.expr, mutatedInfo, cur) val tpt = transform(tree.tpt, mutatedInfo, cur) - goTyped(cpy.Typed(tree, expr, tpt), mutatedInfo.nx.nxTransTyped(cur)) + goTyped(cpy.Typed(tree)(expr, tpt), mutatedInfo.nx.nxTransTyped(cur)) } case tree: Assign => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForAssign, info.nx.nxPrepAssign, tree, cur) @@ -1039,7 +1039,7 @@ object TreeTransforms { else { val lhs = transform(tree.lhs, mutatedInfo, cur) val rhs = transform(tree.rhs, mutatedInfo, cur) - goAssign(cpy.Assign(tree, lhs, rhs), mutatedInfo.nx.nxTransAssign(cur)) + goAssign(cpy.Assign(tree)(lhs, rhs), mutatedInfo.nx.nxTransAssign(cur)) } case tree: Block => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForBlock, info.nx.nxPrepBlock, tree, cur) @@ -1047,7 +1047,7 @@ object TreeTransforms { else { val stats = transformStats(tree.stats, ctx.owner, mutatedInfo, cur) val expr = transform(tree.expr, mutatedInfo, cur) - goBlock(cpy.Block(tree, stats, expr), mutatedInfo.nx.nxTransBlock(cur)) + goBlock(cpy.Block(tree)(stats, expr), mutatedInfo.nx.nxTransBlock(cur)) } case tree: If => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForIf, info.nx.nxPrepIf, tree, cur) @@ -1056,7 +1056,7 @@ object TreeTransforms { val cond = transform(tree.cond, mutatedInfo, cur) val thenp = transform(tree.thenp, mutatedInfo, cur) val elsep = transform(tree.elsep, mutatedInfo, cur) - goIf(cpy.If(tree, cond, thenp, elsep), mutatedInfo.nx.nxTransIf(cur)) + goIf(cpy.If(tree)(cond, thenp, elsep), mutatedInfo.nx.nxTransIf(cur)) } case tree: Closure => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForClosure, info.nx.nxPrepClosure, tree, cur) @@ -1065,7 +1065,7 @@ object TreeTransforms { val env = transformTrees(tree.env, mutatedInfo, cur) val meth = transform(tree.meth, mutatedInfo, cur) val tpt = transform(tree.tpt, mutatedInfo, cur) - goClosure(cpy.Closure(tree, env, meth, tpt), mutatedInfo.nx.nxTransClosure(cur)) + goClosure(cpy.Closure(tree)(env, meth, tpt), mutatedInfo.nx.nxTransClosure(cur)) } case tree: Match => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForMatch, info.nx.nxPrepMatch, tree, cur) @@ -1073,7 +1073,7 @@ object TreeTransforms { else { val selector = transform(tree.selector, mutatedInfo, cur) val cases = transformSubTrees(tree.cases, mutatedInfo, cur) - goMatch(cpy.Match(tree, selector, cases), mutatedInfo.nx.nxTransMatch(cur)) + goMatch(cpy.Match(tree)(selector, cases), mutatedInfo.nx.nxTransMatch(cur)) } case tree: CaseDef => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForCaseDef, info.nx.nxPrepCaseDef, tree, cur) @@ -1082,7 +1082,7 @@ object TreeTransforms { 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)) + goCaseDef(cpy.CaseDef(tree)(pat, guard, body), mutatedInfo.nx.nxTransCaseDef(cur)) } case tree: Return => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForReturn, info.nx.nxPrepReturn, tree, cur) @@ -1090,7 +1090,7 @@ object TreeTransforms { else { val expr = transform(tree.expr, mutatedInfo, cur) val from = transform(tree.from, mutatedInfo, cur) - goReturn(cpy.Return(tree, expr, from), mutatedInfo.nx.nxTransReturn(cur)) + goReturn(cpy.Return(tree)(expr, from), mutatedInfo.nx.nxTransReturn(cur)) } case tree: Try => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTry, info.nx.nxPrepTry, tree, cur) @@ -1099,35 +1099,35 @@ object TreeTransforms { val block = transform(tree.expr, mutatedInfo, cur) val handler = transform(tree.handler, mutatedInfo, cur) val finalizer = transform(tree.finalizer, mutatedInfo, cur) - goTry(cpy.Try(tree, block, handler, finalizer), mutatedInfo.nx.nxTransTry(cur)) + goTry(cpy.Try(tree)(block, handler, finalizer), mutatedInfo.nx.nxTransTry(cur)) } case tree: Throw => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForThrow, info.nx.nxPrepThrow, tree, cur) if (mutatedInfo eq null) tree else { val expr = transform(tree.expr, mutatedInfo, cur) - goThrow(cpy.Throw(tree, expr), mutatedInfo.nx.nxTransThrow(cur)) + goThrow(cpy.Throw(tree)(expr), mutatedInfo.nx.nxTransThrow(cur)) } case tree: SeqLiteral => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForSeqLiteral, info.nx.nxPrepSeqLiteral, tree, cur) if (mutatedInfo eq null) tree else { val elems = transformTrees(tree.elems, mutatedInfo, cur) - goSeqLiteral(cpy.SeqLiteral(tree, elems), mutatedInfo.nx.nxTransLiteral(cur)) + goSeqLiteral(cpy.SeqLiteral(tree)(elems), mutatedInfo.nx.nxTransLiteral(cur)) } case tree: TypeTree => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTypeTree, info.nx.nxPrepTypeTree, tree, cur) if (mutatedInfo eq null) tree else { val original = transform(tree.original, mutatedInfo, cur) - goTypeTree(cpy.TypeTree(tree, original), mutatedInfo.nx.nxTransTypeTree(cur)) + goTypeTree(cpy.TypeTree(tree)(original), mutatedInfo.nx.nxTransTypeTree(cur)) } case tree: Alternative => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForAlternative, info.nx.nxPrepAlternative, tree, cur) if (mutatedInfo eq null) tree else { val trees = transformTrees(tree.trees, mutatedInfo, cur) - goAlternative(cpy.Alternative(tree, trees), mutatedInfo.nx.nxTransAlternative(cur)) + goAlternative(cpy.Alternative(tree)(trees), mutatedInfo.nx.nxTransAlternative(cur)) } case tree: UnApply => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForUnApply, info.nx.nxPrepUnApply, tree, cur) @@ -1136,7 +1136,7 @@ object TreeTransforms { val fun = transform(tree.fun, mutatedInfo, cur) val implicits = transformTrees(tree.implicits, mutatedInfo, cur) val patterns = transformTrees(tree.patterns, mutatedInfo, cur) - goUnApply(cpy.UnApply(tree, fun, implicits, patterns), mutatedInfo.nx.nxTransUnApply(cur)) + goUnApply(cpy.UnApply(tree)(fun, implicits, patterns), mutatedInfo.nx.nxTransUnApply(cur)) } case tree: Template => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForTemplate, info.nx.nxPrepTemplate, tree, cur) @@ -1146,7 +1146,7 @@ object TreeTransforms { val parents = transformTrees(tree.parents, mutatedInfo, cur) val self = transformSub(tree.self, mutatedInfo, cur) val body = transformStats(tree.body, tree.symbol, mutatedInfo, cur) - goTemplate(cpy.Template(tree, constr, parents, self, body), mutatedInfo.nx.nxTransTemplate(cur)) + goTemplate(cpy.Template(tree)(constr, parents, self, body), mutatedInfo.nx.nxTransTemplate(cur)) } case tree: PackageDef => implicit val mutatedInfo: TransformerInfo = mutateTransformers(info, prepForPackageDef, info.nx.nxPrepPackageDef, tree, cur) @@ -1155,9 +1155,9 @@ object TreeTransforms { val nestedCtx = localContext(tree.symbol) val pid = transformSub(tree.pid, mutatedInfo, cur) val stats = transformStats(tree.stats, tree.symbol, mutatedInfo, cur)(nestedCtx) - goPackageDef(cpy.PackageDef(tree, pid, stats), mutatedInfo.nx.nxTransPackageDef(cur)) + goPackageDef(cpy.PackageDef(tree)(pid, stats), mutatedInfo.nx.nxTransPackageDef(cur)) } - case Thicket(trees) => cpy.Thicket(tree, transformTrees(trees, info, cur)) + case Thicket(trees) => cpy.Thicket(tree)(transformTrees(trees, info, cur)) case tree => implicit val originalInfo: TransformerInfo = info goOther(tree, info.nx.nxTransOther(cur)) @@ -1190,7 +1190,7 @@ object TreeTransforms { val exprCtx = ctx.withOwner(exprOwner) def transformStat(stat: Tree): Tree = stat match { case _: Import | _: DefTree => transform(stat, newInfo, current) - case Thicket(stats) => cpy.Thicket(stat, stats mapConserve transformStat) + case Thicket(stats) => cpy.Thicket(stat)(stats mapConserve transformStat) case _ => transform(stat, newInfo, current)(exprCtx) } val newTrees = flatten(trees.mapconserve(transformStat)) diff --git a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala index ef0359136028..93acc8e86fb5 100644 --- a/src/dotty/tools/dotc/transform/TypeTestsCasts.scala +++ b/src/dotty/tools/dotc/transform/TypeTestsCasts.scala @@ -34,7 +34,7 @@ class TypeTestsCasts extends MiniPhaseTransform { def isPrimitive(tp: Type) = tp.classSymbol.isPrimitiveValueClass def derivedTree(qual1: Tree, sym: Symbol, tp: Type) = - cpy.TypeApply(tree, qual1.select(sym).withPos(qual.pos), List(TypeTree(tp))) + cpy.TypeApply(tree)(qual1.select(sym).withPos(qual.pos), List(TypeTree(tp))) def qualCls = qual.tpe.classSymbol diff --git a/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala b/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala index f2d8d4d4a075..b8a9e8dfe3c8 100644 --- a/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala +++ b/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala @@ -23,7 +23,7 @@ class UncurryTreeTransform extends MiniPhaseTransform with InfoTransformer { showType ctx.atNextPhase(showType(_)) showType - cpy.Apply(tree, fn, args ++ tree.args) + cpy.Apply(tree)(fn, args ++ tree.args) case _ => tree }} diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 0991bf4a8b4f..86327d2fc942 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -404,7 +404,7 @@ trait Applications extends Compatibility { self: Typer => val result = { var typedArgs = typedArgBuf.toList - val app0 = cpy.Apply(app, normalizedFun, typedArgs) + val app0 = cpy.Apply(app)(normalizedFun, typedArgs) val app1 = if (!success) app0.withType(ErrorType) else { @@ -470,7 +470,7 @@ trait Applications extends Compatibility { self: Typer => failedState.commit() failedVal } else typedApply( - cpy.Apply(tree, untpd.TypedSplice(fun2), proto.typedArgs map untpd.TypedSplice), pt) + cpy.Apply(tree)(untpd.TypedSplice(fun2), proto.typedArgs map untpd.TypedSplice), pt) } case _ => fun1.tpe match { @@ -527,7 +527,7 @@ trait Applications extends Compatibility { self: Typer => checkBounds(typedArgs, pt, tree.pos) case _ => } - assignType(cpy.TypeApply(tree, typedFn, typedArgs), typedFn, typedArgs) + assignType(cpy.TypeApply(tree)(typedFn, typedArgs), typedFn, typedArgs) } def typedUnApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = track("typedUnApply") { @@ -712,14 +712,14 @@ trait Applications extends Compatibility { self: Typer => List.fill(argTypes.length - args.length)(WildcardType) } val unapplyPatterns = (bunchedArgs, argTypes).zipped map (typed(_, _)) - val result = assignType(cpy.UnApply(tree, unapplyFn, unapplyImplicits, unapplyPatterns), ownType) + val result = assignType(cpy.UnApply(tree)(unapplyFn, unapplyImplicits, unapplyPatterns), ownType) unapp.println(s"unapply patterns = $unapplyPatterns") if ((ownType eq pt) || ownType.isError) result else Typed(result, TypeTree(ownType)) case tp => val unapplyErr = if (tp.isError) unapplyFn else notAnExtractor(unapplyFn) val typedArgsErr = args mapconserve (typed(_, defn.AnyType)) - cpy.UnApply(tree, unapplyErr, Nil, typedArgsErr) withType ErrorType + cpy.UnApply(tree)(unapplyErr, Nil, typedArgsErr) withType ErrorType } } @@ -881,7 +881,7 @@ trait Applications extends Compatibility { self: Typer => def treeShape(tree: untpd.Tree): Tree = tree match { case NamedArg(name, arg) => val argShape = treeShape(arg) - cpy.NamedArg(tree, name, argShape).withType(argShape.tpe) + cpy.NamedArg(tree)(name, argShape).withType(argShape.tpe) case _ => dummyTreeOfType(typeShape(tree)) } diff --git a/src/dotty/tools/dotc/typer/EtaExpansion.scala b/src/dotty/tools/dotc/typer/EtaExpansion.scala index 69b512416d0a..790a848a77de 100644 --- a/src/dotty/tools/dotc/typer/EtaExpansion.scala +++ b/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -35,9 +35,9 @@ object EtaExpansion { */ def liftAssigned(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = tree match { case Apply(fn @ Select(pre, name), args) => - cpy.Apply(tree, cpy.Select(fn, lift(defs, pre), name), liftArgs(defs, fn.tpe, args)) + cpy.Apply(tree)(cpy.Select(fn)(lift(defs, pre), name), liftArgs(defs, fn.tpe, args)) case Select(pre, name) => - cpy.Select(tree, lift(defs, pre), name) + cpy.Select(tree)(lift(defs, pre), name) case _ => tree } @@ -80,11 +80,11 @@ object EtaExpansion { */ def liftApp(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = tree match { case Apply(fn, args) => - cpy.Apply(tree, liftApp(defs, fn), liftArgs(defs, fn.tpe, args)) + cpy.Apply(tree)(liftApp(defs, fn), liftArgs(defs, fn.tpe, args)) case TypeApply(fn, targs) => - cpy.TypeApply(tree, liftApp(defs, fn), targs) + cpy.TypeApply(tree)(liftApp(defs, fn), targs) case Select(pre, name) if isPureRef(tree) => - cpy.Select(tree, liftApp(defs, pre), name) + cpy.Select(tree)(liftApp(defs, pre), name) case Block(stats, expr) => liftApp(defs ++= stats, expr) case New(tpt) => diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 1002abe4d35a..638caba5b38d 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -366,9 +366,9 @@ class Namer { typer: Typer => val Thicket(vdef :: (mcls @ TypeDef(_, _, impl: Template)) :: Nil) = mdef.attachment(ExpandedTree) cdef.attachmentOrElse(ExpandedTree, cdef) match { case Thicket(cls :: mval :: TypeDef(_, _, compimpl: Template) :: crest) => - val mcls1 = cpy.TypeDef(mcls, mcls.mods, mcls.name, - cpy.Template(impl, impl.constr, impl.parents, impl.self, - compimpl.body ++ impl.body)) + val mcls1 = cpy.TypeDef(mcls)(mcls.mods, mcls.name, + cpy.Template(impl)(impl.constr, impl.parents, impl.self, + compimpl.body ++ impl.body), Nil) mdef.putAttachment(ExpandedTree, Thicket(vdef :: mcls1 :: Nil)) cdef.putAttachment(ExpandedTree, Thicket(cls :: crest)) case _ => diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala index 392b8dca1abe..dbf353f9efcd 100644 --- a/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/src/dotty/tools/dotc/typer/ReTyper.scala @@ -32,13 +32,13 @@ class ReTyper extends Typer { override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { assert(tree.hasType) val qual1 = typed(tree.qualifier, AnySelectionProto) - untpd.cpy.Select(tree, qual1, tree.name).withType(tree.typeOpt) + untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt) } override def typedSelectFromTypeTree(tree: untpd.SelectFromTypeTree, pt: Type)(implicit ctx: Context): SelectFromTypeTree = { assert(tree.hasType) val qual1 = typed(tree.qualifier, AnySelectionProto) - untpd.cpy.SelectFromTypeTree(tree, qual1, tree.name).withType(tree.typeOpt) + untpd.cpy.SelectFromTypeTree(tree)(qual1, tree.name).withType(tree.typeOpt) } override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Literal = @@ -50,7 +50,7 @@ class ReTyper extends Typer { override def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Bind = { assert(tree.hasType) val body1 = typed(tree.body, pt) - untpd.cpy.Bind(tree, tree.name, body1).withType(tree.typeOpt) + untpd.cpy.Bind(tree)(tree.name, body1).withType(tree.typeOpt) } override def localDummy(cls: ClassSymbol, impl: untpd.Template)(implicit ctx: Context) = impl.symbol diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 2024a993ebbb..cd9ddb0bd5cc 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -272,13 +272,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = track("typedSelect") { val qual1 = typedExpr(tree.qualifier, selectionProto(tree.name, pt, this)) if (tree.name.isTypeName) checkStable(qual1.tpe, qual1.pos) - checkValue(assignType(cpy.Select(tree, qual1, tree.name), qual1), pt) + checkValue(assignType(cpy.Select(tree)(qual1, tree.name), qual1), pt) } def typedSelectFromTypeTree(tree: untpd.SelectFromTypeTree, pt: Type)(implicit ctx: Context): SelectFromTypeTree = track("typedSelectFromTypeTree") { val qual1 = typedType(tree.qualifier, selectionProto(tree.name, pt, this)) checkLegalPrefix(qual1.tpe, tree.name, qual1.pos) - assignType(cpy.SelectFromTypeTree(tree, qual1, tree.name), qual1) + assignType(cpy.SelectFromTypeTree(tree)(qual1, tree.name), qual1) } def typedThis(tree: untpd.This)(implicit ctx: Context): Tree = track("typedThis") { @@ -291,7 +291,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case pt: SelectionProto if pt.name == nme.CONSTRUCTOR => true case _ => false } - assignType(cpy.Super(tree, qual1, tree.mix), qual1, inConstrCall) + assignType(cpy.Super(tree)(qual1, tree.mix), qual1, inConstrCall) } def typedLiteral(tree: untpd.Literal)(implicit ctx: Context) = track("typedLiteral") { @@ -304,11 +304,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit import untpd._ val x = tpnme.ANON_CLASS val clsDef = TypeDef(Modifiers(Final), x, templ) - typed(cpy.Block(tree, clsDef :: Nil, New(Ident(x), Nil)), pt) + typed(cpy.Block(tree)(clsDef :: Nil, New(Ident(x), Nil)), pt) case _ => val tpt1 = typedType(tree.tpt) checkClassTypeWithStablePrefix(tpt1.tpe, tpt1.pos, traitReq = false) - assignType(cpy.New(tree, tpt1), tpt1) + assignType(cpy.New(tree)(tpt1), tpt1) // todo in a later phase: checkInstantiatable(cls, tpt1.pos) } } @@ -320,7 +320,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } val left1 = typed(tree.left, leftProto) val right1 = typed(tree.right, rightProto) - assignType(cpy.Pair(tree, left1, right1), left1, right1) + assignType(cpy.Pair(tree)(left1, right1), left1, right1) } def typedTyped(tree: untpd.Typed, pt: Type)(implicit ctx: Context): Tree = track("typedTyped") { @@ -329,7 +329,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val expr1 = if (isWildcard) tree.expr withType tpt1.tpe else typed(tree.expr, tpt1.tpe) - assignType(cpy.Typed(tree, expr1, tpt1), tpt1) + assignType(cpy.Typed(tree)(expr1, tpt1), tpt1) } tree.expr match { case id: untpd.Ident if (ctx.mode is Mode.Pattern) && isVarPattern(id) => @@ -348,15 +348,15 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedNamedArg(tree: untpd.NamedArg, pt: Type)(implicit ctx: Context) = track("typedNamedArg") { val arg1 = typed(tree.arg, pt) - assignType(cpy.NamedArg(tree, tree.name, arg1), arg1) + assignType(cpy.NamedArg(tree)(tree.name, arg1), arg1) } def typedAssign(tree: untpd.Assign, pt: Type)(implicit ctx: Context) = track("typedAssign") { tree.lhs match { case lhs @ Apply(fn, args) => - typed(cpy.Apply(lhs, untpd.Select(fn, nme.update), args :+ tree.rhs), pt) + typed(cpy.Apply(lhs)(untpd.Select(fn, nme.update), args :+ tree.rhs), pt) case untpd.TypedSplice(Apply(Select(fn, app), args)) if app == nme.apply => - typed(cpy.Apply(fn, + typed(cpy.Apply(fn)( untpd.Select(untpd.TypedSplice(fn), nme.update), (args map untpd.TypedSplice) :+ tree.rhs), pt) case lhs => @@ -364,10 +364,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def lhs1 = typed(untpd.TypedSplice(lhsCore)) lhsCore.tpe match { case ref: TermRef if ref.symbol is (Mutable, butNot = Accessor) => - assignType(cpy.Assign(tree, lhs1, typed(tree.rhs, ref.info))) + assignType(cpy.Assign(tree)(lhs1, typed(tree.rhs, ref.info))) case _ => def reassignmentToVal = - errorTree(cpy.Assign(tree, lhsCore, typed(tree.rhs, lhs1.tpe.widen)), + errorTree(cpy.Assign(tree)(lhsCore, typed(tree.rhs, lhs1.tpe.widen)), "reassignment to val") lhsCore.tpe match { case ref: TermRef => // todo: further conditions to impose on getter? @@ -379,7 +379,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val setterTypeRaw = pre select (setterName, setter) val setterType = ensureAccessible(setterTypeRaw, isSuperSelection(lhsCore), tree.pos) val lhs2 = lhsCore.withName(setterName).withType(setterType) - typed(cpy.Apply(tree, untpd.TypedSplice(lhs2), tree.rhs :: Nil)) + typed(cpy.Apply(tree)(untpd.TypedSplice(lhs2), tree.rhs :: Nil)) case _ => reassignmentToVal } @@ -394,7 +394,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val exprCtx = index(tree.stats) val stats1 = typedStats(tree.stats, ctx.owner) val expr1 = typedExpr(tree.expr, pt)(exprCtx) - ensureNoLocalRefs(assignType(cpy.Block(tree, stats1, expr1), stats1, expr1), pt) + ensureNoLocalRefs(assignType(cpy.Block(tree)(stats1, expr1), stats1, expr1), pt) } def escapingRefs(block: Block)(implicit ctx: Context): collection.Set[NamedType] = { @@ -432,11 +432,11 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (leaks.isEmpty) block else if (isFullyDefined(pt, ForceDegree.all)) { val expr1 = Typed(expr, TypeTree(pt)) - cpy.Block(block, stats, expr1) withType expr1.tpe // no assignType here because avoid is redundant + cpy.Block(block)(stats, expr1) withType expr1.tpe // no assignType here because avoid is redundant } else if (!forcedDefined) { fullyDefinedType(block.tpe, "block", block.pos) val expr1 = Typed(expr, TypeTree(avoid(block.tpe, localSyms(stats)))) - val block1 = cpy.Block(block, stats, expr1) withType expr1.tpe // no assignType here because avoid is already done + val block1 = cpy.Block(block)(stats, expr1) withType expr1.tpe // no assignType here because avoid is already done ensureNoLocalRefs(block1, pt, forcedDefined = true) } else errorTree(block, @@ -447,13 +447,13 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val cond1 = typed(tree.cond, defn.BooleanType) val thenp1 = typed(tree.thenp, pt) val elsep1 = typed(tree.elsep orElse untpd.unitLiteral withPos tree.pos, pt) - assignType(cpy.If(tree, cond1, thenp1, elsep1), thenp1, elsep1) + assignType(cpy.If(tree)(cond1, thenp1, elsep1), thenp1, elsep1) } def typedFunction(tree: untpd.Function, pt: Type)(implicit ctx: Context) = track("typedFunction") { val untpd.Function(args, body) = tree if (ctx.mode is Mode.Type) - typed(cpy.AppliedTypeTree(tree, + typed(cpy.AppliedTypeTree(tree)( untpd.TypeTree(defn.FunctionClass(args.length).typeRef), args :+ body), pt) else { val params = args.asInstanceOf[List[untpd.ValDef]] @@ -491,7 +491,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val protoArgs = args map (_ withType WildcardType) val callProto = FunProto(protoArgs, WildcardType, this) val expr1 = typedExpr(expr, callProto) - fnBody = cpy.Apply(fnBody, untpd.TypedSplice(expr1), args) + fnBody = cpy.Apply(fnBody)(untpd.TypedSplice(expr1), args) expr1.tpe } case _ => @@ -533,7 +533,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (!param.tpt.isEmpty) param else { val paramTpt = untpd.TypeTree(inferredParamType(param, protoFormal(i))) - cpy.ValDef(param, param.mods, param.name, paramTpt, param.rhs) + cpy.ValDef(param)(param.mods, param.name, paramTpt, param.rhs) } // Define result type of closure as the expected type, thereby pushing @@ -569,7 +569,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case tp => throw new Error(i"internal error: closing over non-method $tp, pos = ${tree.pos}") } - assignType(cpy.Closure(tree, env1, meth1, target), meth1, target) + assignType(cpy.Closure(tree)(env1, meth1, target), meth1, target) } def typedMatch(tree: untpd.Match, pt: Type)(implicit ctx: Context) = track("typedMatch") { @@ -612,7 +612,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } val guard1 = typedExpr(tree.guard, defn.BooleanType) val body1 = typedExpr(tree.body, pt) - assignType(cpy.CaseDef(tree, pat, guard1, body1), body1) + assignType(cpy.CaseDef(tree)(pat, guard1, body1), body1) } val doCase: () => CaseDef = () => caseRest(typedPattern(tree.pat, selType))(ctx.fresh.setNewScope) @@ -620,7 +620,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } val cases1 = tree.cases mapconserve typedCase - assignType(cpy.Match(tree, sel1, cases1), cases1) + assignType(cpy.Match(tree)(sel1, cases1), cases1) } } @@ -642,25 +642,25 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } val (from, proto) = enclMethInfo(ctx) val expr1 = typedExpr(tree.expr orElse untpd.unitLiteral.withPos(tree.pos), proto) - assignType(cpy.Return(tree, expr1, from)) + assignType(cpy.Return(tree)(expr1, from)) } def typedTry(tree: untpd.Try, pt: Type)(implicit ctx: Context): Try = track("typedTry") { val expr1 = typed(tree.expr, pt) val handler1 = typed(tree.handler, defn.FunctionType(defn.ThrowableType :: Nil, pt)) val finalizer1 = typed(tree.finalizer, defn.UnitType) - assignType(cpy.Try(tree, expr1, handler1, finalizer1), expr1, handler1) + assignType(cpy.Try(tree)(expr1, handler1, finalizer1), expr1, handler1) } def typedThrow(tree: untpd.Throw)(implicit ctx: Context): Throw = track("typedThrow") { val expr1 = typed(tree.expr, defn.ThrowableType) - assignType(cpy.Throw(tree, expr1)) + assignType(cpy.Throw(tree)(expr1)) } def typedSeqLiteral(tree: untpd.SeqLiteral, pt: Type)(implicit ctx: Context): SeqLiteral = track("typedSeqLiteral") { val proto1 = pt.elemType orElse WildcardType val elems1 = tree.elems mapconserve (typed(_, proto1)) - assignType(cpy.SeqLiteral(tree, elems1), elems1) + assignType(cpy.SeqLiteral(tree)(elems1), elems1) } def typedTypeTree(tree: untpd.TypeTree, pt: Type)(implicit ctx: Context): TypeTree = track("typedTypeTree") { @@ -685,26 +685,26 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } else { val original1 = typed(tree.original) - cpy.TypeTree(tree, original1).withType(original1.tpe) + cpy.TypeTree(tree)(original1).withType(original1.tpe) } } def typedSingletonTypeTree(tree: untpd.SingletonTypeTree)(implicit ctx: Context): SingletonTypeTree = track("typedSingletonTypeTree") { val ref1 = typedExpr(tree.ref) checkStable(ref1.tpe, tree.pos) - assignType(cpy.SingletonTypeTree(tree, ref1), ref1) + assignType(cpy.SingletonTypeTree(tree)(ref1), ref1) } def typedAndTypeTree(tree: untpd.AndTypeTree)(implicit ctx: Context): AndTypeTree = track("typedAndTypeTree") { val left1 = typed(tree.left) val right1 = typed(tree.right) - assignType(cpy.AndTypeTree(tree, left1, right1), left1, right1) + assignType(cpy.AndTypeTree(tree)(left1, right1), left1, right1) } def typedOrTypeTree(tree: untpd.OrTypeTree)(implicit ctx: Context): OrTypeTree = track("typedOrTypeTree") { val left1 = typed(tree.left) val right1 = typed(tree.right) - assignType(cpy.OrTypeTree(tree, left1, right1), left1, right1) + assignType(cpy.OrTypeTree(tree)(left1, right1), left1, right1) } def typedRefinedTypeTree(tree: untpd.RefinedTypeTree)(implicit ctx: Context): RefinedTypeTree = track("typedRefinedTypeTree") { @@ -726,7 +726,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit RefinedType(parent, rsym.name, rt => rinfo.substThis(refineCls, RefinedThis(rt))) // todo later: check that refinement is within bounds } - val res = cpy.RefinedTypeTree(tree, tpt1, refinements1) withType + val res = cpy.RefinedTypeTree(tree)(tpt1, refinements1) withType (tpt1.tpe /: refinements1)(addRefinement) typr.println(i"typed refinement: ${res.tpe}") res @@ -736,12 +736,12 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val tpt1 = typed(tree.tpt) val args1 = tree.args mapconserve (typed(_)) // check that arguments conform to bounds is done in phase FirstTransform - assignType(cpy.AppliedTypeTree(tree, tpt1, args1), tpt1, args1) + assignType(cpy.AppliedTypeTree(tree)(tpt1, args1), tpt1, args1) } def typedByNameTypeTree(tree: untpd.ByNameTypeTree)(implicit ctx: Context): ByNameTypeTree = track("typedByNameTypeTree") { val result1 = typed(tree.result) - assignType(cpy.ByNameTypeTree(tree, result1), result1) + assignType(cpy.ByNameTypeTree(tree)(result1), result1) } def typedTypeBoundsTree(tree: untpd.TypeBoundsTree)(implicit ctx: Context): TypeBoundsTree = track("typedTypeBoundsTree") { @@ -750,19 +750,19 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val hi1 = typed(hi) 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) + assignType(cpy.TypeBoundsTree(tree)(lo1, hi1), lo1, hi1) } def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Bind = track("typedBind") { val body1 = typed(tree.body, pt) typr.println(i"typed bind $tree pt = $pt bodytpe = ${body1.tpe}") val sym = ctx.newSymbol(ctx.owner, tree.name.asTermName, EmptyFlags, body1.tpe, coord = tree.pos) - assignType(cpy.Bind(tree, tree.name, body1), sym) + assignType(cpy.Bind(tree)(tree.name, body1), sym) } def typedAlternative(tree: untpd.Alternative, pt: Type)(implicit ctx: Context): Alternative = track("typedAlternative") { val trees1 = tree.trees mapconserve (typed(_, pt)) - assignType(cpy.Alternative(tree, trees1), trees1) + assignType(cpy.Alternative(tree)(trees1), trees1) } def addTypedModifiersAnnotations(mods: untpd.Modifiers, sym: Symbol)(implicit ctx: Context): Modifiers = { @@ -789,7 +789,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit case Ident(nme.WILDCARD) => rhs withType tpt1.tpe case _ => typedExpr(rhs, tpt1.tpe) } - assignType(cpy.ValDef(vdef, mods1, name, tpt1, rhs1), sym) + assignType(cpy.ValDef(vdef)(mods1, name, tpt1, rhs1), sym) } def typedDefDef(ddef: untpd.DefDef, sym: Symbol)(implicit ctx: Context) = track("typedDefDef") { @@ -800,7 +800,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit if (sym is Implicit) checkImplicitParamsNotSingletons(vparamss1) val tpt1 = typedType(tpt) val rhs1 = typedExpr(rhs, tpt1.tpe) - assignType(cpy.DefDef(ddef, mods1, name, tparams1, vparamss1, tpt1, rhs1), sym) + assignType(cpy.DefDef(ddef)(mods1, name, tparams1, vparamss1, tpt1, rhs1), sym) //todo: make sure dependent method types do not depend on implicits or by-name params } @@ -808,7 +808,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val TypeDef(mods, name, rhs) = tdef val mods1 = addTypedModifiersAnnotations(mods, sym) val _ = typedType(rhs) // unused, typecheck only to remove from typedTree - assignType(cpy.TypeDef(tdef, mods1, name, TypeTree(sym.info)), sym) + assignType(cpy.TypeDef(tdef)(mods1, name, TypeTree(sym.info), Nil), sym) } def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context) = track("typedClassDef") { @@ -841,10 +841,10 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit val dummy = localDummy(cls, impl) val body1 = typedStats(body, dummy)(inClassContext(self1.symbol)) checkNoDoubleDefs(cls) - val impl1 = cpy.Template(impl, constr1, parents1, self1, body1) + val impl1 = cpy.Template(impl)(constr1, parents1, self1, body1) .withType(dummy.termRef) checkVariance(impl1) - assignType(cpy.TypeDef(cdef, mods1, name, impl1), cls) + assignType(cpy.TypeDef(cdef)(mods1, name, impl1, Nil), cls) // todo later: check that // 1. If class is non-abstract, it is instantiatable: @@ -864,7 +864,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedImport(imp: untpd.Import, sym: Symbol)(implicit ctx: Context): Import = track("typedImport") { val expr1 = typedExpr(imp.expr, AnySelectionProto) checkStable(expr1.tpe, imp.expr.pos) - assignType(cpy.Import(imp, expr1, imp.selectors), sym) + assignType(cpy.Import(imp)(expr1, imp.selectors), sym) } def typedPackageDef(tree: untpd.PackageDef)(implicit ctx: Context): Tree = track("typedPackageDef") { @@ -877,17 +877,17 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit ctx } val stats1 = typedStats(tree.stats, pkg.moduleClass)(packageContext) - cpy.PackageDef(tree, pid1.asInstanceOf[RefTree], stats1) withType pkg.valRef + cpy.PackageDef(tree)(pid1.asInstanceOf[RefTree], stats1) withType pkg.valRef } def typedAnnotated(tree: untpd.Annotated, pt: Type)(implicit ctx: Context): Tree = track("typedAnnotated") { val annot1 = typedExpr(tree.annot, defn.AnnotationClass.typeRef) val arg1 = typed(tree.arg, pt) if (ctx.mode is Mode.Type) - assignType(cpy.Annotated(tree, annot1, arg1), annot1, arg1) + assignType(cpy.Annotated(tree)(annot1, arg1), annot1, arg1) else { val tpt = TypeTree(AnnotatedType(Annotation(annot1), arg1.tpe.widen)) - assignType(cpy.Typed(tree, arg1, tpt), tpt) + assignType(cpy.Typed(tree)(arg1, tpt), tpt) } } @@ -1074,7 +1074,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit tryEither { implicit ctx => val qual1 = adaptInterpolated(qual, qualProto, EmptyTree) if ((qual eq qual1) || ctx.reporter.hasErrors) tree - else typedSelect(cpy.Select(tree, untpd.TypedSplice(qual1), name), pt) + else typedSelect(cpy.Select(tree)(untpd.TypedSplice(qual1), name), pt) } { (_, _) => tree } case _ => tree @@ -1253,7 +1253,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit // but this prevents case blocks from implementing polymorphic partial functions, // since we do not know the result parameter a priori. Have to wait until the // body is typechecked. - return cpy.Closure(tree, Nil, id, TypeTree(pt)).withType(pt) + return cpy.Closure(tree)(Nil, id, TypeTree(pt)).withType(pt) case _ => } case _ => diff --git a/test/test/DeSugarTest.scala b/test/test/DeSugarTest.scala index f38706d671e9..c628a9ad15a6 100644 --- a/test/test/DeSugarTest.scala +++ b/test/test/DeSugarTest.scala @@ -42,29 +42,29 @@ class DeSugarTest extends ParserTest { case PostfixOp(od, op) => PostfixOp(transform(od), op) case Select(qual, name) => - cpy.Select(tree1, transform(qual, Expr), name) + cpy.Select(tree1)(transform(qual, Expr), name) case Apply(fn, args) => - cpy.Apply(tree1, transform(fn, Expr), transform(args)) + cpy.Apply(tree1)(transform(fn, Expr), transform(args)) case TypeApply(fn, args) => - cpy.TypeApply(tree1, transform(fn, Expr), transform(args, Type)) + cpy.TypeApply(tree1)(transform(fn, Expr), transform(args, Type)) case New(tpt) => - cpy.New(tree1, transform(tpt, Type)) + cpy.New(tree1)(transform(tpt, Type)) case Typed(expr, tpt) => - cpy.Typed(tree1, transform(expr), transform(tpt, Type)) + cpy.Typed(tree1)(transform(expr), transform(tpt, Type)) case CaseDef(pat, guard, body) => - cpy.CaseDef(tree1, transform(pat, Pattern), transform(guard), transform(body)) + cpy.CaseDef(tree1)(transform(pat, Pattern), transform(guard), transform(body)) case SeqLiteral(elems) => - cpy.SeqLiteral(tree1, transform(elems)) + cpy.SeqLiteral(tree1)(transform(elems)) case UnApply(fun, implicits, patterns) => - cpy.UnApply(tree1, transform(fun, Expr), transform(implicits), transform(patterns)) + cpy.UnApply(tree1)(transform(fun, Expr), transform(implicits), transform(patterns)) case ValDef(mods, name, tpt, rhs) => - cpy.ValDef(tree1, mods, name, transform(tpt, Type), transform(rhs)) + cpy.ValDef(tree1)(mods, name, transform(tpt, Type), transform(rhs)) case DefDef(mods, name, tparams, vparamss, tpt, rhs) => - cpy.DefDef(tree1, mods, name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt, Type), transform(rhs)) + cpy.DefDef(tree1)(mods, name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt, Type), transform(rhs)) case tree1 @ TypeDef(mods, name, rhs) => - cpy.TypeDef(tree1, mods, name, transform(rhs, Type), transformSub(tree1.tparams)) + cpy.TypeDef(tree1)(mods, name, transform(rhs, Type), transformSub(tree1.tparams)) case Template(constr, parents, self, body) => - cpy.Template(tree1, transformSub(constr), transform(parents), transformSub(self), transform(body, Expr)) + cpy.Template(tree1)(transformSub(constr), transform(parents), transformSub(self), transform(body, Expr)) case Thicket(trees) => Thicket(flatten(trees mapConserve super.transform)) case tree1 => diff --git a/test/test/transform/TreeTransformerTest.scala b/test/test/transform/TreeTransformerTest.scala index aea372bf44b2..bfabc2f2c732 100644 --- a/test/test/transform/TreeTransformerTest.scala +++ b/test/test/transform/TreeTransformerTest.scala @@ -66,7 +66,7 @@ class TreeTransformerTest extends DottyTest { Assert.assertTrue("transformation of children succeeded", tree.rhs.toString == "Literal(Constant(-1))" ) - tpd.cpy.ValDef(tree, tree.mods, tree.name, tree.tpt, tpd.Literal(Constant(2))) + tpd.cpy.ValDef(tree)(tree.mods, tree.name, tree.tpt, tpd.Literal(Constant(2))) } init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId) @@ -95,14 +95,14 @@ class TreeTransformerTest extends DottyTest { Assert.assertTrue("correct constant", tree.const.toString == "Constant(1)" ) - tpd.cpy.Literal(tree, Constant(-1)) + tpd.cpy.Literal(tree)(Constant(-1)) } override def transformValDef(tree: tpd.ValDef)(implicit ctx: Context, info: TransformerInfo): tpd.ValDef = { Assert.assertTrue("transformation of children succeeded", tree.rhs.toString == "Literal(Constant(-1))" ) - tpd.cpy.ValDef(tree, tree.mods, tree.name, tree.tpt, tpd.Literal(Constant(2))) + tpd.cpy.ValDef(tree)(tree.mods, tree.name, tree.tpt, tpd.Literal(Constant(2))) } init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId) @@ -113,7 +113,7 @@ class TreeTransformerTest extends DottyTest { Assert.assertTrue("transformation of children succeeded", tree.rhs.toString == "Literal(Constant(2))" ) - tpd.cpy.ValDef(tree, tree.mods, tree.name, tree.tpt, tpd.Literal(Constant(3))) + tpd.cpy.ValDef(tree)(tree.mods, tree.name, tree.tpt, tpd.Literal(Constant(3))) } init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId) @@ -142,7 +142,7 @@ class TreeTransformerTest extends DottyTest { Assert.assertTrue("correct constant", tree.const.toString == "Constant(1)" ) - tpd.cpy.Literal(tree, Constant(-1)) + tpd.cpy.Literal(tree)(Constant(-1)) } override def transformValDef(tree: tpd.ValDef)(implicit ctx: Context, info: TransformerInfo) = { @@ -150,7 +150,7 @@ class TreeTransformerTest extends DottyTest { Assert.assertTrue("transformation of children succeeded", tree.rhs.toString == "Literal(Constant(-3))" ) - tpd.cpy.ValDef(tree, tree.mods, tree.name, tree.tpt, transformFollowing(tpd.Literal(Constant(2)))) + tpd.cpy.ValDef(tree)(tree.mods, tree.name, tree.tpt, transformFollowing(tpd.Literal(Constant(2)))) } init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId) @@ -173,7 +173,7 @@ class TreeTransformerTest extends DottyTest { case _ => Assert.fail("to many constants seen") } constantsSeen += 1 - tpd.cpy.Literal(tree, Constant(-3)) + tpd.cpy.Literal(tree)(Constant(-3)) } override def transformValDef(tree: tpd.ValDef)(implicit ctx: Context, info: TransformerInfo) = { @@ -181,7 +181,7 @@ class TreeTransformerTest extends DottyTest { Assert.assertTrue("transformation of children succeeded", tree.rhs.toString == "Literal(Constant(-3))" ) - transformFollowing(tpd.cpy.ValDef(tree, tree.mods, tree.name, tree.tpt, tpd.Literal(Constant(3)))) + transformFollowing(tpd.cpy.ValDef(tree)(tree.mods, tree.name, tree.tpt, tpd.Literal(Constant(3)))) } init(ctx, ctx.period.firstPhaseId, ctx.period.lastPhaseId) From 891aeba7ac64be808a7a5959fedecd4e2e41dcf3 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 13 Aug 2014 15:05:03 +0200 Subject: [PATCH 37/44] Switched to new cpy scheme. Avoid mentioning arguments that are unchanged. --- src/dotty/tools/dotc/ast/Desugar.scala | 52 ++++++++++--------- src/dotty/tools/dotc/ast/Trees.scala | 10 ++-- src/dotty/tools/dotc/parsing/Parsers.scala | 2 +- .../dotc/transform/ExtensionMethods.scala | 4 +- .../tools/dotc/transform/MacroTransform.scala | 2 +- .../tools/dotc/transform/Nullarify.scala | 4 +- .../tools/dotc/transform/SuperAccessors.scala | 2 +- .../dotc/transform/UncurryTreeTransform.scala | 2 +- src/dotty/tools/dotc/typer/Namer.scala | 5 +- src/dotty/tools/dotc/typer/ReTyper.scala | 6 +-- 10 files changed, 45 insertions(+), 44 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 5e947fc07c17..fcaef9730c47 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -72,11 +72,13 @@ object desugar { /** A type definition copied from `tdef` with a rhs typetree derived from it */ def derivedTypeParam(tdef: TypeDef) = - cpy.TypeDef(tdef)(rhs = new DerivedFromParamTree() withPos tdef.rhs.pos watching tdef) + cpy.TypeDef(tdef)( + rhs = new DerivedFromParamTree() withPos tdef.rhs.pos watching tdef) /** A value definition copied from `vdef` with a tpt typetree derived from it */ def derivedTermParam(vdef: ValDef) = - cpy.ValDef(vdef)(tpt = new DerivedFromParamTree() withPos vdef.tpt.pos watching vdef) + cpy.ValDef(vdef)( + tpt = new DerivedFromParamTree() withPos vdef.tpt.pos watching vdef) // ----- Desugar methods ------------------------------------------------- @@ -157,7 +159,7 @@ object desugar { } def normalizedVparamss = vparamss map (_ map (vparam => - cpy.ValDef(vparam)(vparam.mods, vparam.name, vparam.tpt, EmptyTree))) + cpy.ValDef(vparam)(rhs = EmptyTree))) def defaultGetters(vparamss: List[List[ValDef]], n: Int): List[DefDef] = vparamss match { case (vparam :: vparams) :: vparamss1 => @@ -194,18 +196,17 @@ object desugar { * class C { type v C$T; type v T = C$T } */ def typeDef(tdef: TypeDef)(implicit ctx: Context): Tree = { - val TypeDef(mods, name, rhs) = tdef - if (mods is PrivateLocalParam) { + if (tdef.mods is PrivateLocalParam) { val tparam = cpy.TypeDef(tdef)( - mods = mods &~ PrivateLocal | ExpandedName, - name = name.expandedName(ctx.owner)) + mods = tdef.mods &~ PrivateLocal | ExpandedName, + name = tdef.name.expandedName(ctx.owner)) val alias = cpy.TypeDef(tdef)( - mods = Modifiers(PrivateLocalParamAccessor | Synthetic | mods.flags & VarianceFlags), + mods = Modifiers(PrivateLocalParamAccessor | Synthetic | tdef.mods.flags & VarianceFlags), rhs = refOfDef(tparam), tparams = Nil) Thicket(tparam, alias) } - else cpy.TypeDef(tdef)(mods, name, rhs, tdef.tparams) // TODO: why copy? + else tdef } private val synthetic = Modifiers(Synthetic) @@ -214,7 +215,7 @@ object desugar { cpy.TypeDef(tparam)(mods = Modifiers(Param)) private def toDefParam(vparam: ValDef) = - cpy.ValDef(vparam)(Modifiers(Param | vparam.mods.flags & Implicit), vparam.name, vparam.tpt, vparam.rhs) + cpy.ValDef(vparam)(mods = Modifiers(Param | vparam.mods.flags & Implicit)) /** The expansion of a class definition. See inline comments for what is involved */ def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = { @@ -296,9 +297,9 @@ object desugar { def copyDefault(vparam: ValDef) = makeAnnotated(defn.UncheckedVarianceAnnot, refOfDef(vparam)) val copyFirstParams = derivedVparamss.head.map(vparam => - cpy.ValDef(vparam)(vparam.mods, vparam.name, vparam.tpt, copyDefault(vparam))) + cpy.ValDef(vparam)(rhs = copyDefault(vparam))) val copyRestParamss = derivedVparamss.tail.nestedMap(vparam => - cpy.ValDef(vparam)(vparam.mods, vparam.name, vparam.tpt, EmptyTree)) + cpy.ValDef(vparam)(rhs = EmptyTree)) DefDef(synthetic, nme.copy, derivedTparams, copyFirstParams :: copyRestParamss, TypeTree(), creatorExpr) :: Nil } copyMeths ::: isDefinedMeth :: productArityMeth :: productElemMeths.toList @@ -378,22 +379,23 @@ object desugar { val self1 = { val selfType = if (self.tpt.isEmpty) classTypeRef else self.tpt if (self.isEmpty) self - else cpy.ValDef(self)(self.mods | SelfName, self.name, selfType, self.rhs) + else cpy.ValDef(self)(mods = self.mods | SelfName, tpt = selfType) } val cdef1 = { val originalTparams = constr1.tparams.toIterator val originalVparams = constr1.vparamss.toIterator.flatten val tparamAccessors = derivedTparams map { tdef => - cpy.TypeDef(tdef)(originalTparams.next.mods, tdef.name, tdef.rhs, tdef.tparams) + cpy.TypeDef(tdef)(mods = originalTparams.next.mods) } val caseAccessor = if (mods is Case) CaseAccessor else EmptyFlags val vparamAccessors = derivedVparamss.flatten map { vdef => - cpy.ValDef(vdef)(originalVparams.next.mods | caseAccessor, vdef.name, vdef.tpt, vdef.rhs) + cpy.ValDef(vdef)(mods = originalVparams.next.mods | caseAccessor) } - cpy.TypeDef(cdef)(mods, name, - cpy.Template(impl)(constr, parents1, self1, - tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths), Nil) + cpy.TypeDef(cdef)( + rhs = cpy.Template(impl)(constr, parents1, self1, + tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths), + tparams = Nil) } // install the watch on classTycon @@ -414,18 +416,18 @@ object desugar { * final class name$ extends parents { self: name.type => body } */ def moduleDef(mdef: ModuleDef)(implicit ctx: Context): Tree = { - val ModuleDef(mods, name, tmpl @ Template(constr, parents, self, body)) = mdef + val ModuleDef(mods, name, tmpl) = mdef if (mods is Package) PackageDef(Ident(name), cpy.ModuleDef(mdef)(mods &~ Package, nme.PACKAGE, tmpl) :: Nil) else { val clsName = name.moduleClassName val clsRef = Ident(clsName) val modul = ValDef(mods | ModuleCreationFlags, name, clsRef, New(clsRef, Nil)) withPos mdef.pos - val ValDef(selfMods, selfName, selfTpt, selfRhs) = self - if (!selfTpt.isEmpty) ctx.error("object definition may not have a self type", self.pos) + val ValDef(selfMods, selfName, selfTpt, selfRhs) = tmpl.self + if (!selfTpt.isEmpty) ctx.error("object definition may not have a self type", tmpl.self.pos) val clsSelf = ValDef(selfMods, selfName, SingletonTypeTree(Ident(name)), selfRhs) - .withPos(self.pos orElse tmpl.pos.startPos) - val clsTmpl = cpy.Template(tmpl)(constr, parents, clsSelf, body) + .withPos(tmpl.self.pos orElse tmpl.pos.startPos) + val clsTmpl = cpy.Template(tmpl)(self = clsSelf, body = tmpl.body) val cls = TypeDef(mods.toTypeFlags & AccessFlags | ModuleClassCreationFlags, clsName, clsTmpl) Thicket(modul, classDef(cls)) } @@ -502,8 +504,8 @@ object desugar { */ def block(tree: Block)(implicit ctx: Context): Block = tree.expr match { case EmptyTree => - cpy.Block(tree)(tree.stats, - unitLiteral withPos (if (tree.stats.isEmpty) tree.pos else tree.pos.endPos)) + cpy.Block(tree)( + expr = unitLiteral withPos (if (tree.stats.isEmpty) tree.pos else tree.pos.endPos)) case _ => tree } diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 41ef88b54611..3ff600f4489c 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -433,7 +433,7 @@ object Trees { case class Select[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name) extends RefTree[T] { type ThisTree[-T >: Untyped] = Select[T] - def withName(name: Name)(implicit ctx: Context): untpd.Select = untpd.cpy.Select(this)(qualifier, name) + def withName(name: Name)(implicit ctx: Context): untpd.Select = untpd.cpy.Select(this)(name = name) } class SelectWithSig[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name, val sig: Signature) @@ -620,7 +620,7 @@ object Trees { case class SelectFromTypeTree[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name) extends RefTree[T] { type ThisTree[-T >: Untyped] = SelectFromTypeTree[T] - def withName(name: Name)(implicit ctx: Context): untpd.SelectFromTypeTree = untpd.cpy.SelectFromTypeTree(this)(qualifier, name) + def withName(name: Name)(implicit ctx: Context): untpd.SelectFromTypeTree = untpd.cpy.SelectFromTypeTree(this)(name = name) } /** left & right */ @@ -666,7 +666,7 @@ object Trees { extends NameTree[T] with DefTree[T] with PatternTree[T] { type ThisTree[-T >: Untyped] = Bind[T] override def envelope: Position = pos union initialPos - def withName(name: Name)(implicit ctx: Context): untpd.Bind = untpd.cpy.Bind(this)(name, body) + def withName(name: Name)(implicit ctx: Context): untpd.Bind = untpd.cpy.Bind(this)(name = name) } /** tree_1 | ... | tree_n */ @@ -697,7 +697,7 @@ object Trees { case class ValDef[-T >: Untyped] private[ast] (mods: Modifiers[T], name: TermName, tpt: Tree[T], rhs: Tree[T]) extends ValOrDefDef[T] { type ThisTree[-T >: Untyped] = ValDef[T] - def withName(name: Name)(implicit ctx: Context): untpd.ValDef = untpd.cpy.ValDef(this)(mods, name.toTermName, tpt, rhs) + def withName(name: Name)(implicit ctx: Context): untpd.ValDef = untpd.cpy.ValDef(this)(name = name.toTermName) assert(isEmpty || tpt != genericEmptyTree) } @@ -717,7 +717,7 @@ object Trees { case class TypeDef[-T >: Untyped] private[ast] (mods: Modifiers[T], name: TypeName, rhs: Tree[T]) extends MemberDef[T] { type ThisTree[-T >: Untyped] = TypeDef[T] - def withName(name: Name)(implicit ctx: Context): untpd.TypeDef = untpd.cpy.TypeDef(this)(mods, name.toTypeName, rhs, tparams) + def withName(name: Name)(implicit ctx: Context): untpd.TypeDef = untpd.cpy.TypeDef(this)(name = name.toTypeName) /** Is this a definition of a class? */ def isClassDef = rhs.isInstanceOf[Template[_]] diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index 522d94243e8a..e6de5bb5d347 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -963,7 +963,7 @@ object Parsers { val tpt = typeDependingOn(location) if (isWildcard(t) && location != Location.InPattern) { val vd :: rest = placeholderParams - placeholderParams = cpy.ValDef(vd)(vd.mods, vd.name, tpt, vd.rhs) :: rest + placeholderParams = cpy.ValDef(vd)(tpt = tpt) :: rest } Typed(t, tpt) } diff --git a/src/dotty/tools/dotc/transform/ExtensionMethods.scala b/src/dotty/tools/dotc/transform/ExtensionMethods.scala index e05852dc2494..42e10b126aee 100644 --- a/src/dotty/tools/dotc/transform/ExtensionMethods.scala +++ b/src/dotty/tools/dotc/transform/ExtensionMethods.scala @@ -162,10 +162,10 @@ class ExtensionMethods extends MacroTransform with DenotTransformer with FullPar paramAccessors.foreach(_.makeNotPrivateAfter(ctx.owner, thisTransformer)) super.transform(tree) } else if (ctx.owner.isStaticOwner) { - val tree1 @ Template(constr, parents, selfType, body) = super.transform(tree) + val tree1 @ Template(_, _, _, body) = super.transform(tree) extensionDefs remove tree1.symbol.owner match { case Some(defns) if defns.nonEmpty => - cpy.Template(tree1)(constr, parents, selfType, body ++ defns) + cpy.Template(tree1)(body = body ++ defns) case _ => tree1 } diff --git a/src/dotty/tools/dotc/transform/MacroTransform.scala b/src/dotty/tools/dotc/transform/MacroTransform.scala index 6f38c98a909c..734380661f90 100644 --- a/src/dotty/tools/dotc/transform/MacroTransform.scala +++ b/src/dotty/tools/dotc/transform/MacroTransform.scala @@ -71,6 +71,6 @@ abstract class MacroTransform extends Phase { } def transformSelf(vd: ValDef)(implicit ctx: Context) = - cpy.ValDef(vd)(vd.mods, vd.name, transform(vd.tpt), vd.rhs) + cpy.ValDef(vd)(tpt = transform(vd.tpt)) } } diff --git a/src/dotty/tools/dotc/transform/Nullarify.scala b/src/dotty/tools/dotc/transform/Nullarify.scala index 6ecb095b41fb..7cca19e5a121 100644 --- a/src/dotty/tools/dotc/transform/Nullarify.scala +++ b/src/dotty/tools/dotc/transform/Nullarify.scala @@ -72,7 +72,7 @@ class Nullarify extends MiniPhaseTransform with InfoTransformer { val MethodType(_, formals) = methType(funType, tree.fun) val args1 = tree.args.zipWithConserve(formals)(transformArg) - cpy.Apply(tree)(tree.fun, args1) withType nullarify(tree.tpe) + cpy.Apply(tree)(args = args1) withType nullarify(tree.tpe) } /** Insert () or .apply() if the term refers to something that was converted to a @@ -144,4 +144,4 @@ class Nullarify extends MiniPhaseTransform with InfoTransformer { if (defn.typeTestsOrCasts contains sym) tp else if (sym is Param) nullarifyParam(tp) else nullarify(tp) -} \ No newline at end of file +} diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index 62dd7f0c0b67..f0d25d9c2a80 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -295,7 +295,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this val body1 = forwardParamAccessors(transformStats(impl.body, tree.symbol)) accDefs -= currentClass ownStats ++= body1 - cpy.Template(tree)(impl.constr, impl.parents, impl.self, body1) + cpy.Template(impl)(body = body1) } transformTemplate diff --git a/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala b/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala index b8a9e8dfe3c8..c713a7d30e71 100644 --- a/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala +++ b/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala @@ -23,7 +23,7 @@ class UncurryTreeTransform extends MiniPhaseTransform with InfoTransformer { showType ctx.atNextPhase(showType(_)) showType - cpy.Apply(tree)(fn, args ++ tree.args) + cpy.Apply(tree)(args = args ++ tree.args) case _ => tree }} diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index 638caba5b38d..20a5a1204c67 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -366,9 +366,8 @@ class Namer { typer: Typer => val Thicket(vdef :: (mcls @ TypeDef(_, _, impl: Template)) :: Nil) = mdef.attachment(ExpandedTree) cdef.attachmentOrElse(ExpandedTree, cdef) match { case Thicket(cls :: mval :: TypeDef(_, _, compimpl: Template) :: crest) => - val mcls1 = cpy.TypeDef(mcls)(mcls.mods, mcls.name, - cpy.Template(impl)(impl.constr, impl.parents, impl.self, - compimpl.body ++ impl.body), Nil) + val mcls1 = cpy.TypeDef(mcls)( + rhs = cpy.Template(impl)(body = compimpl.body ++ impl.body)) mdef.putAttachment(ExpandedTree, Thicket(vdef :: mcls1 :: Nil)) cdef.putAttachment(ExpandedTree, Thicket(cls :: crest)) case _ => diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala index dbf353f9efcd..42128f67f566 100644 --- a/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/src/dotty/tools/dotc/typer/ReTyper.scala @@ -32,13 +32,13 @@ class ReTyper extends Typer { override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { assert(tree.hasType) val qual1 = typed(tree.qualifier, AnySelectionProto) - untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt) + untpd.cpy.Select(tree)(qualifier = qual1).withType(tree.typeOpt) } override def typedSelectFromTypeTree(tree: untpd.SelectFromTypeTree, pt: Type)(implicit ctx: Context): SelectFromTypeTree = { assert(tree.hasType) val qual1 = typed(tree.qualifier, AnySelectionProto) - untpd.cpy.SelectFromTypeTree(tree)(qual1, tree.name).withType(tree.typeOpt) + untpd.cpy.SelectFromTypeTree(tree)(qualifier = qual1).withType(tree.typeOpt) } override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Literal = @@ -50,7 +50,7 @@ class ReTyper extends Typer { override def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Bind = { assert(tree.hasType) val body1 = typed(tree.body, pt) - untpd.cpy.Bind(tree)(tree.name, body1).withType(tree.typeOpt) + untpd.cpy.Bind(tree)(body = body1).withType(tree.typeOpt) } override def localDummy(cls: ClassSymbol, impl: untpd.Template)(implicit ctx: Context) = impl.symbol From 0b41954ac5ea6280dda4f4bcb052b29923a282a6 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 13 Aug 2014 15:20:31 +0200 Subject: [PATCH 38/44] Restrict treee copiers with default arguments to trees with more than 2 elements. It's not really an abbreviation to do it for trees with fewer elements and it leads to unncessessary syntactic variation. --- src/dotty/tools/dotc/ast/Desugar.scala | 4 +- src/dotty/tools/dotc/ast/Trees.scala | 78 +++++++++---------- .../tools/dotc/transform/Nullarify.scala | 2 +- .../dotc/transform/UncurryTreeTransform.scala | 2 +- src/dotty/tools/dotc/typer/ReTyper.scala | 6 +- 5 files changed, 46 insertions(+), 46 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index fcaef9730c47..cfb9c338fd86 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -504,8 +504,8 @@ object desugar { */ def block(tree: Block)(implicit ctx: Context): Block = tree.expr match { case EmptyTree => - cpy.Block(tree)( - expr = unitLiteral withPos (if (tree.stats.isEmpty) tree.pos else tree.pos.endPos)) + cpy.Block(tree)(tree.stats, + unitLiteral withPos (if (tree.stats.isEmpty) tree.pos else tree.pos.endPos)) case _ => tree } diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 3ff600f4489c..b28cb87e6426 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -433,7 +433,7 @@ object Trees { case class Select[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name) extends RefTree[T] { type ThisTree[-T >: Untyped] = Select[T] - def withName(name: Name)(implicit ctx: Context): untpd.Select = untpd.cpy.Select(this)(name = name) + def withName(name: Name)(implicit ctx: Context): untpd.Select = untpd.cpy.Select(this)(qualifier, name) } class SelectWithSig[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name, val sig: Signature) @@ -620,7 +620,7 @@ object Trees { case class SelectFromTypeTree[-T >: Untyped] private[ast] (qualifier: Tree[T], name: Name) extends RefTree[T] { type ThisTree[-T >: Untyped] = SelectFromTypeTree[T] - def withName(name: Name)(implicit ctx: Context): untpd.SelectFromTypeTree = untpd.cpy.SelectFromTypeTree(this)(name = name) + def withName(name: Name)(implicit ctx: Context): untpd.SelectFromTypeTree = untpd.cpy.SelectFromTypeTree(this)(qualifier, name) } /** left & right */ @@ -666,7 +666,7 @@ object Trees { extends NameTree[T] with DefTree[T] with PatternTree[T] { type ThisTree[-T >: Untyped] = Bind[T] override def envelope: Position = pos union initialPos - def withName(name: Name)(implicit ctx: Context): untpd.Bind = untpd.cpy.Bind(this)(name = name) + def withName(name: Name)(implicit ctx: Context): untpd.Bind = untpd.cpy.Bind(this)(name, body) } /** tree_1 | ... | tree_n */ @@ -1094,24 +1094,24 @@ object Trees { } // Copier methods with default arguments; these demand that the original tree - // is of the same class as the copy - - def Select(tree: Select)(qualifier: Tree = tree.qualifier, name: Name = tree.name): Select = - Select(tree: Tree)(qualifier, name) - def Super(tree: Super)(qual: Tree = tree.qual, mix: TypeName = tree.mix): Super = - Super(tree: Tree)(qual, mix) - def Apply(tree: Apply)(fun: Tree = tree.fun, args: List[Tree] = tree.args): Apply = - Apply(tree: Tree)(fun, args) - def TypeApply(tree: TypeApply)(fun: Tree = tree.fun, args: List[Tree] = tree.args): TypeApply = - TypeApply(tree: Tree)(fun, args) - def Typed(tree: Typed)(expr: Tree = tree.expr, tpt: Tree = tree.tpt): Typed = - Typed(tree: Tree)(expr, tpt) - def NamedArg(tree: NamedArg)(name: Name = tree.name, arg: Tree = tree.arg): NamedArg = - NamedArg(tree: Tree)(name, arg) - def Assign(tree: Assign)(lhs: Tree = tree.lhs, rhs: Tree = tree.rhs): Assign = - Assign(tree: Tree)(lhs, rhs) - def Block(tree: Block)(stats: List[Tree] = tree.stats, expr: Tree = tree.expr): Block = - Block(tree: Tree)(stats, expr) + // is of the same class as the copy. We only include trees with more than 2 elements here. + + //def Select(tree: Select)(qualifier: Tree = tree.qualifier, name: Name = tree.name): Select = + // Select(tree: Tree)(qualifier, name) + //def Super(tree: Super)(qual: Tree = tree.qual, mix: TypeName = tree.mix): Super = + // Super(tree: Tree)(qual, mix) + //def Apply(tree: Apply)(fun: Tree = tree.fun, args: List[Tree] = tree.args): Apply = + // Apply(tree: Tree)(fun, args) + //def TypeApply(tree: TypeApply)(fun: Tree = tree.fun, args: List[Tree] = tree.args): TypeApply = + // TypeApply(tree: Tree)(fun, args) + //def Typed(tree: Typed)(expr: Tree = tree.expr, tpt: Tree = tree.tpt): Typed = + // Typed(tree: Tree)(expr, tpt) + //def NamedArg(tree: NamedArg)(name: Name = tree.name, arg: Tree = tree.arg): NamedArg = + // NamedArg(tree: Tree)(name, arg) + //def Assign(tree: Assign)(lhs: Tree = tree.lhs, rhs: Tree = tree.rhs): Assign = + // Assign(tree: Tree)(lhs, rhs) + //def Block(tree: Block)(stats: List[Tree] = tree.stats, expr: Tree = tree.expr): Block = + // Block(tree: Tree)(stats, expr) def If(tree: If)(cond: Tree = tree.cond, thenp: Tree = tree.thenp, elsep: Tree = tree.elsep): If = If(tree: Tree)(cond, thenp, elsep) def Closure(tree: Closure)(env: List[Tree] = tree.env, meth: Tree = tree.meth, tpt: Tree = tree.tpt): Closure = @@ -1120,20 +1120,20 @@ object Trees { Match(tree: Tree)(selector, cases) def CaseDef(tree: CaseDef)(pat: Tree = tree.pat, guard: Tree = tree.guard, body: Tree = tree.body): CaseDef = CaseDef(tree: Tree)(pat, guard, body) - def Return(tree: Return)(expr: Tree = tree.expr, from: Tree = tree.from): Return = - Return(tree: Tree)(expr, from) + //def Return(tree: Return)(expr: Tree = tree.expr, from: Tree = tree.from): Return = + // Return(tree: Tree)(expr, from) def Try(tree: Try)(expr: Tree = tree.expr, handler: Tree = tree.handler, finalizer: Tree = tree.finalizer): Try = Try(tree: Tree)(expr, handler, finalizer) - def SelectFromTypeTree(tree: SelectFromTypeTree)(qualifier: Tree = tree.qualifier, name: Name = tree.name): - SelectFromTypeTree = SelectFromTypeTree(tree: Tree)(qualifier, name) - def RefinedTypeTree(tree: RefinedTypeTree)(tpt: Tree = tree.tpt, refinements: List[Tree] = tree.refinements): - RefinedTypeTree = RefinedTypeTree(tree: Tree)(tpt, refinements) - def AppliedTypeTree(tree: AppliedTypeTree)(tpt: Tree = tree.tpt, args: List[Tree] = tree.args): AppliedTypeTree = - AppliedTypeTree(tree: Tree)(tpt, args) - def TypeBoundsTree(tree: TypeBoundsTree)(lo: Tree = tree.lo, hi: Tree = tree.hi): TypeBoundsTree = - TypeBoundsTree(tree: Tree)(lo, hi) - def Bind(tree: Bind)(name: Name = tree.name, body: Tree = tree.body): Bind = - Bind(tree: Tree)(name, body) + //def SelectFromTypeTree(tree: SelectFromTypeTree)(qualifier: Tree = tree.qualifier, name: Name = tree.name): + // SelectFromTypeTree = SelectFromTypeTree(tree: Tree)(qualifier, name) + //def RefinedTypeTree(tree: RefinedTypeTree)(tpt: Tree = tree.tpt, refinements: List[Tree] = tree.refinements): + // RefinedTypeTree = RefinedTypeTree(tree: Tree)(tpt, refinements) + //def AppliedTypeTree(tree: AppliedTypeTree)(tpt: Tree = tree.tpt, args: List[Tree] = tree.args): AppliedTypeTree = + // AppliedTypeTree(tree: Tree)(tpt, args) + //def TypeBoundsTree(tree: TypeBoundsTree)(lo: Tree = tree.lo, hi: Tree = tree.hi): TypeBoundsTree = + // TypeBoundsTree(tree: Tree)(lo, hi) + //def Bind(tree: Bind)(name: Name = tree.name, body: Tree = tree.body): Bind = + // Bind(tree: Tree)(name, body) def UnApply(tree: UnApply)(fun: Tree = tree.fun, implicits: List[Tree] = tree.implicits, patterns: List[Tree] = tree.patterns): UnApply = UnApply(tree: Tree)(fun, implicits, patterns) def ValDef(tree: ValDef)(mods: Modifiers = tree.mods, name: TermName = tree.name, tpt: Tree = tree.tpt, rhs: Tree = tree.rhs): ValDef = @@ -1144,12 +1144,12 @@ object Trees { TypeDef(tree: Tree)(mods, name, rhs, tparams) def Template(tree: Template)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, self: ValDef = tree.self, body: List[Tree] = tree.body): Template = Template(tree: Tree)(constr, parents, self, body) - def Import(tree: Import)(expr: Tree = tree.expr, selectors: List[untpd.Tree] = tree.selectors): Import = - Import(tree: Tree)(expr, selectors) - def PackageDef(tree: PackageDef)(pid: RefTree = tree.pid, stats: List[Tree] = tree.stats): PackageDef = - PackageDef(tree: Tree)(pid, stats) - def Annotated(tree: Annotated)(annot: Tree = tree.annot, arg: Tree = tree.arg): Annotated = - Annotated(tree: Tree)(annot, arg) + //def Import(tree: Import)(expr: Tree = tree.expr, selectors: List[untpd.Tree] = tree.selectors): Import = + // Import(tree: Tree)(expr, selectors) + //def PackageDef(tree: PackageDef)(pid: RefTree = tree.pid, stats: List[Tree] = tree.stats): PackageDef = + // PackageDef(tree: Tree)(pid, stats) + //def Annotated(tree: Annotated)(annot: Tree = tree.annot, arg: Tree = tree.arg): Annotated = + // Annotated(tree: Tree)(annot, arg) } abstract class TreeMap(val cpy: TreeCopier = inst.cpy) { diff --git a/src/dotty/tools/dotc/transform/Nullarify.scala b/src/dotty/tools/dotc/transform/Nullarify.scala index 7cca19e5a121..554a51efbe10 100644 --- a/src/dotty/tools/dotc/transform/Nullarify.scala +++ b/src/dotty/tools/dotc/transform/Nullarify.scala @@ -72,7 +72,7 @@ class Nullarify extends MiniPhaseTransform with InfoTransformer { val MethodType(_, formals) = methType(funType, tree.fun) val args1 = tree.args.zipWithConserve(formals)(transformArg) - cpy.Apply(tree)(args = args1) withType nullarify(tree.tpe) + cpy.Apply(tree)(tree.fun, args1) withType nullarify(tree.tpe) } /** Insert () or .apply() if the term refers to something that was converted to a diff --git a/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala b/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala index c713a7d30e71..cf7668079681 100644 --- a/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala +++ b/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala @@ -23,7 +23,7 @@ class UncurryTreeTransform extends MiniPhaseTransform with InfoTransformer { showType ctx.atNextPhase(showType(_)) showType - cpy.Apply(tree)(args = args ++ tree.args) + cpy.Apply(tree)(tree.fun, args ++ tree.args) case _ => tree }} diff --git a/src/dotty/tools/dotc/typer/ReTyper.scala b/src/dotty/tools/dotc/typer/ReTyper.scala index 42128f67f566..dbf353f9efcd 100644 --- a/src/dotty/tools/dotc/typer/ReTyper.scala +++ b/src/dotty/tools/dotc/typer/ReTyper.scala @@ -32,13 +32,13 @@ class ReTyper extends Typer { override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { assert(tree.hasType) val qual1 = typed(tree.qualifier, AnySelectionProto) - untpd.cpy.Select(tree)(qualifier = qual1).withType(tree.typeOpt) + untpd.cpy.Select(tree)(qual1, tree.name).withType(tree.typeOpt) } override def typedSelectFromTypeTree(tree: untpd.SelectFromTypeTree, pt: Type)(implicit ctx: Context): SelectFromTypeTree = { assert(tree.hasType) val qual1 = typed(tree.qualifier, AnySelectionProto) - untpd.cpy.SelectFromTypeTree(tree)(qualifier = qual1).withType(tree.typeOpt) + untpd.cpy.SelectFromTypeTree(tree)(qual1, tree.name).withType(tree.typeOpt) } override def typedLiteral(tree: untpd.Literal)(implicit ctc: Context): Literal = @@ -50,7 +50,7 @@ class ReTyper extends Typer { override def typedBind(tree: untpd.Bind, pt: Type)(implicit ctx: Context): Bind = { assert(tree.hasType) val body1 = typed(tree.body, pt) - untpd.cpy.Bind(tree)(body = body1).withType(tree.typeOpt) + untpd.cpy.Bind(tree)(tree.name, body1).withType(tree.typeOpt) } override def localDummy(cls: ClassSymbol, impl: untpd.Template)(implicit ctx: Context) = impl.symbol From 3ce9d15dcb72e3da55bd14483c9b2b155c70855b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 13 Aug 2014 16:13:39 +0200 Subject: [PATCH 39/44] Drop commented out code. --- src/dotty/tools/dotc/ast/Trees.scala | 35 ---------------------------- 1 file changed, 35 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index b28cb87e6426..e88a3e6449ae 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -1095,23 +1095,6 @@ object Trees { // Copier methods with default arguments; these demand that the original tree // is of the same class as the copy. We only include trees with more than 2 elements here. - - //def Select(tree: Select)(qualifier: Tree = tree.qualifier, name: Name = tree.name): Select = - // Select(tree: Tree)(qualifier, name) - //def Super(tree: Super)(qual: Tree = tree.qual, mix: TypeName = tree.mix): Super = - // Super(tree: Tree)(qual, mix) - //def Apply(tree: Apply)(fun: Tree = tree.fun, args: List[Tree] = tree.args): Apply = - // Apply(tree: Tree)(fun, args) - //def TypeApply(tree: TypeApply)(fun: Tree = tree.fun, args: List[Tree] = tree.args): TypeApply = - // TypeApply(tree: Tree)(fun, args) - //def Typed(tree: Typed)(expr: Tree = tree.expr, tpt: Tree = tree.tpt): Typed = - // Typed(tree: Tree)(expr, tpt) - //def NamedArg(tree: NamedArg)(name: Name = tree.name, arg: Tree = tree.arg): NamedArg = - // NamedArg(tree: Tree)(name, arg) - //def Assign(tree: Assign)(lhs: Tree = tree.lhs, rhs: Tree = tree.rhs): Assign = - // Assign(tree: Tree)(lhs, rhs) - //def Block(tree: Block)(stats: List[Tree] = tree.stats, expr: Tree = tree.expr): Block = - // Block(tree: Tree)(stats, expr) def If(tree: If)(cond: Tree = tree.cond, thenp: Tree = tree.thenp, elsep: Tree = tree.elsep): If = If(tree: Tree)(cond, thenp, elsep) def Closure(tree: Closure)(env: List[Tree] = tree.env, meth: Tree = tree.meth, tpt: Tree = tree.tpt): Closure = @@ -1120,20 +1103,8 @@ object Trees { Match(tree: Tree)(selector, cases) def CaseDef(tree: CaseDef)(pat: Tree = tree.pat, guard: Tree = tree.guard, body: Tree = tree.body): CaseDef = CaseDef(tree: Tree)(pat, guard, body) - //def Return(tree: Return)(expr: Tree = tree.expr, from: Tree = tree.from): Return = - // Return(tree: Tree)(expr, from) def Try(tree: Try)(expr: Tree = tree.expr, handler: Tree = tree.handler, finalizer: Tree = tree.finalizer): Try = Try(tree: Tree)(expr, handler, finalizer) - //def SelectFromTypeTree(tree: SelectFromTypeTree)(qualifier: Tree = tree.qualifier, name: Name = tree.name): - // SelectFromTypeTree = SelectFromTypeTree(tree: Tree)(qualifier, name) - //def RefinedTypeTree(tree: RefinedTypeTree)(tpt: Tree = tree.tpt, refinements: List[Tree] = tree.refinements): - // RefinedTypeTree = RefinedTypeTree(tree: Tree)(tpt, refinements) - //def AppliedTypeTree(tree: AppliedTypeTree)(tpt: Tree = tree.tpt, args: List[Tree] = tree.args): AppliedTypeTree = - // AppliedTypeTree(tree: Tree)(tpt, args) - //def TypeBoundsTree(tree: TypeBoundsTree)(lo: Tree = tree.lo, hi: Tree = tree.hi): TypeBoundsTree = - // TypeBoundsTree(tree: Tree)(lo, hi) - //def Bind(tree: Bind)(name: Name = tree.name, body: Tree = tree.body): Bind = - // Bind(tree: Tree)(name, body) def UnApply(tree: UnApply)(fun: Tree = tree.fun, implicits: List[Tree] = tree.implicits, patterns: List[Tree] = tree.patterns): UnApply = UnApply(tree: Tree)(fun, implicits, patterns) def ValDef(tree: ValDef)(mods: Modifiers = tree.mods, name: TermName = tree.name, tpt: Tree = tree.tpt, rhs: Tree = tree.rhs): ValDef = @@ -1144,12 +1115,6 @@ object Trees { TypeDef(tree: Tree)(mods, name, rhs, tparams) def Template(tree: Template)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, self: ValDef = tree.self, body: List[Tree] = tree.body): Template = Template(tree: Tree)(constr, parents, self, body) - //def Import(tree: Import)(expr: Tree = tree.expr, selectors: List[untpd.Tree] = tree.selectors): Import = - // Import(tree: Tree)(expr, selectors) - //def PackageDef(tree: PackageDef)(pid: RefTree = tree.pid, stats: List[Tree] = tree.stats): PackageDef = - // PackageDef(tree: Tree)(pid, stats) - //def Annotated(tree: Annotated)(annot: Tree = tree.annot, arg: Tree = tree.arg): Annotated = - // Annotated(tree: Tree)(annot, arg) } abstract class TreeMap(val cpy: TreeCopier = inst.cpy) { From 01f24751cae384ed25badd1faa1f93d56bd26070 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 15 Aug 2014 14:20:36 +0200 Subject: [PATCH 40/44] Make typed tree copiers selectively retype nodes. Those nodes that had so far a propagateType method defined on them are automatically retyped on copying. No more manual interventions are needed. --- src/dotty/tools/dotc/ast/Trees.scala | 33 ++++---- src/dotty/tools/dotc/ast/tpd.scala | 82 +++++++++++++++++++ .../tools/dotc/transform/SuperAccessors.scala | 2 +- 3 files changed, 102 insertions(+), 15 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index e88a3e6449ae..241adfa61e7a 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -912,6 +912,13 @@ object Trees { val cpy: TreeCopier + /** A class for copying trees. The copy methods avid creating a new tree + * If all arguments stay the same. + + * Note: Some of the copy methods take a context. + * These are exactly those methods that are overridden in TypedTreeCopier + * so that they selectively retype themselves. Retyping needs a context. + */ abstract class TreeCopier { def postProcess(tree: Tree, copied: untpd.Tree): copied.ThisTree[T] @@ -926,7 +933,7 @@ object Trees { case tree: Ident if (name == tree.name) => tree case _ => finalize(tree, untpd.Ident(name)) } - def Select(tree: Tree)(qualifier: Tree, name: Name): Select = tree match { + def Select(tree: Tree)(qualifier: Tree, name: Name)(implicit ctx: Context): Select = tree match { case tree: SelectWithSig => if ((qualifier eq tree.qualifier) && (name == tree.name)) tree else finalize(tree, new SelectWithSig(qualifier, name, tree.sig)) @@ -957,7 +964,7 @@ object Trees { case tree: New if (tpt eq tree.tpt) => tree case _ => finalize(tree, untpd.New(tpt)) } - def Pair(tree: Tree)(left: Tree, right: Tree): Pair = tree match { + def Pair(tree: Tree)(left: Tree, right: Tree)(implicit ctx: Context): Pair = tree match { case tree: Pair if (left eq tree.left) && (right eq tree.right) => tree case _ => finalize(tree, untpd.Pair(left, right)) } @@ -973,11 +980,11 @@ object Trees { case tree: Assign if (lhs eq tree.lhs) && (rhs eq tree.rhs) => tree case _ => finalize(tree, untpd.Assign(lhs, rhs)) } - def Block(tree: Tree)(stats: List[Tree], expr: Tree): Block = tree match { + def Block(tree: Tree)(stats: List[Tree], expr: Tree)(implicit ctx: Context): Block = tree match { case tree: Block if (stats eq tree.stats) && (expr eq tree.expr) => tree case _ => finalize(tree, untpd.Block(stats, expr)) } - def If(tree: Tree)(cond: Tree, thenp: Tree, elsep: Tree): If = tree match { + def If(tree: Tree)(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If = tree match { case tree: If if (cond eq tree.cond) && (thenp eq tree.thenp) && (elsep eq tree.elsep) => tree case _ => finalize(tree, untpd.If(cond, thenp, elsep)) } @@ -985,11 +992,11 @@ object Trees { case tree: Closure if (env eq tree.env) && (meth eq tree.meth) && (tpt eq tree.tpt) => tree case _ => finalize(tree, untpd.Closure(env, meth, tpt)) } - def Match(tree: Tree)(selector: Tree, cases: List[CaseDef]): Match = tree match { + def Match(tree: Tree)(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match = tree match { case tree: Match if (selector eq tree.selector) && (cases eq tree.cases) => tree case _ => finalize(tree, untpd.Match(selector, cases)) } - def CaseDef(tree: Tree)(pat: Tree, guard: Tree, body: Tree): CaseDef = tree match { + def CaseDef(tree: Tree)(pat: Tree, guard: Tree, body: Tree)(implicit ctx: Context): CaseDef = tree match { case tree: CaseDef if (pat eq tree.pat) && (guard eq tree.guard) && (body eq tree.body) => tree case _ => finalize(tree, untpd.CaseDef(pat, guard, body)) } @@ -997,7 +1004,7 @@ object Trees { case tree: Return if (expr eq tree.expr) && (from eq tree.from) => tree case _ => finalize(tree, untpd.Return(expr, from)) } - def Try(tree: Tree)(expr: Tree, handler: Tree, finalizer: Tree): Try = tree match { + def Try(tree: Tree)(expr: Tree, handler: Tree, finalizer: Tree)(implicit ctx: Context): Try = tree match { case tree: Try if (expr eq tree.expr) && (handler eq tree.handler) && (finalizer eq tree.finalizer) => tree case _ => finalize(tree, untpd.Try(expr, handler, finalizer)) } @@ -1005,7 +1012,7 @@ object Trees { case tree: Throw if (expr eq tree.expr) => tree case _ => finalize(tree, untpd.Throw(expr)) } - def SeqLiteral(tree: Tree)(elems: List[Tree]): SeqLiteral = tree match { + def SeqLiteral(tree: Tree)(elems: List[Tree])(implicit ctx: Context): SeqLiteral = tree match { case tree: JavaSeqLiteral => if (elems eq tree.elems) tree else finalize(tree, new JavaSeqLiteral(elems)) @@ -1084,7 +1091,7 @@ object Trees { case tree: PackageDef if (pid eq tree.pid) && (stats eq tree.stats) => tree case _ => finalize(tree, untpd.PackageDef(pid, stats)) } - def Annotated(tree: Tree)(annot: Tree, arg: Tree): Annotated = tree match { + def Annotated(tree: Tree)(annot: Tree, arg: Tree)(implicit ctx: Context): Annotated = tree match { case tree: Annotated if (annot eq tree.annot) && (arg eq tree.arg) => tree case _ => finalize(tree, untpd.Annotated(annot, arg)) } @@ -1095,15 +1102,13 @@ object Trees { // Copier methods with default arguments; these demand that the original tree // is of the same class as the copy. We only include trees with more than 2 elements here. - def If(tree: If)(cond: Tree = tree.cond, thenp: Tree = tree.thenp, elsep: Tree = tree.elsep): If = + def If(tree: If)(cond: Tree = tree.cond, thenp: Tree = tree.thenp, elsep: Tree = tree.elsep)(implicit ctx: Context): If = If(tree: Tree)(cond, thenp, elsep) def Closure(tree: Closure)(env: List[Tree] = tree.env, meth: Tree = tree.meth, tpt: Tree = tree.tpt): Closure = Closure(tree: Tree)(env, meth, tpt) - def Match(tree: Match)(selector: Tree = tree.selector, cases: List[CaseDef] = tree.cases): Match = - Match(tree: Tree)(selector, cases) - def CaseDef(tree: CaseDef)(pat: Tree = tree.pat, guard: Tree = tree.guard, body: Tree = tree.body): CaseDef = + def CaseDef(tree: CaseDef)(pat: Tree = tree.pat, guard: Tree = tree.guard, body: Tree = tree.body)(implicit ctx: Context): CaseDef = CaseDef(tree: Tree)(pat, guard, body) - def Try(tree: Try)(expr: Tree = tree.expr, handler: Tree = tree.handler, finalizer: Tree = tree.finalizer): Try = + def Try(tree: Try)(expr: Tree = tree.expr, handler: Tree = tree.handler, finalizer: Tree = tree.finalizer)(implicit ctx: Context): Try = Try(tree: Tree)(expr, handler, finalizer) def UnApply(tree: UnApply)(fun: Tree = tree.fun, implicits: List[Tree] = tree.implicits, patterns: List[Tree] = tree.patterns): UnApply = UnApply(tree: Tree)(fun, implicits, patterns) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 1ba3f223a627..eeb5caf72c9a 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -336,6 +336,88 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { class TypedTreeCopier extends TreeCopier { def postProcess(tree: Tree, copied: untpd.Tree): copied.ThisTree[Type] = copied.withTypeUnchecked(tree.tpe) + + override def Pair(tree: Tree)(left: Tree, right: Tree)(implicit ctx: Context): Pair = { + val tree1 = untpd.cpy.Pair(tree)(left, right) + tree match { + case tree: Pair if (left.tpe eq tree.left.tpe) && (right.tpe eq tree.right.tpe) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, left, right) + } + } + + override def Block(tree: Tree)(stats: List[Tree], expr: Tree)(implicit ctx: Context): Block = { + val tree1 = untpd.cpy.Block(tree)(stats, expr) + tree match { + case tree: Block if (expr.tpe eq tree.expr.tpe) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, stats, expr) + } + } + + override def If(tree: Tree)(cond: Tree, thenp: Tree, elsep: Tree)(implicit ctx: Context): If = { + val tree1 = untpd.cpy.If(tree)(cond, thenp, elsep) + tree match { + case tree: If if (thenp.tpe eq tree.thenp.tpe) && (elsep.tpe eq tree.elsep.tpe) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, thenp, elsep) + } + } + + override def Match(tree: Tree)(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match = { + val tree1 = untpd.cpy.Match(tree)(selector, cases) + tree match { + case tree: Match if sameTypes(cases, tree.cases) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, cases) + } + } + + override def CaseDef(tree: Tree)(pat: Tree, guard: Tree, body: Tree)(implicit ctx: Context): CaseDef = { + val tree1 = untpd.cpy.CaseDef(tree)(pat, guard, body) + tree match { + case tree: CaseDef if (body.tpe eq tree.body.tpe) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, body) + } + } + + override def Try(tree: Tree)(expr: Tree, handler: Tree, finalizer: Tree)(implicit ctx: Context): Try = { + val tree1 = untpd.cpy.Try(tree)(expr, handler, finalizer) + tree match { + case tree: Try if (expr.tpe eq tree.expr.tpe) && (handler.tpe eq tree.handler.tpe) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, expr, handler) + } + } + + override def SeqLiteral(tree: Tree)(elems: List[Tree])(implicit ctx: Context): SeqLiteral = { + val tree1 = untpd.cpy.SeqLiteral(tree)(elems) + tree match { + case tree: SeqLiteral if sameTypes(elems, tree.elems) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, elems) + } + } + + override def Annotated(tree: Tree)(annot: Tree, arg: Tree)(implicit ctx: Context): Annotated = { + val tree1 = untpd.cpy.Annotated(tree)(annot, arg) + tree match { + case tree: Annotated if (arg.tpe eq tree.arg.tpe) && (annot eq tree.annot) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, annot, arg) + } + } + + override def Select(tree: Tree)(qualifier: Tree, name: Name)(implicit ctx: Context): Select = { + val tree1 = untpd.cpy.Select(tree)(qualifier, name) + tree match { + case tree: Select if (qualifier.tpe eq tree.qualifier.tpe) => tree1.withTypeUnchecked(tree.tpe) + case _ => tree.tpe match { + case tpe: NamedType => tree1.withType(tpe.derivedSelect(qualifier.tpe)) + case _ => tree1.withTypeUnchecked(tree.tpe) + } + } + } + + override def If(tree: If)(cond: Tree = tree.cond, thenp: Tree = tree.thenp, elsep: Tree = tree.elsep)(implicit ctx: Context): If = + If(tree: Tree)(cond, thenp, elsep) + override def CaseDef(tree: CaseDef)(pat: Tree = tree.pat, guard: Tree = tree.guard, body: Tree = tree.body)(implicit ctx: Context): CaseDef = + CaseDef(tree: Tree)(pat, guard, body) + override def Try(tree: Try)(expr: Tree = tree.expr, handler: Tree = tree.handler, finalizer: Tree = tree.finalizer)(implicit ctx: Context): Try = + Try(tree: Tree)(expr, handler, finalizer) } implicit class TreeOps[ThisTree <: tpd.Tree](val tree: ThisTree) extends AnyVal { diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index f0d25d9c2a80..cb8d42b2f6bc 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -215,7 +215,7 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this ctx.debuglog("Adding protected accessor for " + tree) transform(makeAccessor(sel, targs)) } - else if (goToSuper) super.transform(tree) + else if (goToSuper) super.transform(tree)(ctx.withPhase(thisTransformer.next)) else tree try tree match { From 9326a00ab1f7838cfe4f0ad07cc7193d7934fe67 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 15 Aug 2014 14:31:27 +0200 Subject: [PATCH 41/44] Delete propagateType and RetypingTreeMap Their functionality is now rolled into TypedTreeCopier. --- src/dotty/tools/dotc/ast/tpd.scala | 120 +------------------ src/dotty/tools/dotc/transform/TailRec.scala | 19 ++- 2 files changed, 10 insertions(+), 129 deletions(-) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index eeb5caf72c9a..25541bf94ee3 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -508,68 +508,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def tpes: List[Type] = xs map (_.tpe) } - /** RetypingTreeMap is a TreeMap that is able to propagate type changes. - * - * This is required when types can change during transformation, - * for example if `Block(stats, expr)` is being transformed - * and type of `expr` changes from `TypeRef(prefix, name)` to `TypeRef(newPrefix, name)` with different prefix, t - * type of enclosing Block should also change, otherwise the whole tree would not be type-correct anymore. - * see `propagateType` methods for propagation rulles. - * - * TreeMap does not include such logic as it assumes that types of threes do not change during transformation. - */ - class RetypingTreeMap extends TreeMap { - - override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match { - case tree@Select(qualifier, name) => - val tree1 = cpy.Select(tree)(transform(qualifier), name) - propagateType(tree, tree1) - case tree@Pair(left, right) => - val left1 = transform(left) - val right1 = transform(right) - val tree1 = cpy.Pair(tree)(left1, right1) - propagateType(tree, tree1) - case tree@Block(stats, expr) => - val stats1 = transform(stats) - val expr1 = transform(expr) - val tree1 = cpy.Block(tree)(stats1, expr1) - propagateType(tree, tree1) - case tree@If(cond, thenp, elsep) => - val cond1 = transform(cond) - val thenp1 = transform(thenp) - val elsep1 = transform(elsep) - val tree1 = cpy.If(tree)(cond1, thenp1, elsep1) - propagateType(tree, tree1) - case tree@Match(selector, cases) => - val selector1 = transform(selector) - val cases1 = transformSub(cases) - val tree1 = cpy.Match(tree)(selector1, cases1) - propagateType(tree, tree1) - case tree@CaseDef(pat, guard, body) => - val pat1 = transform(pat) - val guard1 = transform(guard) - val body1 = transform(body) - val tree1 = cpy.CaseDef(tree)(pat1, guard1, body1) - propagateType(tree, tree1) - case tree@Try(block, handler, finalizer) => - val expr1 = transform(block) - val handler1 = transform(handler) - val finalizer1 = transform(finalizer) - val tree1 = cpy.Try(tree)(expr1, handler1, finalizer1) - propagateType(tree, tree1) - case tree@SeqLiteral(elems) => - val elems1 = transform(elems) - val tree1 = cpy.SeqLiteral(tree)(elems1) - propagateType(tree, tree1) - case tree@Annotated(annot, arg) => - val annot1 = transform(annot) - val arg1 = transform(arg) - val tree1 = cpy.Annotated(tree)(annot1, arg1) - propagateType(tree, tree1) - case _ => super.transform(tree) - } - } - /** A map that applies three functions together to a tree and makes sure * they are coordinated so that the result is well-typed. The functions are * @param typeMap A function from Type to type that gets applied to the @@ -582,7 +520,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { final class TreeTypeMap( val typeMap: Type => Type = IdentityTypeMap, val ownerMap: Symbol => Symbol = identity _, - val treeMap: Tree => Tree = identity _)(implicit ctx: Context) extends RetypingTreeMap { + val treeMap: Tree => Tree = identity _)(implicit ctx: Context) extends TreeMap { override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = { val tree1 = treeMap(tree) @@ -594,15 +532,13 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case blk @ Block(stats, expr) => val (tmap1, stats1) = transformDefs(stats) val expr1 = tmap1.transform(expr) - val tree1 = cpy.Block(blk)(stats1, expr1) - propagateType(blk, tree1) + cpy.Block(blk)(stats1, expr1) case cdef @ CaseDef(pat, guard, rhs) => val tmap = withMappedSyms(patVars(pat)) val pat1 = tmap.transform(pat) val guard1 = tmap.transform(guard) val rhs1 = tmap.transform(rhs) - val tree1 = cpy.CaseDef(tree)(pat1, guard1, rhs1) - propagateType(cdef, tree1) + cpy.CaseDef(tree)(pat1, guard1, rhs1) case tree1 => super.transform(tree1) } @@ -664,56 +600,6 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { acc(Nil, tree) } - def propagateType(origTree: Pair, newTree: Pair)(implicit ctx: Context) = { - if ((newTree eq origTree) || - ((newTree.left.tpe eq origTree.left.tpe) && (newTree.right.tpe eq origTree.right.tpe))) newTree - else ta.assignType(newTree, newTree.left, newTree.right) - } - - def propagateType(origTree: Block, newTree: Block)(implicit ctx: Context) = { - if ((newTree eq origTree) || (newTree.expr.tpe eq origTree.expr.tpe)) newTree - else ta.assignType(newTree, newTree.stats, newTree.expr) - } - - def propagateType(origTree: If, newTree: If)(implicit ctx: Context) = { - if ((newTree eq origTree) || - ((newTree.thenp.tpe eq origTree.thenp.tpe) && (newTree.elsep.tpe eq origTree.elsep.tpe))) newTree - else ta.assignType(newTree, newTree.thenp, newTree.elsep) - } - - def propagateType(origTree: Match, newTree: Match)(implicit ctx: Context) = { - if ((newTree eq origTree) || sameTypes(newTree.cases, origTree.cases)) newTree - else ta.assignType(newTree, newTree.cases) - } - - def propagateType(origTree: CaseDef, newTree: CaseDef)(implicit ctx: Context) = { - if ((newTree eq newTree) || (newTree.body.tpe eq origTree.body.tpe)) newTree - else ta.assignType(newTree, newTree.body) - } - - def propagateType(origTree: Try, newTree: Try)(implicit ctx: Context) = { - if ((newTree eq origTree) || - ((newTree.expr.tpe eq origTree.expr.tpe) && (newTree.handler.tpe eq origTree.handler.tpe))) newTree - else ta.assignType(newTree, newTree.expr, newTree.handler) - } - - def propagateType(origTree: SeqLiteral, newTree: SeqLiteral)(implicit ctx: Context) = { - if ((newTree eq origTree) || sameTypes(newTree.elems, origTree.elems)) newTree - else ta.assignType(newTree, newTree.elems) - } - - def propagateType(origTree: Annotated, newTree: Annotated)(implicit ctx: Context) = { - if ((newTree eq origTree) || ((newTree.arg.tpe eq origTree.arg.tpe) && (newTree.annot eq origTree.annot))) newTree - else ta.assignType(newTree, newTree.annot, newTree.arg) - } - - def propagateType(origTree: Select, newTree: Select)(implicit ctx: Context) = { - if ((origTree eq newTree) || (origTree.qualifier.tpe eq newTree.qualifier.tpe)) newTree - else newTree.tpe match { - case tpe: NamedType => newTree.withType(tpe.derivedSelect(newTree.qualifier.tpe)) - case _ => newTree - } - } // convert a numeric with a toXXX method def primitiveConversion(tree: Tree, numericCls: Symbol)(implicit ctx: Context): Tree = { val mname = ("to" + numericCls.name).toTermName diff --git a/src/dotty/tools/dotc/transform/TailRec.scala b/src/dotty/tools/dotc/transform/TailRec.scala index 359859cec2ab..6aabdb20e3f0 100644 --- a/src/dotty/tools/dotc/transform/TailRec.scala +++ b/src/dotty/tools/dotc/transform/TailRec.scala @@ -126,7 +126,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete } - class TailRecElimination(method: Symbol, enclosingClass: Symbol, thisType: Type, isMandatory: Boolean, label: Symbol) extends tpd.RetypingTreeMap { + class TailRecElimination(method: Symbol, enclosingClass: Symbol, thisType: Type, isMandatory: Boolean, label: Symbol) extends tpd.TreeMap { import dotty.tools.dotc.ast.tpd._ @@ -257,34 +257,29 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete val res: Tree = tree match { case tree@Block(stats, expr) => - val tree1 = tpd.cpy.Block(tree)( + tpd.cpy.Block(tree)( noTailTransforms(stats), transform(expr) ) - propagateType(tree, tree1) case tree@CaseDef(_, _, body) => - val tree1 = cpy.CaseDef(tree)(body = transform(body)) - propagateType(tree, tree1) + cpy.CaseDef(tree)(body = transform(body)) case tree@If(cond, thenp, elsep) => - val tree1 = tpd.cpy.If(tree)( + tpd.cpy.If(tree)( noTailTransform(cond), transform(thenp), transform(elsep) ) - propagateType(tree, tree1) case tree@Match(selector, cases) => - val tree1 = tpd.cpy.Match(tree)( + tpd.cpy.Match(tree)( noTailTransform(selector), transformSub(cases) ) - propagateType(tree, tree1) case tree: Try => - val tree1 = rewriteTry(tree) - propagateType(tree, tree1) + rewriteTry(tree) case Apply(fun, args) if fun.symbol == defn.Boolean_|| || fun.symbol == defn.Boolean_&& => tpd.cpy.Apply(tree)(fun, transform(args)) @@ -299,7 +294,7 @@ class TailRec extends MiniPhaseTransform with DenotTransformer with FullParamete case tree: Select => val sym = tree.symbol if (sym == method && ctx.tailPos) rewriteApply(tree, sym) - else propagateType(tree, tpd.cpy.Select(tree)(noTailTransform(tree.qualifier), tree.name)) + else tpd.cpy.Select(tree)(noTailTransform(tree.qualifier), tree.name) case ValDef(_, _, _, _) | EmptyTree | Super(_, _) | This(_) | Literal(_) | TypeTree(_) | DefDef(_, _, _, _, _, _) | TypeDef(_, _, _) => From 53ab5f01c81344ae88c9e0d5bf94c08b92425eec Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 15 Aug 2014 19:09:12 +0200 Subject: [PATCH 42/44] Add hook for phase when a TreeTransform is run. Achieved by overridanle method treeTransformPhase in TreeTransform. This is currently by default the current phase. We should migrate it to the one after current phase. --- .../tools/dotc/transform/TreeTransform.scala | 68 ++++++++++--------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/src/dotty/tools/dotc/transform/TreeTransform.scala b/src/dotty/tools/dotc/transform/TreeTransform.scala index 4ac6d9aade4c..22f785163e7e 100644 --- a/src/dotty/tools/dotc/transform/TreeTransform.scala +++ b/src/dotty/tools/dotc/transform/TreeTransform.scala @@ -55,6 +55,8 @@ object TreeTransforms { def phase: MiniPhase + def treeTransformPhase: Phase = phase + /** id of this treeTransform in group */ var idx: Int = _ @@ -461,7 +463,7 @@ object TreeTransforms { var allDone = i < l while (i < l) { val oldTransform = result(i) - val newTransform = mutator(oldTransform, tree, ctx.withPhase(oldTransform.phase)) + val newTransform = mutator(oldTransform, tree, ctx.withPhase(oldTransform.treeTransformPhase)) allDone = allDone && (newTransform eq NoTransform) if (!(oldTransform eq newTransform)) { if (!transformersCopied) result = result.clone() @@ -526,7 +528,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.phase), info) match { + trans.transformIdent(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Ident => goIdent(t, info.nx.nxTransIdent(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -537,7 +539,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.phase), info) match { + trans.transformSelect(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Select => goSelect(t, info.nx.nxTransSelect(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -548,7 +550,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.phase), info) match { + trans.transformThis(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: This => goThis(t, info.nx.nxTransThis(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -559,7 +561,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.phase), info) match { + trans.transformSuper(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Super => goSuper(t, info.nx.nxTransSuper(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -570,7 +572,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.phase), info) match { + trans.transformApply(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Apply => goApply(t, info.nx.nxTransApply(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -581,7 +583,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.phase), info) match { + trans.transformTypeApply(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: TypeApply => goTypeApply(t, info.nx.nxTransTypeApply(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -592,7 +594,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.phase), info) match { + trans.transformNew(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: New => goNew(t, info.nx.nxTransNew(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -603,7 +605,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.phase), info) match { + trans.transformPair(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Pair => goPair(t, info.nx.nxTransPair(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -614,7 +616,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.phase), info) match { + trans.transformTyped(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Typed => goTyped(t, info.nx.nxTransTyped(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -625,7 +627,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.phase), info) match { + trans.transformAssign(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Assign => goAssign(t, info.nx.nxTransAssign(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -636,7 +638,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.phase), info) match { + trans.transformLiteral(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Literal => goLiteral(t, info.nx.nxTransLiteral(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -647,7 +649,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.phase), info) match { + trans.transformBlock(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Block => goBlock(t, info.nx.nxTransBlock(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -658,7 +660,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.phase), info) match { + trans.transformIf(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: If => goIf(t, info.nx.nxTransIf(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -669,7 +671,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.phase), info) match { + trans.transformClosure(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Closure => goClosure(t, info.nx.nxTransClosure(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -680,7 +682,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.phase), info) match { + trans.transformMatch(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Match => goMatch(t, info.nx.nxTransMatch(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -691,7 +693,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.phase), info) match { + trans.transformCaseDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: CaseDef => goCaseDef(t, info.nx.nxTransCaseDef(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -702,7 +704,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.phase), info) match { + trans.transformReturn(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Return => goReturn(t, info.nx.nxTransReturn(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -713,7 +715,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.phase), info) match { + trans.transformTry(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Try => goTry(t, info.nx.nxTransTry(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -724,7 +726,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.phase), info) match { + trans.transformThrow(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Throw => goThrow(t, info.nx.nxTransThrow(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -735,7 +737,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.phase), info) match { + trans.transformSeqLiteral(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: SeqLiteral => goSeqLiteral(t, info.nx.nxTransSeqLiteral(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -746,7 +748,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.phase), info) match { + trans.transformTypeTree(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: TypeTree => goTypeTree(t, info.nx.nxTransTypeTree(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -757,7 +759,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.phase), info) match { + trans.transformSelectFromTypeTree(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: SelectFromTypeTree => goSelectFromTypeTree(t, info.nx.nxTransSelectFromTypeTree(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -768,7 +770,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.phase), info) match { + trans.transformBind(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Bind => goBind(t, info.nx.nxTransBind(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -779,7 +781,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.phase), info) match { + trans.transformAlternative(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Alternative => goAlternative(t, info.nx.nxTransAlternative(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -790,7 +792,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.phase), info) match { + trans.transformValDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: ValDef => goValDef(t, info.nx.nxTransValDef(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -801,7 +803,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.phase), info) match { + trans.transformDefDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: DefDef => goDefDef(t, info.nx.nxTransDefDef(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -812,7 +814,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.phase), info) match { + trans.transformUnApply(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: UnApply => goUnApply(t, info.nx.nxTransUnApply(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -823,7 +825,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.phase), info) match { + trans.transformTypeDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: TypeDef => goTypeDef(t, info.nx.nxTransTypeDef(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -834,7 +836,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.phase), info) match { + trans.transformTemplate(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: Template => goTemplate(t, info.nx.nxTransTemplate(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -845,7 +847,7 @@ 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.phase), info) match { + trans.transformPackageDef(tree)(ctx.withPhase(trans.treeTransformPhase), info) match { case t: PackageDef => goPackageDef(t, info.nx.nxTransPackageDef(cur + 1)) case t => transformSingle(t, cur + 1) } @@ -855,7 +857,7 @@ object TreeTransforms { 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) + val t = trans.transformOther(tree)(ctx.withPhase(trans.treeTransformPhase), info) transformSingle(t, cur + 1) } else tree } @@ -1167,7 +1169,7 @@ object TreeTransforms { 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 period - val pctx = ctx.withPhase(info.transformers(cur).phase) + val pctx = ctx.withPhase(info.transformers(cur).treeTransformPhase) tree match { //split one big match into 2 smaller ones case tree: NameTree => transformNamed(tree, info, cur)(pctx) From c54cd3e0503144f362ecb000109b75a0a53b3165 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 15 Aug 2014 19:11:45 +0200 Subject: [PATCH 43/44] Extend retyping to more copy methods. We now retype basically everything except leave nodes and definitions. This provides for more robust tree copying and transformation. It also flushed out errors in SuperAccessors (fixed by a hack, awaiting systematic phase change there) and UnCurryTreeTransform. Uncurry is disabled for now, will be fixed shortly. --- src/dotty/tools/dotc/Compiler.scala | 6 +- src/dotty/tools/dotc/ast/Trees.scala | 24 +++---- src/dotty/tools/dotc/ast/tpd.scala | 69 ++++++++++++++++--- .../tools/dotc/transform/SuperAccessors.scala | 4 +- 4 files changed, 76 insertions(+), 27 deletions(-) diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 09e58d186de4..fffcfd8f898b 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -32,9 +32,9 @@ class Compiler { new TypeTestsCasts, new InterceptedMethods, new Literalize), - List(new Erasure), - List(new UncurryTreeTransform - /* , new Constructors */) + List(new Erasure)//, + //List(new UncurryTreeTransform + // /* , new Constructors */) ) var runId = 1 diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index 241adfa61e7a..1c915ca43abb 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -914,7 +914,7 @@ object Trees { /** A class for copying trees. The copy methods avid creating a new tree * If all arguments stay the same. - + * * Note: Some of the copy methods take a context. * These are exactly those methods that are overridden in TypedTreeCopier * so that they selectively retype themselves. Retyping needs a context. @@ -948,19 +948,19 @@ object Trees { case tree: Super if (qual eq tree.qual) && (mix == tree.mix) => tree case _ => finalize(tree, untpd.Super(qual, mix)) } - def Apply(tree: Tree)(fun: Tree, args: List[Tree]): Apply = tree match { + def Apply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): Apply = tree match { case tree: Apply if (fun eq tree.fun) && (args eq tree.args) => tree case _ => finalize(tree, untpd.Apply(fun, args)) } - def TypeApply(tree: Tree)(fun: Tree, args: List[Tree]): TypeApply = tree match { + def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = tree match { case tree: TypeApply if (fun eq tree.fun) && (args eq tree.args) => tree case _ => finalize(tree, untpd.TypeApply(fun, args)) } - def Literal(tree: Tree)(const: Constant): Literal = tree match { + def Literal(tree: Tree)(const: Constant)(implicit ctx: Context): Literal = tree match { case tree: Literal if (const == tree.const) => tree case _ => finalize(tree, untpd.Literal(const)) } - def New(tree: Tree)(tpt: Tree): New = tree match { + def New(tree: Tree)(tpt: Tree)(implicit ctx: Context): New = tree match { case tree: New if (tpt eq tree.tpt) => tree case _ => finalize(tree, untpd.New(tpt)) } @@ -968,15 +968,15 @@ object Trees { case tree: Pair if (left eq tree.left) && (right eq tree.right) => tree case _ => finalize(tree, untpd.Pair(left, right)) } - def Typed(tree: Tree)(expr: Tree, tpt: Tree): Typed = tree match { + def Typed(tree: Tree)(expr: Tree, tpt: Tree)(implicit ctx: Context): Typed = tree match { case tree: Typed if (expr eq tree.expr) && (tpt eq tree.tpt) => tree case _ => finalize(tree, untpd.Typed(expr, tpt)) } - def NamedArg(tree: Tree)(name: Name, arg: Tree): NamedArg = tree match { + def NamedArg(tree: Tree)(name: Name, arg: Tree)(implicit ctx: Context): NamedArg = tree match { case tree: NamedArg if (name == tree.name) && (arg eq tree.arg) => tree case _ => finalize(tree, untpd.NamedArg(name, arg)) } - def Assign(tree: Tree)(lhs: Tree, rhs: Tree): Assign = tree match { + def Assign(tree: Tree)(lhs: Tree, rhs: Tree)(implicit ctx: Context): Assign = tree match { case tree: Assign if (lhs eq tree.lhs) && (rhs eq tree.rhs) => tree case _ => finalize(tree, untpd.Assign(lhs, rhs)) } @@ -988,7 +988,7 @@ object Trees { case tree: If if (cond eq tree.cond) && (thenp eq tree.thenp) && (elsep eq tree.elsep) => tree case _ => finalize(tree, untpd.If(cond, thenp, elsep)) } - def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree): Closure = tree match { + def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure = tree match { case tree: Closure if (env eq tree.env) && (meth eq tree.meth) && (tpt eq tree.tpt) => tree case _ => finalize(tree, untpd.Closure(env, meth, tpt)) } @@ -1000,7 +1000,7 @@ object Trees { case tree: CaseDef if (pat eq tree.pat) && (guard eq tree.guard) && (body eq tree.body) => tree case _ => finalize(tree, untpd.CaseDef(pat, guard, body)) } - def Return(tree: Tree)(expr: Tree, from: Tree): Return = tree match { + def Return(tree: Tree)(expr: Tree, from: Tree)(implicit ctx: Context): Return = tree match { case tree: Return if (expr eq tree.expr) && (from eq tree.from) => tree case _ => finalize(tree, untpd.Return(expr, from)) } @@ -1008,7 +1008,7 @@ object Trees { case tree: Try if (expr eq tree.expr) && (handler eq tree.handler) && (finalizer eq tree.finalizer) => tree case _ => finalize(tree, untpd.Try(expr, handler, finalizer)) } - def Throw(tree: Tree)(expr: Tree): Throw = tree match { + def Throw(tree: Tree)(expr: Tree)(implicit ctx: Context): Throw = tree match { case tree: Throw if (expr eq tree.expr) => tree case _ => finalize(tree, untpd.Throw(expr)) } @@ -1104,7 +1104,7 @@ object Trees { // is of the same class as the copy. We only include trees with more than 2 elements here. def If(tree: If)(cond: Tree = tree.cond, thenp: Tree = tree.thenp, elsep: Tree = tree.elsep)(implicit ctx: Context): If = If(tree: Tree)(cond, thenp, elsep) - def Closure(tree: Closure)(env: List[Tree] = tree.env, meth: Tree = tree.meth, tpt: Tree = tree.tpt): Closure = + def Closure(tree: Closure)(env: List[Tree] = tree.env, meth: Tree = tree.meth, tpt: Tree = tree.tpt)(implicit ctx: Context): Closure = Closure(tree: Tree)(env, meth, tpt) def CaseDef(tree: CaseDef)(pat: Tree = tree.pat, guard: Tree = tree.guard, body: Tree = tree.body)(implicit ctx: Context): CaseDef = CaseDef(tree: Tree)(pat, guard, body) diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index 25541bf94ee3..b1e4a0cd39fb 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -337,6 +337,39 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def postProcess(tree: Tree, copied: untpd.Tree): copied.ThisTree[Type] = copied.withTypeUnchecked(tree.tpe) + override def Select(tree: Tree)(qualifier: Tree, name: Name)(implicit ctx: Context): Select = { + val tree1 = untpd.cpy.Select(tree)(qualifier, name) + tree match { + case tree: Select if (qualifier.tpe eq tree.qualifier.tpe) => tree1.withTypeUnchecked(tree.tpe) + case _ => tree.tpe match { + case tpe: NamedType => tree1.withType(tpe.derivedSelect(qualifier.tpe)) + case _ => tree1.withTypeUnchecked(tree.tpe) + } + } + } + + override def Apply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): Apply = { + val tree1 = untpd.cpy.Apply(tree)(fun, args) + tree match { + case tree: Apply if (fun.tpe.widen eq tree.fun.tpe.widen) && sameTypes(args, tree.args) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, fun, args) + } + } + + override def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = { + val tree1 = untpd.cpy.TypeApply(tree)(fun, args) + tree match { + case tree: TypeApply if (fun.tpe.widen eq tree.fun.tpe.widen) && sameTypes(args, tree.args) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, fun, args) + } + } + + override def Literal(tree: Tree)(const: Constant)(implicit ctx: Context): Literal = + ta.assignType(untpd.cpy.Literal(tree)(const)) + + override def New(tree: Tree)(tpt: Tree)(implicit ctx: Context): New = + ta.assignType(untpd.cpy.New(tree)(tpt), tpt) + override def Pair(tree: Tree)(left: Tree, right: Tree)(implicit ctx: Context): Pair = { val tree1 = untpd.cpy.Pair(tree)(left, right) tree match { @@ -345,6 +378,15 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } } + override def Typed(tree: Tree)(expr: Tree, tpt: Tree)(implicit ctx: Context): Typed = + ta.assignType(untpd.cpy.Typed(tree)(expr, tpt), tpt) + + override def NamedArg(tree: Tree)(name: Name, arg: Tree)(implicit ctx: Context): NamedArg = + ta.assignType(untpd.cpy.NamedArg(tree)(name, arg), arg) + + override def Assign(tree: Tree)(lhs: Tree, rhs: Tree)(implicit ctx: Context): Assign = + ta.assignType(untpd.cpy.Assign(tree)(lhs, rhs)) + override def Block(tree: Tree)(stats: List[Tree], expr: Tree)(implicit ctx: Context): Block = { val tree1 = untpd.cpy.Block(tree)(stats, expr) tree match { @@ -361,6 +403,14 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } } + override def Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure = { + val tree1 = untpd.cpy.Closure(tree)(env, meth, tpt) + tree match { + case tree: Closure if (meth.tpe.widen eq tree.meth.tpe.widen) && (tpt eq tree.tpt) => tree1.withTypeUnchecked(tree.tpe) + case _ => ta.assignType(tree1, meth, tpt) + } + } + override def Match(tree: Tree)(selector: Tree, cases: List[CaseDef])(implicit ctx: Context): Match = { val tree1 = untpd.cpy.Match(tree)(selector, cases) tree match { @@ -377,6 +427,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } } + override def Return(tree: Tree)(expr: Tree, from: Tree)(implicit ctx: Context): Return = + ta.assignType(untpd.cpy.Return(tree)(expr, from)) + override def Try(tree: Tree)(expr: Tree, handler: Tree, finalizer: Tree)(implicit ctx: Context): Try = { val tree1 = untpd.cpy.Try(tree)(expr, handler, finalizer) tree match { @@ -385,6 +438,9 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } } + override def Throw(tree: Tree)(expr: Tree)(implicit ctx: Context): Throw = + ta.assignType(untpd.cpy.Throw(tree)(expr)) + override def SeqLiteral(tree: Tree)(elems: List[Tree])(implicit ctx: Context): SeqLiteral = { val tree1 = untpd.cpy.SeqLiteral(tree)(elems) tree match { @@ -401,19 +457,10 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { } } - override def Select(tree: Tree)(qualifier: Tree, name: Name)(implicit ctx: Context): Select = { - val tree1 = untpd.cpy.Select(tree)(qualifier, name) - tree match { - case tree: Select if (qualifier.tpe eq tree.qualifier.tpe) => tree1.withTypeUnchecked(tree.tpe) - case _ => tree.tpe match { - case tpe: NamedType => tree1.withType(tpe.derivedSelect(qualifier.tpe)) - case _ => tree1.withTypeUnchecked(tree.tpe) - } - } - } - override def If(tree: If)(cond: Tree = tree.cond, thenp: Tree = tree.thenp, elsep: Tree = tree.elsep)(implicit ctx: Context): If = If(tree: Tree)(cond, thenp, elsep) + override def Closure(tree: Closure)(env: List[Tree] = tree.env, meth: Tree = tree.meth, tpt: Tree = tree.tpt)(implicit ctx: Context): Closure = + Closure(tree: Tree)(env, meth, tpt) override def CaseDef(tree: CaseDef)(pat: Tree = tree.pat, guard: Tree = tree.guard, body: Tree = tree.body)(implicit ctx: Context): CaseDef = CaseDef(tree: Tree)(pat, guard, body) override def Try(tree: Try)(expr: Tree = tree.expr, handler: Tree = tree.handler, finalizer: Tree = tree.finalizer)(implicit ctx: Context): Try = diff --git a/src/dotty/tools/dotc/transform/SuperAccessors.scala b/src/dotty/tools/dotc/transform/SuperAccessors.scala index cb8d42b2f6bc..510e0abf0bc0 100644 --- a/src/dotty/tools/dotc/transform/SuperAccessors.scala +++ b/src/dotty/tools/dotc/transform/SuperAccessors.scala @@ -391,7 +391,9 @@ class SuperAccessors extends MacroTransform with IdentityDenotTransformer { this case Apply(fn, args) => val MethodType(_, formals) = fn.tpe.widen - cpy.Apply(tree)(transform(fn), transformArgs(formals, args)) + ctx.atPhase(thisTransformer.next) { implicit ctx => + cpy.Apply(tree)(transform(fn), transformArgs(formals, args)) + } case _ => super.transform(tree) From 7d414eb69b28fa0f6855168aa7afe43a75b3f23e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 16 Aug 2014 11:55:57 +0200 Subject: [PATCH 44/44] Roll Uncurry into Erasure Making cpy recompute types uncovered errors in uncurry. In a nutshell, the intermediate Apply nodes of a curried function were ill-typed, which caused errors produced by TypeAssigner. These nodes were eliminated down the road, but the errors are already issued. I did not find a good way to treat uncurry as a treetransform. Since it is rather trivial, it did not seem warranted to make it a full transformer either. So in the end the uncurry functionality became part of erasure. --- src/dotty/tools/dotc/Compiler.scala | 4 +- .../tools/dotc/core/transform/Erasure.scala | 12 +++-- src/dotty/tools/dotc/transform/Erasure.scala | 27 ++++++++--- .../dotc/transform/UncurryTreeTransform.scala | 46 ------------------- 4 files changed, 31 insertions(+), 58 deletions(-) delete mode 100644 src/dotty/tools/dotc/transform/UncurryTreeTransform.scala diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index fffcfd8f898b..cae8c5782bbf 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -32,9 +32,7 @@ class Compiler { new TypeTestsCasts, new InterceptedMethods, new Literalize), - List(new Erasure)//, - //List(new UncurryTreeTransform - // /* , new Constructors */) + List(new Erasure) ) var runId = 1 diff --git a/src/dotty/tools/dotc/core/transform/Erasure.scala b/src/dotty/tools/dotc/core/transform/Erasure.scala index 41254c9826d8..19662d22af0a 100644 --- a/src/dotty/tools/dotc/core/transform/Erasure.scala +++ b/src/dotty/tools/dotc/core/transform/Erasure.scala @@ -107,7 +107,8 @@ class Erasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcard * - For T1 & T2, erasure(T1) (??) * - For T1 | T2, the first base class in the linearization of T which is also a base class of T2 * - For a method type (Fs)scala.Unit, (|Fs|)scala.Unit. - * - For any other method type (Fs)T, (|Fs|)|T|. + * - For any other uncurried method type (Fs)T, (|Fs|)|T|. + * - For a curried method type (Fs1)(Fs2)T, (|Fs1|,Es2)ET where (Es2)ET = |(Fs2)T|. * - For a polymorphic type, the erasure of its result type. * - For the class info type of java.lang.Object, the same type without any parents. * - For a class info type of a value class, the same type without any parents. @@ -140,8 +141,13 @@ class Erasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcard this(tp.baseTypeRef(lubClass(tp1, tp2))) case tp: MethodType => val paramErasure = erasureFn(tp.isJava, isSemi, isConstructor, wildcardOK)(_) - tp.derivedMethodType( - tp.paramNames, tp.paramTypes.mapConserve(paramErasure), eraseResult(tp.resultType)) + val formals = tp.paramTypes.mapConserve(paramErasure) + eraseResult(tp.resultType) match { + case rt: MethodType => + tp.derivedMethodType(tp.paramNames ++ rt.paramNames, formals ++ rt.paramTypes, rt.resultType) + case rt => + tp.derivedMethodType(tp.paramNames, formals, rt) + } case tp: PolyType => this(tp.resultType) case tp @ ClassInfo(pre, cls, classParents, decls, _) => diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index 6b2a5a676a95..f02846735b11 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -264,13 +264,28 @@ object Erasure { override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { val Apply(fun, args) = tree - val fun1 = typedExpr(fun, WildcardType) - fun1.tpe.widen match { - case mt: MethodType => - val args1 = args.zipWithConserve(mt.paramTypes)(typedExpr) - untpd.cpy.Apply(tree)(fun1, args1) withType mt.resultType + fun match { + case fun: Apply => + typedApply(fun, pt)(ctx.fresh.setTree(tree)) case _ => - throw new MatchError(i"tree $tree has uxpected type of function ${fun1.tpe.widen}, was ${fun.typeOpt.widen}") + def nextOuter(ctx: Context): Context = + if (ctx.outer.tree eq tree) nextOuter(ctx.outer) else ctx.outer + def contextArgs(tree: untpd.Apply)(implicit ctx: Context): List[untpd.Tree] = + ctx.tree match { + case enclApp @ Apply(enclFun, enclArgs) if enclFun eq tree => + enclArgs ++ contextArgs(enclApp)(nextOuter(ctx)) + case _ => + Nil + } + val allArgs = args ++ contextArgs(tree) + val fun1 = typedExpr(fun, WildcardType) + fun1.tpe.widen match { + case mt: MethodType => + val allArgs1 = allArgs.zipWithConserve(mt.paramTypes)(typedExpr) + untpd.cpy.Apply(tree)(fun1, allArgs1) withType mt.resultType + case _ => + throw new MatchError(i"tree $tree has unexpected type of function ${fun1.tpe.widen}, was ${fun.typeOpt.widen}") + } } } diff --git a/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala b/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala deleted file mode 100644 index cf7668079681..000000000000 --- a/src/dotty/tools/dotc/transform/UncurryTreeTransform.scala +++ /dev/null @@ -1,46 +0,0 @@ -package dotty.tools.dotc -package transform - -import TreeTransforms._ -import core.DenotTransformers._ -import core.Denotations._ -import core.SymDenotations._ -import core.Contexts._ -import core.Types._ -import core.Symbols._ -import ast.Trees._ -import ast.tpd.{Apply, Tree, cpy} - -class UncurryTreeTransform extends MiniPhaseTransform with InfoTransformer { - - override def name: String = "uncurry" - override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = - ctx.traceIndented(s"transforming ${tree.show}", show = true) { - tree.fun match { - case Apply(fn, args) => - def showType(implicit ctx: Context) = - ctx.log(s"at ${ctx.phase} ${fn.symbol} has type ${fn.symbol.info.widen.show}") - showType - ctx.atNextPhase(showType(_)) - showType - cpy.Apply(tree)(tree.fun, args ++ tree.args) - case _ => tree - }} - - def uncurry(tp: Type)(implicit ctx: Context): Type = tp match { - case tp @ MethodType(pnames1, ptypes1) => - tp.resultType match { - case rt @ MethodType(pnames2, ptypes2) => - tp.derivedMethodType(pnames1 ++ pnames2, ptypes1 ++ ptypes2, rt.resultType) - case _ => - tp - } - case tp: PolyType => - tp.derivedPolyType(tp.paramNames, tp.paramBounds, uncurry(tp.resultType)) - case _ => - tp - } - - def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = - uncurry(tp) -} \ No newline at end of file