diff --git a/bench/src/test/scala/TestsAsBenchmarks.scala b/bench/src/test/scala/TestsAsBenchmarks.scala new file mode 100644 index 000000000000..a078c9f787e0 --- /dev/null +++ b/bench/src/test/scala/TestsAsBenchmarks.scala @@ -0,0 +1,61 @@ +package dotty.tools.benchmarks + +import java.lang.annotation.Annotation +import java.lang.reflect.Method + +import org.junit.runner.Request +import org.junit.runner.notification.RunNotifier +import org.scalameter.PerformanceTest.OnlineRegressionReport +import org.scalameter.api._ +import org.scalameter.reporting.RegressionReporter.Tester + +import scala.collection.mutable.ListBuffer + + +abstract class TestsToBenchmarkConverter +(targetClass: Class[_], + filterAnnot: Class[_ <: java.lang.annotation.Annotation] = classOf[org.junit.Test].asInstanceOf[Class[_ <: java.lang.annotation.Annotation]]) + extends OnlineRegressionReport { + + // accept all the results, do not fail + override def tester: Tester = new Tester.Accepter + + override def executor: Executor = LocalExecutor(warmer, aggregator, measurer) + val testNames = getMethodsAnnotatedWith(targetClass, filterAnnot).map(_.getName).sorted + + + val tests = testNames.map{name => + val runner = Request.method(targetClass, name).getRunner + (name, Gen.single("test")(name).map(Request.method(targetClass, _).getRunner))}.toMap + //Gen.enumeration("test")(testNames:_*) + + performance of targetClass.getSimpleName config (Context(reports.resultDir -> "./tmp")) in { + for (test <- testNames) + measure.method(test) in { + using(tests(test)) curve test in { + r => + val dummy = new RunNotifier() + r.run(dummy) + } + } + } + + def getMethodsAnnotatedWith(clazz: Class[_], annotation: Class[_ <: java.lang.annotation.Annotation]): List[Method] = { + val methods = ListBuffer[Method]() + var klass: Class[_] = clazz + while (klass ne classOf[AnyRef]) { + val allMethods = klass.getDeclaredMethods + import scala.collection.JavaConversions._ + for (method <- allMethods) { + if (annotation == null || method.isAnnotationPresent(annotation)) { + val annotInstance: Annotation = method.getAnnotation(annotation) + methods.add(method) + } + } + klass = klass.getSuperclass + } + methods.toList + } +} + +object dotcTests extends TestsToBenchmarkConverter(classOf[dotc.tests]) diff --git a/project/Build.scala b/project/Build.scala index b6ae0c74255f..33a09a4f7224 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1,13 +1,12 @@ +import sbt.Keys._ import sbt._ -import Keys._ -import Process._ object DottyBuild extends Build { val TRAVIS_BUILD = "dotty.travis.build" val agentOptions = List( - // "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005", + // "-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" // "-agentpath:/home/dark/opt/yjp-2013-build-13072/bin/linux-x86-64/libyjpagent.so" ) @@ -74,4 +73,51 @@ object DottyBuild extends Build { ) lazy val dotty = Project(id = "dotty", base = file("."), settings = defaults) + + lazy val benchmarkSettings = Defaults.defaultSettings ++ Seq( + + // to get Scala 2.11 + resolvers += Resolver.sonatypeRepo("releases"), + + baseDirectory in (Test,run) := (baseDirectory in dotty).value, + + + libraryDependencies ++= Seq("com.storm-enroute" %% "scalameter" % "0.6" % Test, + "com.novocode" % "junit-interface" % "0.11-RC1"), + testFrameworks += new TestFramework("org.scalameter.ScalaMeterFramework"), + + // scalac options + scalacOptions in Global ++= Seq("-feature", "-deprecation", "-language:_"), + + javacOptions ++= Seq("-Xlint:unchecked", "-Xlint:deprecation"), + + fork in Test := true, + parallelExecution in Test := false, + + // http://grokbase.com/t/gg/simple-build-tool/135ke5y90p/sbt-setting-jvm-boot-paramaters-for-scala + javaOptions <++= (dependencyClasspath in Runtime, packageBin in Compile) map { (attList, bin) => + // put the Scala {library, reflect, compiler} in the classpath + val path = for { + file <- attList.map(_.data) + path = file.getAbsolutePath + prefix = if(path.endsWith(".jar")) "p" else "a" + } yield "-Xbootclasspath/"+ prefix +":" + path + // dotty itself needs to be in the bootclasspath + val fullpath = ("-Xbootclasspath/a:" + bin) :: path.toList + // System.err.println("BOOTPATH: " + fullpath) + + val travis_build = // propagate if this is a travis build + if (sys.props.isDefinedAt(TRAVIS_BUILD)) + List(s"-D$TRAVIS_BUILD=${sys.props(TRAVIS_BUILD)}") + else + List() + val res = agentOptions ::: travis_build ::: fullpath + println("Running with javaOptions: " +res) + res + } + ) + + + lazy val benchmarks = Project(id = "dotty-bench", settings = benchmarkSettings, + base = file("bench")) dependsOn(dotty % "compile->test") } diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index fff4b517b6a4..258cab36889a 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._ @@ -16,25 +16,54 @@ import dotty.tools.dotc.core.Denotations.SingleDenotation class Compiler { + /** Meta-ordering constraint: + * + * DenotTransformers that change the signature of their denotation's info must go + * after erasure. The reason is that denotations are permanently referred to by + * TermRefs which contain a signature. If the signature of a symbol would change, + * all refs to it would become outdated - they could not be dereferenced in the + * new phase. + * + * As an example, addGetters would change a field + * + * val x: T + * + * to a method + * + * def x: T + * + * but this would affect the signature of `x` (goes from NotAMethod to a method + * signature). So we can't do this before erasure. + * + * After erasure, signature changing denot-transformers are OK because erasure + * will make sure that only term refs with fixed SymDenotations survive beyond it. This + * is possible because: + * + * - splitter has run, so every ident or select refers to a unique symbol + * - after erasure, asSeenFrom is the identity, so every reference has a + * plain SymDenotation, as opposed to a UniqueRefDenotation. + */ def phases: List[List[Phase]] = List( List(new FrontEnd), - List(new Companions), + List(new FirstTransform, + new SyntheticMethods), List(new SuperAccessors), - // pickling and refchecks goes here - List(new ElimRepeated, new ElimLocals), - List(new ExtensionMethods), - List(new TailRec), + // pickling goes here + List(new RefChecks, + new ElimRepeated, + new ElimLocals, + new ExtensionMethods, + new TailRec), List(new PatternMatcher, - new LazyValTranformContext().transformer, + new ExplicitOuter, + new LazyValsTransform, new Splitter), - List(new Nullarify, - new TypeTestsCasts, + List(new ElimByName, new InterceptedMethods, new Literalize), List(new Erasure), - List(new UncurryTreeTransform - /* , new Constructors */) + List(new CapturedVars) ) var runId = 1 diff --git a/src/dotty/tools/dotc/ElimLocals.scala b/src/dotty/tools/dotc/ElimLocals.scala index cc971f05c5fe..20afc8eb4dc4 100644 --- a/src/dotty/tools/dotc/ElimLocals.scala +++ b/src/dotty/tools/dotc/ElimLocals.scala @@ -6,12 +6,12 @@ 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 => - override def name = "elimlocals" +class ElimLocals extends MiniPhaseTransform with SymTransformer { thisTransformer => + override def phaseName = "elimLocals" def transformSym(ref: SymDenotation)(implicit ctx: Context) = dropLocal(ref) diff --git a/src/dotty/tools/dotc/Flatten.scala b/src/dotty/tools/dotc/Flatten.scala new file mode 100644 index 000000000000..71f669e26717 --- /dev/null +++ b/src/dotty/tools/dotc/Flatten.scala @@ -0,0 +1,15 @@ +package dotty.tools.dotc +package transform + +import core._ +import DenotTransformers.SymTransformer +import Phases.Phase +import Contexts.Context +import SymDenotations.SymDenotation +import TreeTransforms.MiniPhaseTransform + +class Flatten extends MiniPhaseTransform with SymTransformer { thisTransformer => + override def phaseName = "flatten" + + def transformSym(ref: SymDenotation)(implicit ctx: Context) = ??? +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/Run.scala b/src/dotty/tools/dotc/Run.scala index a639b20cd770..2fbfc3f9b1f2 100644 --- a/src/dotty/tools/dotc/Run.scala +++ b/src/dotty/tools/dotc/Run.scala @@ -28,6 +28,12 @@ class Run(comp: Compiler)(implicit ctx: Context) { compileSources(sources) } + /** TODO: There's a fundamental design problem here: We assmble phases using `squash` + * when we first build the compiler. But we modify them with -Yskip, -Ystop + * on each run. That modification needs to either trasnform the tree structure, + * or we need to assmeble phases on each run, and take -Yskip, -Ystop into + * account. I think the latter would be preferable. + */ def compileSources(sources: List[SourceFile]) = Stats.monitorHeartBeat { if (sources forall (_.exists)) { units = sources map (new CompilationUnit(_)) @@ -36,18 +42,19 @@ class Run(comp: Compiler)(implicit ctx: Context) { ctx.settings.YstopAfter.value.containsPhase(phase.prev) val phasesToRun = ctx.allPhases.init .takeWhile(!stoppedBefore(_)) - .filterNot(ctx.settings.Yskip.value.containsPhase(_)) - for (phase <- phasesToRun) { + .filterNot(ctx.settings.Yskip.value.containsPhase(_)) // TODO: skip only subphase + for (phase <- phasesToRun) if (!ctx.reporter.hasErrors) { phase.runOn(units) def foreachUnit(op: Context => Unit)(implicit ctx: Context): Unit = for (unit <- units) op(ctx.fresh.setPhase(phase.next).setCompilationUnit(unit)) if (ctx.settings.Xprint.value.containsPhase(phase)) foreachUnit(printTree) - if (ctx.settings.Ycheck.value.containsPhase(phase) && !ctx.reporter.hasErrors) - foreachUnit(TreeChecker.check) + if (ctx.settings.Ycheck.value.containsPhase(phase) && !ctx.reporter.hasErrors) { + assert(phase.isCheckable, s"phase $phase is not checkable") + foreachUnit(TreeChecker.check(phasesToRun, _)) + } } - } } } diff --git a/src/dotty/tools/dotc/core/transform/Erasure.scala b/src/dotty/tools/dotc/TypeErasure.scala similarity index 52% rename from src/dotty/tools/dotc/core/transform/Erasure.scala rename to src/dotty/tools/dotc/TypeErasure.scala index 587f0c088304..1212c9e4dac8 100644 --- a/src/dotty/tools/dotc/core/transform/Erasure.scala +++ b/src/dotty/tools/dotc/TypeErasure.scala @@ -1,11 +1,54 @@ -package dotty.tools.dotc +package dotty.tools +package dotc package core -package transform -import Symbols._, Types._, Contexts._, Flags._, Names._, StdNames._, Flags.JavaDefined +import Symbols._, Types._, Contexts._, Flags._, Names._, StdNames._, Decorators._, Flags.JavaDefined +import dotc.transform.ExplicitOuter._ import util.DotClass -object Erasure { +/** Erased types are: + * + * TypeRef(prefix is ignored, denot is ClassDenotation) + * TermRef(prefix is ignored, denot is SymDenotation) + * JavaArrayType + * AnnotatedType + * MethodType + * ThisType + * SuperType + * ClassInfo (NoPrefix, ...) + * NoType + * NoPrefix + * WildcardType + * ErrorType + * + * only for isInstanceOf, asInstanceOf: PolyType, PolyParam, TypeBounds + * + */ +object TypeErasure { + + /** A predicate that tests whether a type is a legal erased type. Only asInstanceOf and + * isInstanceOf may have types that do not satisfy the predicate. + */ + def isErasedType(tp: Type)(implicit ctx: Context): Boolean = tp match { + case tp: TypeRef => + tp.symbol.isClass + case _: TermRef => + true + case JavaArrayType(elem) => + isErasedType(elem) + case AnnotatedType(_, tp) => + isErasedType(tp) + case ThisType(tref) => + isErasedType(tref) + case tp: MethodType => + tp.paramTypes.forall(isErasedType) && isErasedType(tp.resultType) + case tp @ ClassInfo(pre, _, parents, decls, _) => + isErasedType(pre) && parents.forall(isErasedType) //&& decls.forall(sym => isErasedType(sym.info)) && isErasedType(tp.selfType) + case NoType | NoPrefix | WildcardType | ErrorType | SuperType(_, _) => + true + case _ => + false + } case class ErasedValueType(cls: ClassSymbol, underlying: Type) extends CachedGroundType { override def computeHash = doHash(cls, underlying) @@ -17,7 +60,7 @@ object Erasure { (if (isConstructor) 4 else 0) + (if (wildcardOK) 8 else 0) - private val erasures = new Array[Erasure](16) + private val erasures = new Array[TypeErasure](16) for { isJava <- List(false, true) @@ -25,7 +68,7 @@ object Erasure { isConstructor <- List(false, true) wildcardOK <- List(false, true) } erasures(erasureIdx(isJava, isSemi, isConstructor, wildcardOK)) = - new Erasure(isJava, isSemi, isConstructor, wildcardOK) + new TypeErasure(isJava, isSemi, isConstructor, wildcardOK) /** Produces an erasure function. * @param isJava Arguments should be treated the way Java does it @@ -36,7 +79,7 @@ object Erasure { * @param wildcardOK Wildcards are acceptable (true when using the erasure * for computing a signature name). */ - private def erasureFn(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcardOK: Boolean): Erasure = + private def erasureFn(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcardOK: Boolean): TypeErasure = erasures(erasureIdx(isJava, isSemi, isConstructor, wildcardOK)) private val scalaErasureFn = erasureFn(isJava = false, isSemi = false, isConstructor = false, wildcardOK = false) @@ -56,19 +99,21 @@ object Erasure { /** The symbol's erased info. This is the type's erasure, except for the following symbols: * * - For $asInstanceOf : [T]T - * - For $isInstanceOf : [T]scala#Boolean - * - For Array[T]. : [T]{scala#Int)Array[T] - * - For type members of Array : The original info - * - For all other abstract types: = ? + * - For $isInstanceOf : [T]Boolean + * - For all abstract types : = ? * - For all other symbols : the semi-erasure of their types, with * isJava, isConstructor set according to symbol. */ def transformInfo(sym: Symbol, tp: Type)(implicit ctx: Context): Type = { val erase = erasureFn(sym is JavaDefined, isSemi = true, sym.isConstructor, wildcardOK = false) - if ((sym eq defn.Any_asInstanceOf) || - (sym eq defn.Any_isInstanceOf) || - (sym.owner eq defn.ArrayClass) && (sym.isType || sym.isConstructor)) sym.info + + def eraseParamBounds(tp: PolyType): Type = + tp.derivedPolyType( + tp.paramNames, tp.paramNames map (Function.const(TypeBounds.upper(defn.ObjectType))), tp.resultType) + + if ((sym eq defn.Any_asInstanceOf) || (sym eq defn.Any_isInstanceOf)) eraseParamBounds(sym.info.asInstanceOf[PolyType]) else if (sym.isAbstractType) TypeAlias(WildcardType) + else if (sym.isConstructor) outer.addParam(sym.owner.asClass, erase(tp)) else erase(tp) } @@ -77,8 +122,70 @@ object Erasure { tp.classSymbol.isPrimitiveValueClass || (tp.typeSymbol is JavaDefined)) + /** The erased least upper bound is computed as follows + * - if both argument are arrays, an array of the lub of the element types + * - if one argument is an array, Object + * - otherwise a common superclass or trait S of the argument classes, with the + * following two properties: + * S is minimal: no other common superclass or trait derives from S] + * S is last : in the linearization of the first argument type `tp1` + * there are no minimal common superclasses or traits that + * come after S. + * (the reason to pick last is that we prefer classes over traits that way). + */ + def erasedLub(tp1: Type, tp2: Type)(implicit ctx: Context): Type = tp1 match { + case JavaArrayType(elem1) => + tp2 match { + case JavaArrayType(elem2) => JavaArrayType(erasedLub(elem1, elem2)) + case _ => defn.ObjectType + } + case _ => + tp2 match { + case JavaArrayType(_) => defn.ObjectType + case _ => + val cls2 = tp2.classSymbol + def loop(bcs: List[ClassSymbol], bestSoFar: ClassSymbol): ClassSymbol = bcs match { + case bc :: bcs1 => + if (cls2.derivesFrom(bc)) + if (!bc.is(Trait)) bc + else loop(bcs1, if (bestSoFar.derivesFrom(bc)) bestSoFar else bc) + else + loop(bcs1, bestSoFar) + case nil => + bestSoFar + } + loop(tp1.baseClasses, defn.ObjectClass).typeRef + } + } + + /** The erased greatest lower bound picks one of the two argument types. It prefers, in this order: + * - arrays over non-arrays + * - subtypes over supertypes, unless isJava is set + * - real classes over traits + */ + def erasedGlb(tp1: Type, tp2: Type, isJava: Boolean)(implicit ctx: Context): Type = tp1 match { + case JavaArrayType(elem1) => + tp2 match { + case JavaArrayType(elem2) => JavaArrayType(erasedGlb(elem1, elem2, isJava)) + case _ => tp1 + } + case _ => + tp2 match { + case JavaArrayType(_) => tp2 + case _ => + val tsym1 = tp1.typeSymbol + val tsym2 = tp2.typeSymbol + if (!tsym2.exists) tp1 + else if (!tsym1.exists) tp2 + else if (!isJava && tsym1.derivesFrom(tsym2)) tp1 + else if (!isJava && tsym2.derivesFrom(tsym1)) tp2 + else if (tp1.typeSymbol.isRealClass) tp1 + else if (tp2.typeSymbol.isRealClass) tp2 + else tp1 + } + } } -import Erasure._ +import TypeErasure._ /** * This is used as the Scala erasure during the erasure phase itself @@ -86,40 +193,42 @@ import Erasure._ * are then later converted to the underlying parameter type in phase posterasure. * */ -class Erasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcardOK: Boolean) extends DotClass { +class TypeErasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcardOK: Boolean) extends DotClass { /** The erasure |T| of a type T. This is: * * - For a refined type scala.Array+[T]: - * - if T is Nothing or Null, scala.Array+[Object] - * - otherwise, if T <: Object, scala.Array+[|T|] - * - otherwise, if T is a type paramter coming from Java, scala.Array+[Object]. + * - if T is Nothing or Null, []Object + * - otherwise, if T <: Object, []|T| + * - otherwise, if T is a type paramter coming from Java, []Object * - otherwise, Object - * - For a constant type, NoType or NoPrefix, the type itself. - * - For all other type proxies: The erasure of the underlying type. - * - For a typeref scala.Any, scala.AnyVal, scala.Singleon or scala.NotNull: java.lang.Object. - * - For a typeref scala.Unit, scala.runtime.BoxedUnit. - * - For a typeref whose symbol is owned by Array: The typeref itself - * - For a typeref P.C where C refers to a toplevel class, P.C. - * - For a typeref P.C where C refers to a nested class, |P|.C. + * - For a term ref p.x, the type # x. + * - For a typeref scala.Any, scala.AnyVal, scala.Singleon or scala.NotNull: |java.lang.Object| + * - For a typeref scala.Unit, |scala.runtime.BoxedUnit|. + * - For a typeref P.C where C refers to a class, # C. * - For a typeref P.C where C refers to an alias type, the erasure of C's alias. * - For a typeref P.C where C refers to an abstract type, the erasure of C's upper bound. - * - For T1 & T2, erasure(T1) (??) + * - For a this-type C.this, the type itself. + * - For all other type proxies: The erasure of the underlying type. + * - For T1 & T2, the erased glb of |T1| and |T2| (see erasedGlb) * - For T1 | T2, the first base class in the linearization of T which is also a base class of T2 + * - For => T, ()T * - For a method type (Fs)scala.Unit, (|Fs|)scala.Unit. - * - For any other method type (Fs)T, (|Fs|)|T|. - * - For a polymorphic type, the erasure of its result type. + * - 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 [Ts](Ps)T, |(Ps)T| + * _ For a polymorphic type [Ts]T where T is not a method type, ()|T| * - 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. * - For any other class info type with parents Ps, the same type with * parents |Ps|, but with duplicate references of Object removed. + * - For NoType or NoPrefix, the type itself. * - For any other type, exception. */ def apply(tp: Type)(implicit ctx: Context): Type = tp match { case tp: TypeRef => val sym = tp.symbol - if (!sym.isClass) - if (sym.exists && (sym.owner eq defn.ArrayClass)) tp else this(tp.info) //!!!! + if (!sym.isClass) this(tp.info) else if (sym.isDerivedValueClass) eraseDerivedValueClassRef(tp) else eraseNormalClassRef(tp) case tp: RefinedType => @@ -127,50 +236,65 @@ class Erasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcard if (parent isRef defn.ArrayClass) eraseArray(tp) else this(parent) case tp: TermRef => - val sym = tp.symbol - if (sym.exists && (sym.owner is Package)) sym.termRef - else tp.derivedSelect(this(tp.prefix)) - case _: ThisType | _: ConstantType => + assert(tp.symbol.exists, tp) + TermRef(NoPrefix, tp.symbol.asTerm) + case ThisType(_) | SuperType(_, _) => tp + case ExprType(rt) => + MethodType(Nil, Nil, this(rt)) case tp: TypeProxy => this(tp.underlying) case AndType(tp1, tp2) => - mergeAnd(this(tp1), this(tp2)) + erasedGlb(this(tp1), this(tp2), isJava) case OrType(tp1, tp2) => - this(tp.baseTypeRef(lubClass(tp1, tp2))) + ctx.typeComparer.orType(this(tp1), this(tp2), erased = true) 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) + this(tp.resultType) match { + case rt: MethodType => rt + case rt => MethodType(Nil, Nil, rt) + } 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)) - case NoType | NoPrefix | ErrorType => + 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 removeLaterObjects(classParents.mapConserve(eraseTypeRef)) + tp.derivedClassInfo(NoPrefix, parents, decls, this(tp.selfType)) + // can't replace selftype by NoType because this would lose the sourceModule link + } + case NoType | NoPrefix | ErrorType | JavaArrayType(_) => tp case tp: WildcardType if wildcardOK => tp } - private def eraseArray(tp: RefinedType)(implicit ctx: Context) = { + def eraseArray(tp: RefinedType)(implicit ctx: Context) = { val defn.ArrayType(elemtp) = tp - if (elemtp derivesFrom defn.NullClass) defn.ObjectArrayType - else if (isUnboundedGeneric(elemtp)) defn.ObjectType - else defn.ArrayType(this(elemtp)) + if (elemtp derivesFrom defn.NullClass) JavaArrayType(defn.ObjectType) + else if (isUnboundedGeneric(elemtp)) + elemtp match { + case elemtp: TypeRef if elemtp.symbol.is(JavaDefined) => JavaArrayType(defn.ObjectType) + case _ => defn.ObjectType + } + else JavaArrayType(this(elemtp)) } private def eraseDerivedValueClassRef(tref: TypeRef)(implicit ctx: Context): Type = unsupported("eraseDerivedValueClass") private def eraseNormalClassRef(tref: TypeRef)(implicit ctx: Context): Type = { - val sym = tref.symbol - if (sym.owner is Package) normalizeClass(sym.asClass).typeRef - else tref.derivedSelect(this(tref.prefix)) + val cls = tref.symbol.asClass + (if (cls.owner is Package) normalizeClass(cls) else cls).typeRef } private def eraseResult(tp: Type)(implicit ctx: Context): Type = tp match { @@ -195,41 +319,11 @@ class Erasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcard cls } - private def lubClass(tp1: Type, tp2: Type)(implicit ctx: Context): ClassSymbol = { - var bcs1 = tp1.baseClasses - val bc2 = tp2.baseClasses.head - while (bcs1.nonEmpty && !bc2.derivesFrom(bcs1.head)) - bcs1 = bcs1.tail - if (bcs1.isEmpty) defn.ObjectClass else bcs1.head - } - private def removeLaterObjects(trs: List[TypeRef])(implicit ctx: Context): List[TypeRef] = trs match { case tr :: trs1 => tr :: trs1.filterNot(_ isRef defn.ObjectClass) case nil => nil } - private def mergeAnd(tp1: Type, tp2: Type)(implicit ctx: Context): Type = tp1 match { - case defn.ArrayType(elem1) => - tp2 match { - case defn.ArrayType(elem2) => defn.ArrayType(mergeAnd(elem1, elem2)) - case _ => defn.ObjectType - } - case _ => - tp2 match { - case defn.ArrayType(_) => defn.ObjectType - case _ => - val tsym1 = tp1.typeSymbol - val tsym2 = tp2.typeSymbol - if (!tsym2.exists) tp1 - else if (!tsym1.exists) tp2 - else if (!isJava && tsym1.derivesFrom(tsym2)) tp1 - else if (!isJava && tsym2.derivesFrom(tsym1)) tp2 - else if (tp1.typeSymbol.isRealClass) tp1 - else if (tp2.typeSymbol.isRealClass) tp2 - else tp1 - } - } - /** The name of the type as it is used in `Signature`s. * Need to ensure correspondence with erasure! */ @@ -240,9 +334,13 @@ class Erasure(isJava: Boolean, isSemi: Boolean, isConstructor: Boolean, wildcard else if (sym.isDerivedValueClass) sigName(eraseDerivedValueClassRef(tp)) else normalizeClass(sym.asClass).fullName.asTypeName case defn.ArrayType(elem) => + sigName(this(tp)) + case JavaArrayType(elem) => sigName(elem) ++ "[]" case tp: TypeBounds => sigName(tp.hi) + case ExprType(rt) => + sigName(defn.FunctionType(Nil, rt)) case tp: TypeProxy => sigName(tp.underlying) case ErrorType | WildcardType => diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index c3d0f9c3a8a3..d7bbdf2bb386 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -72,13 +72,13 @@ 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,8 +98,8 @@ 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, - mods | Accessor, name.setterName, Nil, (setterParam :: Nil) :: Nil, + val setter = cpy.DefDef(vdef)( + (mods | Accessor) &~ CaseAccessor, 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 +124,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 +146,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 +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 => @@ -182,9 +182,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) } } @@ -196,25 +196,26 @@ 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) { - 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)) + if (tdef.mods is PrivateLocalParam) { + val tparam = cpy.TypeDef(tdef)( + mods = tdef.mods &~ PrivateLocal | ExpandedName, + name = tdef.name.expandedName(ctx.owner)) + val alias = cpy.TypeDef(tdef)( + 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) + else tdef } 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)(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 = { @@ -239,13 +240,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 } @@ -287,24 +287,21 @@ object desugar { def syntheticProperty(name: TermName, rhs: Tree) = 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 { 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 + copyMeths ::: isDefinedMeth :: productElemMeths.toList } else Nil @@ -312,12 +309,12 @@ object desugar { def productConstr(n: Int) = { val tycon = ref(defn.ProductNClass(n).typeRef) val targs = constrVparamss.head map (_.tpt) - AppliedTypeTree(tycon, targs) + if (targs.isEmpty) tycon else AppliedTypeTree(tycon, targs) } // Case classes get a ProductN parent var parents1 = parents - if ((mods is Case) && 2 <= arity && arity <= Definitions.MaxTupleArity) + if ((mods is Case) && arity <= Definitions.MaxTupleArity) parents1 = parents1 :+ productConstr(arity) // The thicket which is the desugared version of the companion object @@ -326,7 +323,8 @@ object desugar { moduleDef( ModuleDef( Modifiers(Synthetic), name.toTermName, - Template(emptyConstructor, parentTpt :: Nil, EmptyValDef, defs))).toList + Template(emptyConstructor, parentTpt :: Nil, EmptyValDef, defs))) + .withPos(cdef.pos).toList // The companion object defifinitions, if a companion is needed, Nil otherwise. // companion definitions include: @@ -340,7 +338,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 @@ -379,21 +378,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, 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)) + cpy.TypeDef(cdef)( + rhs = cpy.Template(impl)(constr, parents1, self1, + tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths), + tparams = Nil) } // install the watch on classTycon @@ -414,18 +415,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) + 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,7 +503,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 @@ -515,7 +516,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. @@ -562,7 +563,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)) { @@ -860,7 +861,7 @@ object desugar { case tree @ Bind(_, tree1) => add(tree, TypeTree()) collect(tree1) - case Typed(id: Ident, t) if isVarPattern(id) && id.name != nme.WILDCARD => + case Typed(id: Ident, t) if isVarPattern(id) && id.name != nme.WILDCARD && !isWildcardStarArg(tree) => add(id, t) case id: Ident if isVarPattern(id) && id.name != nme.WILDCARD => add(id, TypeTree()) diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala index cc3e53abcd8a..51e1ff16ffb7 100644 --- a/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -103,11 +103,19 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => /** The number of arguments in an application */ def numArgs(tree: Tree): Int = unsplice(tree) match { case Apply(fn, args) => numArgs(fn) + args.length - case TypeApply(fn, args) => numArgs(fn) - case Block(stats, expr) => numArgs(expr) + case TypeApply(fn, _) => numArgs(fn) + case Block(_, expr) => numArgs(expr) case _ => 0 } + /** The (last) list of arguments of an application */ + def arguments(tree: Tree): List[Tree] = unsplice(tree) match { + case Apply(_, args) => args + case TypeApply(fn, _) => arguments(fn) + case Block(_, expr) => arguments(expr) + case _ => Nil + } + /** Is tree a self constructor call this(...)? I.e. a call to a constructor of the * same object? */ @@ -253,7 +261,7 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => * is an abstract typoe declaration */ def lacksDefinition(mdef: MemberDef) = mdef match { - case mdef: ValOrDefDef => mdef.rhs.isEmpty && !mdef.name.isConstructorName + case mdef: ValOrDefDef => mdef.rhs.isEmpty && !mdef.name.isConstructorName && !mdef.mods.is(ParamAccessor) case mdef: TypeDef => mdef.rhs.isEmpty || mdef.rhs.isInstanceOf[TypeBoundsTree] case _ => false } @@ -401,7 +409,7 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => /** Strips layers of `.asInstanceOf[T]` / `_.$asInstanceOf[T]()` from an expression */ def stripCast(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = { - def isCast(sel: tpd.Tree) = defn.asInstanceOfMethods contains sel.symbol + def isCast(sel: tpd.Tree) = sel.symbol == defn.Any_asInstanceOf unsplice(tree) match { case TypeApply(sel @ Select(inner, _), _) if isCast(sel) => stripCast(inner) @@ -412,6 +420,17 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => } } + /** The variables defined by a pattern, in reverse order of their appearance. */ + def patVars(tree: Tree)(implicit ctx: Context): List[Symbol] = { + val acc = new TreeAccumulator[List[Symbol]] { + def apply(syms: List[Symbol], tree: Tree) = tree match { + case Bind(_, body) => apply(tree.symbol :: syms, body) + case _ => foldOver(syms, tree) + } + } + acc(Nil, tree) + } + /** Is this pattern node a catch-all or type-test pattern? */ def isCatchCase(cdef: CaseDef)(implicit ctx: Context) = cdef match { case CaseDef(Typed(Ident(nme.WILDCARD), tpt), EmptyTree, _) => @@ -430,6 +449,10 @@ trait TypedTreeInfo extends TreeInfo[Type] { self: Trees.Instance[Type] => false } + /** The symbols defined locally in a statement list */ + def localSyms(stats: List[Tree])(implicit ctx: Context): List[Symbol] = + for (stat <- stats if stat.isDef && stat.symbol.exists) yield stat.symbol + /** If `tree` is a DefTree, the symbol defined by it, otherwise NoSymbol */ def definedSym(tree: Tree)(implicit ctx: Context): Symbol = if (tree.isDef) tree.symbol else NoSymbol diff --git a/src/dotty/tools/dotc/ast/TreeTypeMap.scala b/src/dotty/tools/dotc/ast/TreeTypeMap.scala new file mode 100644 index 000000000000..56602cca91e1 --- /dev/null +++ b/src/dotty/tools/dotc/ast/TreeTypeMap.scala @@ -0,0 +1,172 @@ +package dotty.tools +package dotc +package ast + +import core._ +import Types._, Contexts._, Constants._, Names._, Flags._ +import SymDenotations._, Symbols._, Annotations._, Trees._, Symbols._ +import Denotations._, Decorators._ + +/** A map that applies three functions and a substitution 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 + * type of every tree node and to all locally defined symbols, + * followed by the substitution [substFrom := substTo]. + * @param ownerMap A function that translates owners of top-level local symbols + * defined in the mapped tree. + * @param treeMap A transformer that translates all encountered subtrees in + * prefix traversal orders + * @param oldOwners Previous owners. If a top-level local symbol in the mapped tree + * has one of these as an owner, the owner is replaced by the corresponding + * symbol in `newOwners`. + * @param newOwners New owners, replacing previous owners. + * @param substFrom The symbols that need to be substituted. + * @param substTo The substitution targets. + * + * The reason the substitution is broken out from the rest of the type map is + * that all symbols have to be substituted at the same time. If we do not do this, + * we risk data races on named types. Example: Say we have `outer#1.inner#2` and we + * have two substitutons S1 = [outer#1 := outer#3], S2 = [inner#2 := inner#4] where + * hashtags precede symbol ids. If we do S1 first, we get outer#2.inner#3. If we then + * do S2 we get outer#2.inner#4. But that means that the named type outer#2.inner + * gets two different denotations in the same period. Hence, if -YnoDoubleBindings is + * set, we would get a data race assertion error. + */ +final class TreeTypeMap( + val typeMap: Type => Type = IdentityTypeMap, + val treeMap: tpd.Tree => tpd.Tree = identity _, + val oldOwners: List[Symbol] = Nil, + val newOwners: List[Symbol] = Nil, + val substFrom: List[Symbol] = Nil, + val substTo: List[Symbol] = Nil)(implicit ctx: Context) extends tpd.TreeMap { + import tpd._ + + /** If `sym` is one of `oldOwners`, replace by corresponding symbol in `newOwners` */ + def mapOwner(sym: Symbol) = { + def loop(from: List[Symbol], to: List[Symbol]): Symbol = + if (from.isEmpty) sym + else if (sym eq from.head) to.head + else loop(from.tail, to.tail) + loop(oldOwners, newOwners) + } + + /** Replace occurrences of `This(oldOwner)` in some prefix of a type + * by the corresponding `This(newOwner)`. + */ + private val mapOwnerThis = new TypeMap { + private def mapPrefix(from: List[Symbol], to: List[Symbol], tp: Type): Type = from match { + case Nil => tp + case (cls: ClassSymbol) :: from1 => mapPrefix(from1, to.tail, tp.substThis(cls, to.head.thisType)) + case _ :: from1 => mapPrefix(from1, to.tail, tp) + } + def apply(tp: Type): Type = tp match { + case tp: NamedType => tp.derivedSelect(mapPrefix(oldOwners, newOwners, tp.prefix)) + case _ => mapOver(tp) + } + } + + def mapType(tp: Type) = + mapOwnerThis(typeMap(tp).substSym(substFrom, substTo)) + + private def updateDecls(prevStats: List[Tree], newStats: List[Tree]): Unit = + if (prevStats.isEmpty) assert(newStats.isEmpty) + else { + prevStats.head match { + case pdef: MemberDef => + val prevSym = pdef.symbol + val newSym = newStats.head.symbol + val newCls = newSym.owner.asClass + if (prevSym != newSym) newCls.replace(prevSym, newSym) + case _ => + } + updateDecls(prevStats.tail, newStats.tail) + } + + override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = treeMap(tree) match { + case impl @ Template(constr, parents, self, body) => + val tmap = withMappedSyms(impl.symbol :: impl.constr.symbol :: Nil) + val parents1 = parents mapconserve transform + val (_, constr1 :: self1 :: Nil) = transformDefs(constr :: self :: Nil) + val body1 = tmap.transformStats(body) + updateDecls(constr :: body, constr1 :: body1) + cpy.Template(impl)( + constr1.asInstanceOf[DefDef], parents1, self1.asInstanceOf[ValDef], body1) + .withType(tmap.mapType(impl.tpe)) + case tree1 => + tree1.withType(mapType(tree1.tpe)) match { + case id: Ident if tpd.needsSelect(id.tpe) => + ref(id.tpe.asInstanceOf[TermRef]).withPos(id.pos) + 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)) + case blk @ Block(stats, expr) => + val (tmap1, stats1) = transformDefs(stats) + val expr1 = tmap1.transform(expr) + 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) + cpy.CaseDef(cdef)(pat1, guard1, rhs1) + case tree1 => + super.transform(tree1) + } + } + + override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context) = + transformDefs(trees)._2 + + private def transformDefs[TT <: tpd.Tree](trees: List[TT])(implicit ctx: Context): (TreeTypeMap, List[TT]) = { + val tmap = withMappedSyms(tpd.localSyms(trees)) + (tmap, tmap.transformSub(trees)) + } + + private def transformVParamss(vparamss: List[List[ValDef]]): (TreeTypeMap, List[List[ValDef]]) = vparamss match { + case vparams :: rest => + val (tmap1, vparams1) = transformDefs(vparams) + val (tmap2, vparamss2) = tmap1.transformVParamss(rest) + (tmap2, vparams1 :: vparamss2) + case nil => + (this, vparamss) + } + + def apply[ThisTree <: tpd.Tree](tree: ThisTree): ThisTree = transform(tree).asInstanceOf[ThisTree] + + def apply(annot: Annotation): Annotation = { + val tree1 = apply(annot.tree) + if (tree1 eq annot.tree) annot else ConcreteAnnotation(tree1) + } + + /** The current tree map composed with a substitution [from -> to] */ + def withSubstitution(from: List[Symbol], to: List[Symbol]): TreeTypeMap = + if (from eq to) this + else { + // assert that substitution stays idempotent, assuming its parts are + // TODO: It might be better to cater for the asserted-away conditions, by + // setting up a proper substitution abstraction with a compose operator that + // guarantees idempotence. But this might be too inefficient in some cases. + // We'll cross that bridge when we need to. + assert(!from.exists(substTo contains _)) + assert(!to.exists(substFrom contains _)) + assert(!from.exists(newOwners contains _)) + assert(!to.exists(oldOwners contains _)) + new TreeTypeMap( + typeMap, + treeMap, + from ++ oldOwners, + to ++ newOwners, + from ++ substFrom, + to ++ substTo) + } + + /** Apply `typeMap` and `ownerMap` to given symbols `syms` + * and return a treemap that contains the substitution + * between original and mapped symbols. + */ + def withMappedSyms(syms: List[Symbol]): TreeTypeMap = { + val mapped = ctx.mapSymbols(syms, this) + withSubstitution(syms, mapped) + } +} diff --git a/src/dotty/tools/dotc/ast/Trees.scala b/src/dotty/tools/dotc/ast/Trees.scala index e19221841dd0..b63f0ad8cc03 100644 --- a/src/dotty/tools/dotc/ast/Trees.scala +++ b/src/dotty/tools/dotc/ast/Trees.scala @@ -131,6 +131,29 @@ object Trees { found } } + + /** The path from this node to `that` node, represented + * as a list starting with `this`, ending with`that` where + * every node is a child of its predecessor. + * Nil if no such path exists. + */ + def pathTo(that: Positioned): List[Positioned] = { + def childPath(it: Iterator[Any]): List[Positioned] = + if (it.hasNext) { + val cpath = it.next match { + case x: Positioned => x.pathTo(that) + case xs: List[_] => childPath(xs.iterator) + case _ => Nil + } + if (cpath.nonEmpty) cpath else childPath(it) + } else Nil + if (this eq that) this :: Nil + else if (this.envelope contains that.pos) { + val cpath = childPath(productIterator) + if (cpath.nonEmpty) this :: cpath else Nil + } + else Nil + } } /** Modifiers and annotations for definitions @@ -221,17 +244,21 @@ object Trees { private[this] var myTpe: T = _ /** Destructively set the type of the tree. This should be called only when it is known that - * it is safe under sharing to do so. One user-case is in the withType method below + * it is safe under sharing to do so. One use-case is in the withType method below * which implements copy-on-write. Another use-case is in method interpolateAndAdapt in Typer, * where we overwrite with a simplified version of the type itself. */ - private[dotc] def overwriteType(tpe: T) = myTpe = tpe + private[dotc] def overwriteType(tpe: T) = { + if (this.isInstanceOf[Template[_]]) assert(tpe.isInstanceOf[WithFixedSym], s"$this <--- $tpe") + myTpe = tpe + } /** The type of the tree. In case of an untyped tree, * an UnAssignedTypeException is thrown. (Overridden by empty trees) */ def tpe: T @uncheckedVariance = { - if (myTpe == null) throw new UnAssignedTypeException(this) + if (myTpe == null) + throw new UnAssignedTypeException(this) myTpe } @@ -361,7 +388,7 @@ object Trees { type ThisTree[-T >: Untyped] <: DenotingTree[T] override def denot(implicit ctx: Context) = tpe match { case tpe: NamedType => tpe.denot - case ThisType(cls) => cls.denot + case tpe: ThisType => tpe.cls.denot case _ => NoDenotation } } @@ -422,7 +449,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 +460,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) @@ -443,6 +470,15 @@ object Trees { case class This[-T >: Untyped] private[ast] (qual: TypeName) extends DenotingTree[T] with TermTree[T] { type ThisTree[-T >: Untyped] = This[T] + // Denotation of a This tree is always the udnerlying class; needs correction for modules. + override def denot(implicit ctx: Context): Denotation = { + tpe match { + case tpe @ TermRef(pre, _) if tpe.symbol is Module => + tpe.symbol.moduleClass.denot.asSeenFrom(pre) + case _ => + super.denot + } + } } /** C.super[mix], where qual = C.this */ @@ -620,7 +656,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 +702,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 */ @@ -683,6 +719,7 @@ object Trees { * @param implicits Any implicit parameters passed to the unapply after the selector * @param patterns The argument patterns in the pattern match. * + * It is typed with same type as first `fun` argument * Given a match selector `sel` a pattern UnApply(fun, implicits, patterns) is roughly translated as follows * * val result = fun(sel)(implicits) @@ -697,7 +734,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) } @@ -705,7 +742,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 +754,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[_]] @@ -899,19 +936,17 @@ object Trees { case ys => Thicket(ys) } - // ----- Position handling ----------------------------------------- - - def foreachSubTreeOf(tree: Tree)(f: Tree => Unit): Unit = { - val traverser = new TreeTraverser { - def traverse(tree: Tree) = foldOver(f(tree), tree) - } - traverser.traverse(tree) - } - // ----- Helper classes for copying, transforming, accumulating ----------------- 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] @@ -919,179 +954,200 @@ 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)(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)) 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])(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)) } - 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)) } - 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)) } - 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)) } - 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)) } - 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)) } - 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)) } - 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)) } - 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)) } - 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)) 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)(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)) } - 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. 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)(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) + 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) + 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) } abstract class TreeMap(val cpy: TreeCopier = inst.cpy) { @@ -1100,88 +1156,90 @@ 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)) + val tpt1 = transform(tpt) + val rhs1 = transform(rhs) + cpy.ValDef(tree)(mods, name, transform(tpt1), transform(rhs1)) 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 d38d4695eedf..3fa08b9c6a6f 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -8,6 +8,7 @@ import util.Positions._, Types._, Contexts._, Constants._, Names._, Flags._ import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._, Symbols._ import Denotations._, Decorators._ import config.Printers._ +import typer.Mode import typer.ErrorReporting._ import scala.annotation.tailrec @@ -35,7 +36,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { untpd.SelectFromTypeTree(qualifier, tp.name).withType(tp) def This(cls: ClassSymbol)(implicit ctx: Context): This = - ta.assignType(untpd.This(cls.name)) + untpd.This(cls.name).withType(cls.thisType) def Super(qual: Tree, mix: TypeName, inConstrCall: Boolean)(implicit ctx: Context): Super = ta.assignType(untpd.Super(qual, mix), qual, inConstrCall) @@ -169,7 +170,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { ta.assignType(untpd.ValDef(Modifiers(sym), sym.name, TypeTree(sym.info), rhs), sym) def SyntheticValDef(name: TermName, rhs: Tree)(implicit ctx: Context): ValDef = - ValDef(ctx.newSymbol(ctx.owner, name, Synthetic, rhs.tpe, coord = rhs.pos), rhs) + ValDef(ctx.newSymbol(ctx.owner, name, Synthetic, rhs.tpe.widen, coord = rhs.pos), rhs) def DefDef(sym: TermSymbol, rhs: Tree = EmptyTree)(implicit ctx: Context): DefDef = ta.assignType(DefDef(sym, Function.const(rhs) _), sym) @@ -192,7 +193,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { val params = (paramNames, paramTypes).zipped.map(valueParam) val (paramss, rtp) = valueParamss(tp.instantiate(params map (_.termRef))) (params :: paramss, rtp) - case tp => (Nil, tp) + case tp => (Nil, tp.widenExpr) } val (vparamss, rtp) = valueParamss(mtp) val targs = tparams map (_.typeRef) @@ -237,7 +238,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { val localDummy = ((NoSymbol: Symbol) /: body)(findLocalDummy) .orElse(ctx.newLocalDummy(cls)) val impl = untpd.Template(constr, parents, selfType, newTypeParams ++ body) - .withType(localDummy.termRef) + .withType(localDummy.nonMemberTermRef) ta.assignType(untpd.TypeDef(Modifiers(cls), cls.name, impl), cls) } @@ -252,20 +253,44 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { // ------ Making references ------------------------------------------------------ + def prefixIsElidable(tp: NamedType)(implicit ctx: Context) = { + def test(implicit ctx: Context) = tp.prefix match { + case NoPrefix => + true + case pre: ThisType => + pre.cls.isStaticOwner || + tp.symbol.is(ParamOrAccessor) && ctx.owner.enclosingClass.derivesFrom(pre.cls) + case pre: TermRef => + pre.symbol.is(Module) && pre.symbol.isStatic + case _ => + false + } + try test + catch { // See remark in SymDenotations#accessWithin + case ex: NotDefinedHere => test(ctx.addMode(Mode.FutureDefsOK)) + } + } + + def needsSelect(tp: Type)(implicit ctx: Context) = tp match { + case tp: TermRef => !prefixIsElidable(tp) + case _ => false + } + /** A tree representing the same reference as the given type */ - def ref(tp: NamedType)(implicit ctx: Context): NameTree = - if (tp.symbol.isStatic || tp.prefix == NoPrefix) Ident(tp) + def ref(tp: NamedType)(implicit ctx: Context): Tree = + if (tp.isType) TypeTree(tp) + else if (prefixIsElidable(tp)) Ident(tp) else tp.prefix match { case pre: SingletonType => singleton(pre).select(tp) case pre => SelectFromTypeTree(TypeTree(pre), tp) } // no checks necessary - def ref(sym: Symbol)(implicit ctx: Context): NameTree = + def ref(sym: Symbol)(implicit ctx: Context): Tree = ref(NamedType(sym.owner.thisType, sym.name, sym.denot)) def singleton(tp: Type)(implicit ctx: Context): Tree = tp match { case tp: TermRef => ref(tp) - case ThisType(cls) => This(cls) + case tp: ThisType => This(tp.cls) case SuperType(qual, _) => singleton(qual) case ConstantType(value) => Literal(value) } @@ -320,6 +345,20 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { Thicket(valdef, clsdef) } + def initValue(tpe: Types.Type)(implicit ctx: Context) = { + val tpw = tpe.widen + + if (tpw isRef defn.IntClass) Literal(Constant(0)) + else if (tpw isRef defn.LongClass) Literal(Constant(0L)) + else if (tpw isRef defn.BooleanClass) Literal(Constant(false)) + else if (tpw isRef defn.CharClass) Literal(Constant('\u0000')) + else if (tpw isRef defn.FloatClass) Literal(Constant(0f)) + else if (tpw isRef defn.DoubleClass) Literal(Constant(0d)) + else if (tpw isRef defn.ByteClass) Literal(Constant(0.toByte)) + else if (tpw isRef defn.ShortClass) Literal(Constant(0.toShort)) + else Literal(Constant(null)).select(defn.Any_asInstanceOf).appliedToType(tpe) + } + private class FindLocalDummyAccumulator(cls: ClassSymbol)(implicit ctx: Context) extends TreeAccumulator[Symbol] { def apply(sym: Symbol, tree: Tree) = if (sym.exists) sym @@ -336,6 +375,126 @@ 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 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 = + ta.assignType(untpd.cpy.Apply(tree)(fun, args), fun, args) + // Note: Reassigning the original type if `fun` and `args` have the same types as before + // does not work here: The computed type depends on the widened function type, not + // the function type itself. A treetransform may keep the function type the + // same but its widened type might change. + + override def TypeApply(tree: Tree)(fun: Tree, args: List[Tree])(implicit ctx: Context): TypeApply = + ta.assignType(untpd.cpy.TypeApply(tree)(fun, args), fun, args) + // Same remark as for Apply + + 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 { + 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 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 { + 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 Closure(tree: Tree)(env: List[Tree], meth: Tree, tpt: Tree)(implicit ctx: Context): Closure = + ta.assignType(untpd.cpy.Closure(tree)(env, meth, tpt), meth, tpt) + // Same remark as for Apply + + 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 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 { + 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 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 { + 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 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 = + Try(tree: Tree)(expr, handler, finalizer) } implicit class TreeOps[ThisTree <: tpd.Tree](val tree: ThisTree) extends AnyVal { @@ -364,10 +523,21 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { shallowFold[Option[tpd.Tree]](None)((accum, tree) => if (pred(tree)) Some(tree) else accum) def subst(from: List[Symbol], to: List[Symbol])(implicit ctx: Context): ThisTree = - new TreeTypeMap(typeMap = new ctx.SubstSymMap(from, to)).apply(tree) + new TreeTypeMap(substFrom = from, substTo = to).apply(tree) - def changeOwner(from: Symbol, to: Symbol)(implicit ctx: Context): ThisTree = - new TreeTypeMap(ownerMap = (sym => if (sym == from) to else sym)).apply(tree) + /** Change owner from `from` to `to`. If `from` is a weak owner, also change its + * owner to `to`, and continue until a non-weak owner is reached. + */ + def changeOwner(from: Symbol, to: Symbol)(implicit ctx: Context): ThisTree = { + def loop(from: Symbol, froms: List[Symbol], tos: List[Symbol]): ThisTree = { + if (from.isWeakOwner) loop(from.owner, from :: froms, to :: tos) + else { + //println(i"change owner ${from :: froms}%, % ==> $tos of $tree") + new TreeTypeMap(oldOwners = from :: froms, newOwners = tos).apply(tree) + } + } + loop(from, Nil, to :: Nil) + } def select(name: Name)(implicit ctx: Context): Select = Select(tree, name) @@ -377,7 +547,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def select(sym: Symbol)(implicit ctx: Context): Select = untpd.Select(tree, sym.name).withType( - TermRef.withSig(tree.tpe, sym.name.asTermName, sym.signature, sym.denot.asSeenFrom(tree.tpe))) + TermRef.withSigAndDenot(tree.tpe, sym.name.asTermName, sym.signature, sym.denot.asSeenFrom(tree.tpe))) def selectWithSig(name: Name, sig: Signature)(implicit ctx: Context) = untpd.SelectWithSig(tree, name, sig) @@ -395,7 +565,15 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def appliedToArgss(argss: List[List[Tree]])(implicit ctx: Context): Tree = ((tree: Tree) /: argss)(Apply(_, _)) - def appliedToNone(implicit ctx: Context): Apply = appliedToArgs(Nil) + def appliedToNone(implicit ctx: Context): Tree = appliedToArgs(Nil) + + def appliedIfMethod(implicit ctx: Context): Tree = { + tree.tpe.widen match { + case fntpe: MethodType => appliedToArgs(Nil) + case _ => tree + } + } + def appliedToType(targ: Type)(implicit ctx: Context): Tree = appliedToTypes(targ :: Nil) @@ -405,218 +583,45 @@ 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) - } - - implicit class ListOfTreeDecorator(val xs: List[tpd.Tree]) extends AnyVal { - 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) - } - } + def isInstance(tp: Type)(implicit ctx: Context): Tree = + tree.select(defn.Any_isInstanceOf).appliedToType(tp) - /** 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 - * type of every tree node and to all locally defined symbols - * @param ownerMap A function that translates owners of top-level local symbols - * defined in the mapped tree. - * @param treeMap A transformer that translates all encountered subtrees in - * prefix traversal order. - */ - final class TreeTypeMap( - val typeMap: Type => Type = IdentityTypeMap, - val ownerMap: Symbol => Symbol = identity _, - val treeMap: Tree => Tree = identity _)(implicit ctx: Context) extends RetypingTreeMap { - - override def transform(tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = { - val tree1 = treeMap(tree) - tree1.withType(typeMap(tree1.tpe)) match { - 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)) - 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) - 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) - case tree1 => - super.transform(tree1) - } + def asInstance(tp: Type)(implicit ctx: Context): Tree = { + assert(tp.isValueType, i"bad cast: $tree.asInstanceOf[$tp]") + tree.select(defn.Any_asInstanceOf).appliedToType(tp) } - override def transformStats(trees: List[tpd.Tree])(implicit ctx: Context) = - transformDefs(trees)._2 - - private def transformDefs[TT <: tpd.Tree](trees: List[TT])(implicit ctx: Context): (TreeTypeMap, List[TT]) = { - val tmap = withMappedSyms(ta.localSyms(trees)) - (tmap, tmap.transformSub(trees)) - } + def ensureConforms(tp: Type)(implicit ctx: Context): Tree = + if (tree.tpe <:< tp) tree else asInstance(tp) - private def transformVParamss(vparamss: List[List[ValDef]]): (TreeTypeMap, List[List[ValDef]]) = vparamss match { - case vparams :: rest => - val (tmap1, vparams1) = transformDefs(vparams) - val (tmap2, vparamss2) = tmap1.transformVParamss(rest) - (tmap2, vparams1 :: vparamss2) - case nil => - (this, vparamss) - } + def and(that: Tree)(implicit ctx: Context): Tree = + tree.select(defn.Boolean_&&).appliedTo(that) - def apply[ThisTree <: tpd.Tree](tree: ThisTree): ThisTree = transform(tree).asInstanceOf[ThisTree] + def or(that: Tree)(implicit ctx: Context): Tree = + tree.select(defn.Boolean_||).appliedTo(that) - def apply(annot: Annotation): Annotation = { - val tree1 = apply(annot.tree) - if (tree1 eq annot.tree) annot else ConcreteAnnotation(tree1) - } + // --- Higher order traversal methods ------------------------------- - /** The current tree map composed with a substitution [from -> to] */ - def withSubstitution(from: List[Symbol], to: List[Symbol]): TreeTypeMap = - if (from eq to) this - else new TreeTypeMap( - typeMap andThen (_.substSym(from, to)), - ownerMap andThen { sym => - val idx = from.indexOf(sym) - if (idx >= 0) to(idx) else sym - }, - treeMap) - - /** Apply `typeMap` and `ownerMap` to given symbols `syms` - * and return a treemap that contains the substitution - * between original and mapped symbols. - */ - def withMappedSyms(syms: List[Symbol]): TreeTypeMap = { - val mapped = ctx.mapSymbols(syms, typeMap, ownerMap) - withSubstitution(syms, mapped) + def foreachSubTree(f: Tree => Unit): Unit = { //TODO should go in tpd. + val traverser = new TreeTraverser { + def traverse(tree: Tree) = foldOver(f(tree), tree) + } + traverser.traverse(tree) } - } - /** The variables defined by a pattern, in reverse order of their appearance. */ - def patVars(tree: Tree)(implicit ctx: Context): List[Symbol] = { - val acc = new TreeAccumulator[List[Symbol]] { - def apply(syms: List[Symbol], tree: Tree) = tree match { - case Bind(_, body) => apply(tree.symbol :: syms, body) - case _ => foldOver(syms, tree) + def existsSubTree(p: Tree => Boolean): Boolean = { + val acc = new TreeAccumulator[Boolean] { + def apply(x: Boolean, t: Tree) = x || p(t) || foldOver(x, t) } + acc(false, tree) } - 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) + implicit class ListOfTreeDecorator(val xs: List[tpd.Tree]) extends AnyVal { + def tpes: List[Type] = xs map (_.tpe) } - 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 @@ -646,16 +651,9 @@ 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) + def runtimeCall(name: TermName, args: List[Tree])(implicit ctx: Context): Tree = { + Ident(defn.ScalaRuntimeModule.requiredMethod(name).termRef).appliedToArgs(args) + } // ensure that constructors are fully applied? // ensure that normal methods are fully applied? diff --git a/src/dotty/tools/dotc/ast/untpd.scala b/src/dotty/tools/dotc/ast/untpd.scala index 44f34093235d..9ac01df9cc93 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) } @@ -408,6 +408,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { this(this(this(x, pats), tpt), rhs) case tree: PolyTypeDef => this(this(x, tree.tparams), tree.rhs) + case TypedSplice(tree) => + this(x, tree) case _ => super.foldOver(x, tree) } diff --git a/src/dotty/tools/dotc/config/Config.scala b/src/dotty/tools/dotc/config/Config.scala index 906d173804e7..b17a5630f98a 100644 --- a/src/dotty/tools/dotc/config/Config.scala +++ b/src/dotty/tools/dotc/config/Config.scala @@ -21,13 +21,10 @@ object Config { */ final val checkNoDoubleBindings = true - /** Throw an exception if a deep subtype recursion is detected */ - final val flagDeepSubTypeRecursions = true - /** Show subtype traces for all deep subtype recursions */ final val traceDeepSubTypeRecursions = false - final val verboseExplainSubtype = false + final val verboseExplainSubtype = true /** When set, use new signature-based matching. * Advantantage of doing so: It's supposed to be faster @@ -48,4 +45,7 @@ object Config { * variance of the underlying lambda class. */ final val checkLambdaVariance = false + + /** Check that certain types cannot be created in erasedTypes phases */ + final val checkUnerased = true } \ No newline at end of file diff --git a/src/dotty/tools/dotc/config/Printers.scala b/src/dotty/tools/dotc/config/Printers.scala index 5bfe1d0b6829..3926be59b121 100644 --- a/src/dotty/tools/dotc/config/Printers.scala +++ b/src/dotty/tools/dotc/config/Printers.scala @@ -23,7 +23,9 @@ object Printers { val completions = noPrinter val gadts = noPrinter val hk = noPrinter + val variances = noPrinter val incremental = noPrinter val config = noPrinter val transforms = noPrinter + val cyclicErrors = noPrinter } \ No newline at end of file diff --git a/src/dotty/tools/dotc/config/ScalaSettings.scala b/src/dotty/tools/dotc/config/ScalaSettings.scala index aab2942bdfe4..f23fb620106b 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.") @@ -107,6 +108,7 @@ class ScalaSettings extends Settings.SettingGroup { val debug = BooleanSetting("-Ydebug", "Increase the quantity of debugging output.") val debugNames = BooleanSetting("-YdebugNames", "Show name-space indicators when printing names") val debugTrace = BooleanSetting("-YdebugTrace", "Trace core operations") + val debugFlags = BooleanSetting("-YdebugFlags", "Print all flags of definitions") //val doc = BooleanSetting ("-Ydoc", "Generate documentation") val termConflict = ChoiceSetting("-Yresolve-term-conflict", "strategy", "Resolve term conflicts", List("package", "object", "error"), "error") val inline = BooleanSetting("-Yinline", "Perform inlining when possible.") @@ -130,7 +132,7 @@ class ScalaSettings extends Settings.SettingGroup { val Ydumpclasses = StringSetting("-Ydump-classes", "dir", "Dump the generated bytecode to .class files (useful for reflective compilation that utilizes in-memory classloaders).", "") val Ynosqueeze = BooleanSetting("-Yno-squeeze", "Disable creation of compact code in matching.") val YstopAfter = PhasesSetting("-Ystop-after", "Stop after") withAbbreviation ("-stop") // backward compat - val YstopBefore = PhasesSetting("-Ystop-before", "Stop before", "erasure") // stop before erasure as long as we have not debugged it fully + val YstopBefore = PhasesSetting("-Ystop-before", "Stop before") // stop before erasure as long as we have not debugged it fully val refinementMethodDispatch = ChoiceSetting("-Ystruct-dispatch", "policy", "structural method dispatch policy", List("no-cache", "mono-cache", "poly-cache", "invoke-dynamic"), "poly-cache") val Yrangepos = BooleanSetting("-Yrangepos", "Use range positions for syntax trees.") val Ybuilderdebug = ChoiceSetting("-Ybuilder-debug", "manager", "Compile using the specified build manager.", List("none", "refined", "simple"), "none") @@ -144,6 +146,7 @@ class ScalaSettings extends Settings.SettingGroup { val YshowSuppressedErrors = BooleanSetting("-Yshow-suppressed-errors", "Also show follow-on errors and warnings that are normally supressed.") val Yheartbeat = BooleanSetting("-Yheartbeat", "show heartbeat stack trace of compiler operations.") val Yprintpos = BooleanSetting("-Yprintpos", "show tree positions") + val YnoDeepSubtypes = BooleanSetting("-YnoDeepSubtypes", "throw an exception on deep subtyping call stacks") def stop = YstopAfter /** Area-specific debug output. diff --git a/src/dotty/tools/dotc/config/ScalaVersion.scala b/src/dotty/tools/dotc/config/ScalaVersion.scala new file mode 100644 index 000000000000..7d45854417c6 --- /dev/null +++ b/src/dotty/tools/dotc/config/ScalaVersion.scala @@ -0,0 +1,183 @@ +/* @author James Iry + */ +package dotty.tools.dotc.config + +import scala.util.{Try, Success, Failure} + +/** + * Represents a single Scala version in a manner that + * supports easy comparison and sorting. + */ +sealed abstract class ScalaVersion extends Ordered[ScalaVersion] { + def unparse: String +} + +/** + * A scala version that sorts higher than all actual versions + */ +case object NoScalaVersion extends ScalaVersion { + def unparse = "none" + + def compare(that: ScalaVersion): Int = that match { + case NoScalaVersion => 0 + case _ => 1 + } +} + +/** + * A specific Scala version, not one of the magic min/max versions. An SpecificScalaVersion + * may or may not be a released version - i.e. this same class is used to represent + * final, release candidate, milestone, and development builds. The build argument is used + * to segregate builds + */ +case class SpecificScalaVersion(major: Int, minor: Int, rev: Int, build: ScalaBuild) extends ScalaVersion { + def unparse = s"${major}.${minor}.${rev}.${build.unparse}" + + def compare(that: ScalaVersion): Int = that match { + case SpecificScalaVersion(thatMajor, thatMinor, thatRev, thatBuild) => + // this could be done more cleanly by importing scala.math.Ordering.Implicits, but we have to do these + // comparisons a lot so I'm using brute force direct style code + if (major < thatMajor) -1 + else if (major > thatMajor) 1 + else if (minor < thatMinor) -1 + else if (minor > thatMinor) 1 + else if (rev < thatRev) -1 + else if (rev > thatRev) 1 + else build compare thatBuild + case AnyScalaVersion => 1 + case NoScalaVersion => -1 + } +} + +/** + * A Scala version that sorts lower than all actual versions + */ +case object AnyScalaVersion extends ScalaVersion { + def unparse = "any" + + def compare(that: ScalaVersion): Int = that match { + case AnyScalaVersion => 0 + case _ => -1 + } +} + +/** + * Methods for parsing ScalaVersions + */ +object ScalaVersion { + private val dot = "\\." + private val dash = "\\-" + private def not(s:String) = s"[^${s}]" + private val R = s"((${not(dot)}*)(${dot}(${not(dot)}*)(${dot}(${not(dash)}*)(${dash}(.*))?)?)?)".r + + def parse(versionString : String): Try[ScalaVersion] = { + def failure = Failure(new NumberFormatException( + s"There was a problem parsing ${versionString}. " + + "Versions should be in the form major[.minor[.revision]] " + + "where each part is a positive number, as in 2.10.1. " + + "The minor and revision parts are optional." + )) + + def toInt(s: String) = s match { + case null | "" => 0 + case _ => s.toInt + } + + def isInt(s: String) = Try(toInt(s)).isSuccess + + import ScalaBuild._ + + def toBuild(s: String) = s match { + case null | "FINAL" => Final + case s if (s.toUpperCase.startsWith("RC") && isInt(s.substring(2))) => RC(toInt(s.substring(2))) + case s if (s.toUpperCase.startsWith("M") && isInt(s.substring(1))) => Milestone(toInt(s.substring(1))) + case _ => Development(s) + } + + try versionString match { + case "" | "any" => Success(AnyScalaVersion) + case "none" => Success(NoScalaVersion) + case R(_, majorS, _, minorS, _, revS, _, buildS) => + Success(SpecificScalaVersion(toInt(majorS), toInt(minorS), toInt(revS), toBuild(buildS))) + case _ => failure + } catch { + case e: NumberFormatException => failure + } + } + + /** + * The version of the compiler running now + */ + val current = parse(util.Properties.versionNumberString).get +} + +/** + * Represents the data after the dash in major.minor.rev-build + */ +abstract class ScalaBuild extends Ordered[ScalaBuild] { + /** + * Return a version of this build information that can be parsed back into the + * same ScalaBuild + */ + def unparse: String +} + +object ScalaBuild { + + /** A development, test, nightly, snapshot or other "unofficial" build + */ + case class Development(id: String) extends ScalaBuild { + def unparse = s"-${id}" + + def compare(that: ScalaBuild) = that match { + // sorting two development builds based on id is reasonably valid for two versions created with the same schema + // otherwise it's not correct, but since it's impossible to put a total ordering on development build versions + // this is a pragmatic compromise + case Development(thatId) => id compare thatId + // assume a development build is newer than anything else, that's not really true, but good luck + // mapping development build versions to other build types + case _ => 1 + } + } + + /** A final build + */ + case object Final extends ScalaBuild { + def unparse = "" + + def compare(that: ScalaBuild) = that match { + case Final => 0 + // a final is newer than anything other than a development build or another final + case Development(_) => -1 + case _ => 1 + } + } + + /** A candidate for final release + */ + case class RC(n: Int) extends ScalaBuild { + def unparse = s"-RC${n}" + + def compare(that: ScalaBuild) = that match { + // compare two rcs based on their RC numbers + case RC(thatN) => n - thatN + // an rc is older than anything other than a milestone or another rc + case Milestone(_) => 1 + case _ => -1 + } + } + + /** An intermediate release + */ + case class Milestone(n: Int) extends ScalaBuild { + def unparse = s"-M${n}" + + def compare(that: ScalaBuild) = that match { + // compare two milestones based on their milestone numbers + case Milestone(thatN) => n - thatN + // a milestone is older than anything other than another milestone + case _ => -1 + + } + } +} diff --git a/src/dotty/tools/dotc/config/Settings.scala b/src/dotty/tools/dotc/config/Settings.scala index 17d4d67125e8..531c49bfbc52 100644 --- a/src/dotty/tools/dotc/config/Settings.scala +++ b/src/dotty/tools/dotc/config/Settings.scala @@ -18,6 +18,7 @@ object Settings { val IntTag = ClassTag.Int val StringTag = ClassTag(classOf[String]) val ListTag = ClassTag(classOf[List[_]]) + val VersionTag = ClassTag(classOf[ScalaVersion]) class SettingsState(initialValues: Seq[Any]) { private var values = ArrayBuffer(initialValues: _*) @@ -132,6 +133,11 @@ object Settings { case _: NumberFormatException => fail(s"$arg2 is not an integer argument for $name", args2) } + case (VersionTag, _) => + ScalaVersion.parse(argRest) match { + case Success(v) => update(v, args) + case Failure(ex) => fail(ex.getMessage, args) + } case (_, Nil) => missingArg } @@ -246,5 +252,8 @@ object Settings { def PrefixSetting(name: String, pre: String, descr: String): Setting[List[String]] = publish(Setting(name, descr, Nil, prefix = pre)) + + def VersionSetting(name: String, descr: String, default: ScalaVersion = NoScalaVersion): Setting[ScalaVersion] = + publish(Setting(name, descr, default)) } } \ No newline at end of file diff --git a/src/dotty/tools/dotc/core/Annotations.scala b/src/dotty/tools/dotc/core/Annotations.scala index b4b7ebd24e2a..92b28a1934ad 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 { @@ -59,7 +68,8 @@ object Annotations { deferred(atp.classSymbol, implicit ctx => New(atp, args)) def makeAlias(sym: TermSymbol)(implicit ctx: Context) = - apply(defn.AliasAnnot, List(Ident(TermRef.withSig(sym.owner.thisType, sym.name, sym.signature, sym)))) + apply(defn.AliasAnnot, List( + ref(TermRef.withSigAndDenot(sym.owner.thisType, sym.name, sym.signature, sym)))) def makeChild(sym: Symbol)(implicit ctx: Context) = apply(defn.ChildAnnot.typeRef.appliedTo(sym.owner.thisType.select(sym.name, sym)), Nil) @@ -69,4 +79,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/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index b662326c5ef2..6b9b1dec7a08 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -12,6 +12,7 @@ import Scopes._ import NameOps._ import Uniques._ import SymDenotations._ +import Flags.ParamAccessor import util.Positions._ import ast.Trees._ import ast.untpd @@ -259,6 +260,53 @@ object Contexts { c } + /** The context for a supercall. This context is used for elaborating + * the parents of a class and their arguments. + * The context is computed from the current class context. It has + * + * - as owner: The primary constructor of the class + * - as outer context: The context enclosing the class context + * - as scope: The parameter accessors in the class context + * - with additional mode: InSuperCall + * + * The reasons for this peculiar choice of attributes are as follows: + * + * - The constructor must be the owner, because that's where any local methods or closures + * should go. + * - The context may not see any class members (inherited or defined), and should + * instead see definitions defined in the outer context which might be shadowed by + * such class members. That's why the outer context must be the outer context of the class. + * - At the same time the context should see the parameter accessors of the current class, + * that's why they get added to the local scope. An alternative would have been to have the + * context see the constructor parameters instead, but then we'd need a final substitution step + * from constructor parameters to class paramater accessors. + */ + def superCallContext: Context = { + val locals = newScopeWith(owner.decls.filter(_ is ParamAccessor).toList: _*) + superOrThisCallContext(owner.primaryConstructor, locals) + } + + /** The context for the arguments of a this(...) constructor call. + * The context is computed from the local auxiliary constructor context. + * It has + * + * - as owner: The auxiliary constructor + * - as outer context: The context enclosing the enclosing class context + * - as scope: The parameters of the auxiliary constructor. + */ + def thisCallArgContext: Context = { + assert(owner.isClassConstructor) + val constrCtx = outersIterator.dropWhile(_.outer.owner == owner).next + var classCtx = outersIterator.dropWhile(!_.isClassDefContext).next + classCtx.superOrThisCallContext(owner, constrCtx.scope).setTyperState(typerState) + } + + /** The super= or this-call context with given owner and locals. */ + private def superOrThisCallContext(owner: Symbol, locals: Scope): FreshContext = { + assert(isClassDefContext) + outer.fresh.setOwner(owner).setScope(locals).setMode(ctx.mode | Mode.InSuperCall) + } + /** The current source file; will be derived from current * compilation unit. */ diff --git a/src/dotty/tools/dotc/core/Decorators.scala b/src/dotty/tools/dotc/core/Decorators.scala index cd7b4689689f..99af4d0cb42e 100644 --- a/src/dotty/tools/dotc/core/Decorators.scala +++ b/src/dotty/tools/dotc/core/Decorators.scala @@ -130,8 +130,8 @@ 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 _ => names exists (n => n == "all" || phase.name.startsWith(n)) + case phase: TreeTransformer => phase.transformations.exists(trans => containsPhase(trans.phase)) + case _ => names exists (n => n == "all" || phase.phaseName.startsWith(n)) } } @@ -157,10 +157,15 @@ object Decorators { (treatSingleArg(arg), suffix) } - def treatSingleArg(arg: Any) : Any = arg match { - case arg: Showable => arg.show - case _ => arg - } + def treatSingleArg(arg: Any) : Any = + try + arg match { + case arg: Showable => arg.show + case _ => arg + } + catch { + case ex: Exception => s"(missing due to $ex)" + } val prefix :: suffixes = sc.parts.toList val (args1, suffixes1) = (args, suffixes).zipped.map(treatArg(_, _)).unzip diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 286d1437f553..b74506caf45a 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -4,6 +4,7 @@ package core import Types._, Contexts._, Symbols._, Denotations._, SymDenotations._, StdNames._, Names._ import Flags._, Scopes._, Decorators._, NameOps._, util.Positions._ +import TypeApplications._ import pickling.UnPickler.ensureConstructor import scala.annotation.{ switch, meta } import scala.collection.{ mutable, immutable } @@ -103,9 +104,13 @@ class Definitions { lazy val EmptyPackageVal = EmptyPackageClass.sourceModule.entered lazy val ScalaPackageVal = ctx.requiredPackage("scala") + lazy val ScalaMathPackageVal = ctx.requiredPackage("scala.math") lazy val ScalaPackageClass = ScalaPackageVal.moduleClass.asClass 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 @@ -177,8 +182,16 @@ 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) @@ -191,22 +204,33 @@ class Definitions { ScalaPackageClass, tpnme.Singleton, Trait | Interface | Final, List(AnyClass.typeRef), EmptyScope) lazy val SeqClass: ClassSymbol = ctx.requiredClass("scala.collection.Seq") + lazy val Seq_apply = ctx.requiredMethod(SeqClass, nme.apply) + lazy val Seq_head = ctx.requiredMethod(SeqClass, nme.head) lazy val ArrayClass: ClassSymbol = ctx.requiredClass("scala.Array") lazy val Array_apply = ctx.requiredMethod(ArrayClass, nme.apply) lazy val Array_update = ctx.requiredMethod(ArrayClass, nme.update) lazy val Array_length = ctx.requiredMethod(ArrayClass, nme.length) lazy val Array_clone = ctx.requiredMethod(ArrayClass, nme.clone_) + lazy val traversableDropMethod = ctx.requiredMethod(ScalaRuntimeClass, nme.drop) + lazy val uncheckedStableClass: ClassSymbol = ctx.requiredClass("scala.annotation.unchecked.uncheckedStable") 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) lazy val CharClass = valueClassSymbol("scala.Char", BoxedCharClass, java.lang.Character.TYPE, CharEnc) lazy val IntClass = valueClassSymbol("scala.Int", BoxedIntClass, java.lang.Integer.TYPE, IntEnc) + lazy val Int_- = IntClass.requiredMethod(nme.MINUS, List(IntType)) + lazy val Int_+ = IntClass.requiredMethod(nme.PLUS, List(IntType)) + lazy val Int_/ = IntClass.requiredMethod(nme.DIV, List(IntType)) + lazy val Int_* = IntClass.requiredMethod(nme.MUL, List(IntType)) + lazy val Int_== = IntClass.requiredMethod(nme.EQ, List(IntType)) + lazy val Int_>= = IntClass.requiredMethod(nme.GE, List(IntType)) + lazy val Int_<= = IntClass.requiredMethod(nme.LE, List(IntType)) lazy val LongClass = valueClassSymbol("scala.Long", BoxedLongClass, java.lang.Long.TYPE, LongEnc) lazy val Long_XOR_Long = LongClass.info.member(nme.XOR).requiredSymbol( x => (x is Method) && (x.info.firstParamTypes.head isRef defn.LongClass) @@ -251,6 +275,8 @@ class Definitions { lazy val JavaCloneableClass = ctx.requiredClass("java.lang.Cloneable") lazy val StringBuilderClass = ctx.requiredClass("scala.collection.mutable.StringBuilder") lazy val NullPointerExceptionClass = ctx.requiredClass(jnme.NPException) + lazy val MatchErrorClass = ctx.requiredClass("scala.MatchError") + lazy val MatchErrorType = MatchErrorClass.typeRef lazy val StringAddClass = ctx.requiredClass("scala.runtime.StringAdd") @@ -269,6 +295,8 @@ 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 Product_productArity = ProductClass.requiredMethod(nme.productArity) lazy val LanguageModuleClass = ctx.requiredModule("dotty.language").moduleClass.asClass // Annotation base classes @@ -292,6 +320,7 @@ class Definitions { lazy val ScalaSignatureAnnot = ctx.requiredClass("scala.reflect.ScalaSignature") lazy val ScalaLongSignatureAnnot = ctx.requiredClass("scala.reflect.ScalaLongSignature") lazy val DeprecatedAnnot = ctx.requiredClass("scala.deprecated") + lazy val MigrationAnnot = ctx.requiredClass("scala.annotation.migration") lazy val AnnotationDefaultAnnot = ctx.requiredClass("dotty.annotation.internal.AnnotationDefault") lazy val ThrowsAnnot = ctx.requiredClass("scala.throws") lazy val UncheckedAnnot = ctx.requiredClass("scala.unchecked") @@ -313,7 +342,6 @@ class Definitions { def NothingType: Type = NothingClass.typeRef def NullType: Type = NullClass.typeRef def SeqType: Type = SeqClass.typeRef - def ObjectArrayType = ArrayType(ObjectType) def UnitType: Type = UnitClass.typeRef def BooleanType: Type = BooleanClass.typeRef @@ -345,9 +373,9 @@ class Definitions { sym.owner.linkedClass.typeRef object FunctionType { - def apply(args: List[Type], resultType: Type) = + def apply(args: List[Type], resultType: Type)(implicit ctx: Context) = FunctionClass(args.length).typeRef.appliedTo(args ::: resultType :: Nil) - def unapply(ft: Type)/*: Option[(List[Type], Type)]*/ = { + def unapply(ft: Type)(implicit ctx: Context)/*: Option[(List[Type], Type)]*/ = { // -language:keepUnions difference: unapply needs result type because inferred type // is Some[(List[Type], Type)] | None, which is not a legal unapply type. val tsym = ft.typeSymbol @@ -360,18 +388,18 @@ class Definitions { } object ArrayType { - def apply(elem: Type) = + def apply(elem: Type)(implicit ctx: Context) = ArrayClass.typeRef.appliedTo(elem :: Nil) - def unapply(tp: Type) = tp.dealias match { + def unapply(tp: Type)(implicit ctx: Context) = tp.dealias match { case at: RefinedType if (at isRef ArrayClass) && at.argInfos.length == 1 => Some(at.argInfos.head) case _ => None } } object MultiArrayType { - def apply(elem: Type, ndims: Int): Type = + def apply(elem: Type, ndims: Int)(implicit ctx: Context): Type = if (ndims == 0) elem else ArrayType(apply(elem, ndims - 1)) - def unapply(tp: Type): Option[(Type, Int)] = tp match { + def unapply(tp: Type)(implicit ctx: Context): Option[(Type, Int)] = tp match { case ArrayType(elemtp) => elemtp match { case MultiArrayType(finalElemTp, n) => Some(finalElemTp, n + 1) @@ -389,7 +417,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 @@ -400,17 +428,18 @@ class Definitions { lazy val PhantomClasses = Set[Symbol](AnyClass, AnyValClass, NullClass, NothingClass) - lazy val asInstanceOfMethods = Set[Symbol](Any_asInstanceOf) - lazy val isInstanceOfMethods = Set[Symbol](Any_isInstanceOf) - lazy val typeTestsOrCasts = asInstanceOfMethods ++ isInstanceOfMethods - 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)) } + def tupleType(elems: List[Type]) = { + TupleClass(elems.size).typeRef.appliedTo(elems) + } + def isProductSubType(tp: Type)(implicit ctx: Context) = (tp derivesFrom ProductClass) && tp.baseClasses.exists(ProductClasses contains _) @@ -524,6 +553,15 @@ class Definitions { vcls } + /** The classes for which a Ref type exists. */ + lazy val refClasses: collection.Set[Symbol] = ScalaNumericValueClasses + BooleanClass + ObjectClass + + lazy val refClass: Map[Symbol, Symbol] = + refClasses.map(rc => rc -> ctx.requiredClass(s"scala.runtime.${rc.name}Ref")).toMap + + lazy val volatileRefClass: Map[Symbol, Symbol] = + refClasses.map(rc => rc -> ctx.requiredClass(s"scala.runtime.Volatile${rc.name}Ref")).toMap + def wrapArrayMethodName(elemtp: Type): TermName = { val cls = elemtp.classSymbol if (cls.isPrimitiveValueClass) nme.wrapXArray(cls.name) diff --git a/src/dotty/tools/dotc/core/Denotations.scala b/src/dotty/tools/dotc/core/Denotations.scala index 022230ff81a5..9970c5948274 100644 --- a/src/dotty/tools/dotc/core/Denotations.scala +++ b/src/dotty/tools/dotc/core/Denotations.scala @@ -13,11 +13,12 @@ import Periods._ import Flags._ import DenotTransformers._ import Decorators._ -import transform.Erasure +import dotc.transform.Erasure import printing.Texts._ import printing.Printer import io.AbstractFile import config.Config +import typer.Mode import util.common._ import collection.mutable.ListBuffer import Decorators.SymbolIteratorDecorator @@ -190,6 +191,11 @@ object Denotations { def requiredMethod(name: PreName)(implicit ctx: Context): TermSymbol = info.member(name.toTermName).requiredSymbol(_ is Method).asTerm + def requiredMethod(name: PreName, argTypes: List[Type])(implicit ctx: Context): TermSymbol = + info.member(name.toTermName).requiredSymbol(x=> + (x is Method) && x.info.paramTypess == List(argTypes) + ).asTerm + def requiredValue(name: PreName)(implicit ctx: Context): TermSymbol = info.member(name.toTermName).requiredSymbol(_.info.isParameterless).asTerm @@ -202,7 +208,7 @@ object Denotations { def matchingDenotation(site: Type, targetType: Type)(implicit ctx: Context): SingleDenotation = if (isOverloaded) atSignature(targetType.signature).matchingDenotation(site, targetType) - else if (exists && !(site.memberInfo(symbol) matches targetType)) + else if (exists && !site.memberInfo(symbol).matchesLoosely(targetType)) NoDenotation else asSingleDenotation @@ -231,13 +237,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 { @@ -421,7 +430,7 @@ object Denotations { * and at signature `NotAMethod`. */ def valRef(implicit ctx: Context): TermRef = - TermRef.withSig(symbol.owner.thisType, symbol.name.asTermName, Signature.NotAMethod, this) + TermRef.withSigAndDenot(symbol.owner.thisType, symbol.name.asTermName, Signature.NotAMethod, this) /** The TermRef representing this term denotation at its original location * at the denotation's signature. @@ -429,7 +438,7 @@ object Denotations { * denotation via a call to `info`. */ def termRefWithSig(implicit ctx: Context): TermRef = - TermRef.withSig(symbol.owner.thisType, symbol.name.asTermName, signature, this) + TermRef.withSigAndDenot(symbol.owner.thisType, symbol.name.asTermName, signature, this) /** The NamedType representing this denotation at its original location. * Same as either `typeRef` or `termRefWithSig` depending whether this denotes a type or not. @@ -492,7 +501,7 @@ object Denotations { d.validFor = Period(ctx.period.runId, d.validFor.firstPhaseId, d.validFor.lastPhaseId) d = d.nextInRun } while (d ne denot) - initial.syncWithParents + syncWithParents case _ => if (coveredInterval.containsPhaseId(ctx.phaseId)) staleSymbolError else NoDenotation @@ -521,7 +530,7 @@ object Denotations { assert(false) } - if (valid.runId != currentPeriod.runId) bringForward.current + if (valid.runId != currentPeriod.runId) initial.bringForward.current else { var cur = this if (currentPeriod.code > valid.code) { @@ -550,7 +559,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 @@ -571,7 +582,11 @@ object Denotations { //println(s"searching: $cur at $currentPeriod, valid for ${cur.validFor}") cur = cur.nextInRun cnt += 1 - assert(cnt <= MaxPossiblePhaseId, demandOutsideDefinedMsg) + if (cnt > MaxPossiblePhaseId) + if (ctx.mode is Mode.FutureDefsOK) + return current(ctx.withPhase(coveredInterval.firstPhaseId)) + else + throw new NotDefinedHere(demandOutsideDefinedMsg) } cur } @@ -885,5 +900,10 @@ object Denotations { util.Stats.record("stale symbol") override def getMessage() = msg } + + class NotDefinedHere(msg: => String) extends Exception { + util.Stats.record("not defined here") + override def getMessage() = msg + } } diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index c527cef62209..e34483f8f75f 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -190,6 +190,8 @@ object Flags { /** Labeled with `private` modifier */ final val Private = commonFlag(2, "private") + final val PrivateTerm = Private.toTermFlags + final val PrivateType = Private.toTypeFlags /** Labeled with `protected` modifier */ final val Protected = commonFlag(3, "protected") @@ -236,9 +238,6 @@ object Flags { /** A mutable var */ final val Mutable = termFlag(12, "mutable") - /** Class symbol is defined in this/superclass constructor. */ - final val InConstructor = typeFlag(12, "") - /** Symbol is local to current class (i.e. private[this] or protected[this] * pre: Private or Protected are also set */ @@ -274,88 +273,98 @@ object Flags { /** Symbol's name is expanded */ final val ExpandedName = commonFlag(19, "") - /** A covariant type variable */ - final val CovariantCommon = commonFlag(20, "") - final val Covariant = CovariantCommon.toTypeFlags + /** A covariant type variable / an outer accessor */ + final val CovariantOrOuter = commonFlag(20, "") + final val Covariant = typeFlag(20, "") + final val OuterAccessor = termFlag(20, "") - final val ContravariantCommon = commonFlag(21, "") - final val Contravariant = ContravariantCommon.toTypeFlags + /** A contravariant type variable / a label method */ + final val ContravariantOrLabel = commonFlag(21, "") + final val Contravariant = typeFlag(21, "") + final val Label = termFlag(21, "