Skip to content

Commit

Permalink
Merge pull request #1154 from dotty-staging/add-rewrite
Browse files Browse the repository at this point in the history
First steps towards rewriting from Scala2 in dotty
  • Loading branch information
odersky committed Mar 18, 2016
2 parents 16f0bea + 6c18e37 commit cdbc163
Show file tree
Hide file tree
Showing 24 changed files with 433 additions and 84 deletions.
2 changes: 2 additions & 0 deletions src/dotty/tools/dotc/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import io.PlainFile
import util.{SourceFile, NoSource, Stats, SimpleMap}
import reporting.Reporter
import transform.TreeChecker
import rewrite.Rewrites
import java.io.{BufferedWriter, OutputStreamWriter}
import scala.reflect.io.VirtualFile
import scala.util.control.NonFatal
Expand Down Expand Up @@ -64,6 +65,7 @@ class Run(comp: Compiler)(implicit ctx: Context) {
foreachUnit(printTree)
ctx.informTime(s"$phase ", start)
}
if (!ctx.reporter.hasErrors) Rewrites.writeBack()
}

private def printTree(ctx: Context) = {
Expand Down
38 changes: 22 additions & 16 deletions src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ object desugar {
val tparam = cpy.TypeDef(tdef)(name = tdef.name.expandedName(ctx.owner))
.withMods(tdef.mods &~ PrivateLocal | ExpandedName)
val alias = cpy.TypeDef(tdef)(rhs = refOfDef(tparam), tparams = Nil)
.withFlags(PrivateLocalParamAccessor | Synthetic | tdef.mods.flags & VarianceFlags)
.withMods(tdef.mods & VarianceFlags | PrivateLocalParamAccessor | Synthetic)
Thicket(tparam, alias)
}
else tdef
Expand All @@ -237,15 +237,15 @@ object desugar {
@sharable private val synthetic = Modifiers(Synthetic)

private def toDefParam(tparam: TypeDef): TypeDef =
tparam.withFlags(Param)
tparam.withMods(tparam.rawMods & EmptyFlags | Param)
private def toDefParam(vparam: ValDef): ValDef =
vparam.withFlags(Param | vparam.rawMods.flags & Implicit)
vparam.withMods(vparam.rawMods & Implicit | Param)

/** The expansion of a class definition. See inline comments for what is involved */
def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = {
val TypeDef(name, impl @ Template(constr0, parents, self, _)) = cdef
val mods = cdef.mods
val accessFlags = (mods.flags & AccessFlags).toCommonFlags
val companionMods = mods.withFlags((mods.flags & AccessFlags).toCommonFlags)

val (constr1, defaultGetters) = defDef(constr0, isPrimaryConstructor = true) match {
case meth: DefDef => (meth, Nil)
Expand Down Expand Up @@ -364,7 +364,7 @@ object desugar {
moduleDef(
ModuleDef(
name.toTermName, Template(emptyConstructor, parentTpt :: Nil, EmptyValDef, defs))
.withFlags(Synthetic | accessFlags))
.withMods(companionMods | Synthetic))
.withPos(cdef.pos).toList

// The companion object definitions, if a companion is needed, Nil otherwise.
Expand Down Expand Up @@ -421,10 +421,9 @@ object desugar {
// implicit wrapper is typechecked in same scope as constructor, so
// we can reuse the constructor parameters; no derived params are needed.
DefDef(name.toTermName, constrTparams, constrVparamss, classTypeRef, creatorExpr)
.withFlags(Synthetic | Implicit | accessFlags)
.withMods(companionMods | Synthetic | Implicit)
.withPos(cdef.pos) :: Nil


val self1 = {
val selfType = if (self.tpt.isEmpty) classTypeRef else self.tpt
if (self.isEmpty) self
Expand Down Expand Up @@ -498,18 +497,18 @@ object desugar {

/** If `pat` is a variable pattern,
*
* val/var p = e
* val/var/lazy val p = e
*
* Otherwise, in case there is exactly one variable x_1 in pattern
* val/var p = e ==> val/var x_1 = (e: @unchecked) match (case p => (x_1))
* val/var/lazy val p = e ==> val/var/lazy val x_1 = (e: @unchecked) match (case p => (x_1))
*
* in case there are zero or more than one variables in pattern
* val/var p = e ==> private synthetic val t$ = (e: @unchecked) match (case p => (x_1, ..., x_N))
* val/var x_1 = t$._1
* val/var/lazy p = e ==> private synthetic [lazy] val t$ = (e: @unchecked) match (case p => (x_1, ..., x_N))
* val/var/def x_1 = t$._1
* ...
* val/var x_N = t$._N
* val/var/def x_N = t$._N
* If the original pattern variable carries a type annotation, so does the corresponding
* ValDef.
* ValDef or DefDef.
*/
def makePatDef(mods: Modifiers, pat: Tree, rhs: Tree)(implicit ctx: Context): Tree = pat match {
case VarPattern(named, tpt) =>
Expand All @@ -533,12 +532,16 @@ object desugar {
derivedValDef(named, tpt, matchExpr, mods)
case _ =>
val tmpName = ctx.freshName().toTermName
val patFlags = mods.flags & AccessFlags | Synthetic | (mods.flags & Lazy)
val firstDef = ValDef(tmpName, TypeTree(), matchExpr).withFlags(patFlags)
val patMods = mods & (AccessFlags | Lazy) | Synthetic
val firstDef =
ValDef(tmpName, TypeTree(), matchExpr)
.withPos(pat.pos.union(rhs.pos)).withMods(patMods)
def selector(n: Int) = Select(Ident(tmpName), nme.selectorName(n))
val restDefs =
for (((named, tpt), n) <- vars.zipWithIndex)
yield derivedValDef(named, tpt, selector(n), mods)
yield
if (mods is Lazy) derivedDefDef(named, tpt, selector(n), mods &~ Lazy)
else derivedValDef(named, tpt, selector(n), mods)
flatTree(firstDef :: restDefs)
}
}
Expand Down Expand Up @@ -635,6 +638,9 @@ object desugar {
private def derivedValDef(named: NameTree, tpt: Tree, rhs: Tree, mods: Modifiers) =
ValDef(named.name.asTermName, tpt, rhs).withMods(mods).withPos(named.pos)

private def derivedDefDef(named: NameTree, tpt: Tree, rhs: Tree, mods: Modifiers) =
DefDef(named.name.asTermName, Nil, Nil, tpt, rhs).withMods(mods).withPos(named.pos)

/** Main desugaring method */
def apply(tree: Tree)(implicit ctx: Context): Tree = {

Expand Down
83 changes: 83 additions & 0 deletions src/dotty/tools/dotc/ast/NavigateAST.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package dotty.tools.dotc
package ast

import core.Contexts.Context
import core.Decorators._
import util.Positions._
import Trees.{MemberDef, DefTree}

/** Utility functions to go from typed to untyped ASTs */
object NavigateAST {

/** The untyped tree corresponding to typed tree `tree` in the compilation
* unit specified by `ctx`
*/
def toUntyped(tree: tpd.Tree)(implicit ctx: Context): untpd.Tree =
untypedPath(tree, exactMatch = true) match {
case (utree: untpd.Tree) :: _ =>
utree
case _ =>
val loosePath = untypedPath(tree, exactMatch = false)
throw new
Error(i"""no untyped tree for $tree, pos = ${tree.pos}, envelope = ${tree.envelope}
|best matching path =\n$loosePath%\n====\n%
|path positions = ${loosePath.map(_.pos)}
|path envelopes = ${loosePath.map(_.envelope)}""".stripMargin)
}

/** The reverse path of untyped trees starting with a tree that closest matches
* `tree` and ending in the untyped tree at the root of the compilation unit
* specified by `ctx`.
* @param exactMatch If `true`, the path must start with a node that exactly
* matches `tree`, or `Nil` is returned.
* If `false` the path might start with a node enclosing
* the logical position of `tree`.
* Note: A complication concerns member definitions. ValDefs and DefDefs
* have after desugaring a position that spans just the name of the symbol being
* defined and nothing else. So we look instead for an untyped tree approximating the
* envelope of the definition, and declare success if we find another DefTree.
*/
def untypedPath(tree: tpd.Tree, exactMatch: Boolean = false)(implicit ctx: Context): List[Positioned] =
tree match {
case tree: MemberDef[_] =>
untypedPath(tree.envelope) match {
case path @ (last: DefTree[_]) :: _ => path
case path if !exactMatch => path
case _ => Nil
}
case _ =>
untypedPath(tree.pos) match {
case (path @ last :: _) if last.pos == tree.pos || !exactMatch => path
case _ => Nil
}
}

/** The reverse part of the untyped root of the compilation unit of `ctx` to
* position `pos`.
*/
def untypedPath(pos: Position)(implicit ctx: Context): List[Positioned] =
pathTo(pos, ctx.compilationUnit.untpdTree)


/** The reverse path from node `from` to the node that closest encloses position `pos`,
* or `Nil` if no such path exists. If a non-empty path is returned it starts with
* the node closest enclosing `pos` and ends with `from`.
*/
def pathTo(pos: Position, from: Positioned)(implicit ctx: Context): List[Positioned] = {
def childPath(it: Iterator[Any], path: List[Positioned]): List[Positioned] = {
while (it.hasNext) {
val path1 = it.next match {
case p: Positioned => singlePath(p, path)
case xs: List[_] => childPath(xs.iterator, path)
case _ => path
}
if (path1 ne path) return path1
}
path
}
def singlePath(p: Positioned, path: List[Positioned]): List[Positioned] =
if (p.envelope contains pos) childPath(p.productIterator, p :: path)
else path
singlePath(from, Nil)
}
}
78 changes: 45 additions & 33 deletions src/dotty/tools/dotc/ast/Positioned.scala
Original file line number Diff line number Diff line change
Expand Up @@ -54,21 +54,55 @@ abstract class Positioned extends DotClass with Product {
*/
private[dotc] def setPosUnchecked(pos: Position) = curPos = pos

/** If any children of this node do not have positions, set them to the given position,
/** If any children of this node do not have positions,
* fit their positions between the positions of the known subtrees
* and transitively visit their children.
* The method is likely time-critical because it is invoked on any node
* we create, so we want to avoid object allocations in the common case.
* The method is naturally expressed as two mutually (tail-)recursive
* functions, one which computes the next element to consider or terminates if there
* is none and the other which propagates the position information to that element.
* But since mutual tail recursion is not supported in Scala, we express it instead
* as a while loop with a termination by return in the middle.
*/
private def setChildPositions(pos: Position): Unit = {
def deepSetPos(x: Any): Unit = x match {
case p: Positioned =>
if (!p.pos.exists) p.setPos(pos)
case xs: List[_] =>
xs foreach deepSetPos
case _ =>
var n = productArity // subnodes are analyzed right to left
var elems: List[Any] = Nil // children in lists still to be considered, from right to left
var end = pos.end // the last defined offset, fill in positions up to this offset
var outstanding: List[Positioned] = Nil // nodes that need their positions filled once a start position
// is known, from left to right.
def fillIn(ps: List[Positioned], start: Int, end: Int): Unit = ps match {
case p :: ps1 =>
p.setPos(Position(start, end))
fillIn(ps1, end, end)
case nil =>
}
var n = productArity
while (n > 0) {
n -= 1
deepSetPos(productElement(n))
while (true) {
var nextChild: Any = null // the next child to be considered
if (elems.nonEmpty) {
nextChild = elems.head
elems = elems.tail
}
else if (n > 0) {
n = n - 1
nextChild = productElement(n)
}
else {
fillIn(outstanding, pos.start, end)
return
}
nextChild match {
case p: Positioned =>
if (p.pos.exists) {
fillIn(outstanding, p.pos.end, end)
outstanding = Nil
end = p.pos.start
}
else outstanding = p :: outstanding
case xs: List[_] =>
elems = elems ::: xs.reverse
case _ =>
}
}
}

Expand Down Expand Up @@ -114,26 +148,4 @@ abstract class Positioned extends DotClass with Product {
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
}
}
10 changes: 7 additions & 3 deletions src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,17 @@ object Trees {
def toTypeFlags: Modifiers[T] = withFlags(flags.toTypeFlags)
def toTermFlags: Modifiers[T] = withFlags(flags.toTermFlags)

private def withFlags(flags: FlagSet) =
def withFlags(flags: FlagSet) =
if (this.flags == flags) this
else copy(flags = flags)

def withAddedAnnotation[U >: Untyped <: T](annot: Tree[U]): Modifiers[U] =
if (annotations.exists(_ eq annot)) this
else withAnnotations(annotations :+ annot)

def withAnnotations[U >: Untyped <: T](annots: List[Tree[U]]): Modifiers[U] =
if (annots.isEmpty) this
else copy(annotations = annotations ++ annots)
if (annots eq annotations) this
else copy(annotations = annots)

def withPrivateWithin(pw: TypeName) =
if (pw.isEmpty) this
Expand Down
5 changes: 4 additions & 1 deletion src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package dotty.tools.dotc.config
package dotty.tools.dotc
package config

import PathResolver.Defaults
import rewrite.Rewrites

class ScalaSettings extends Settings.SettingGroup {

Expand Down Expand Up @@ -48,6 +50,7 @@ class ScalaSettings extends Settings.SettingGroup {
val d = StringSetting("-d", "directory|jar", "destination for generated classfiles.", ".")
val nospecialization = BooleanSetting("-no-specialization", "Ignore @specialize annotations.")
val language = MultiStringSetting("-language", "feature", "Enable one or more language features.")
val rewrite = OptionSetting[Rewrites]("-rewrite", "When used in conjunction with -language:Scala2 rewrites sources to migrate to new syntax")

/** -X "Advanced" settings
*/
Expand Down
9 changes: 8 additions & 1 deletion src/dotty/tools/dotc/config/Settings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ object Settings {
val StringTag = ClassTag(classOf[String])
val ListTag = ClassTag(classOf[List[_]])
val VersionTag = ClassTag(classOf[ScalaVersion])
val OptionTag = ClassTag(classOf[Option[_]])

class SettingsState(initialValues: Seq[Any]) {
private var values = ArrayBuffer(initialValues: _*)
Expand Down Expand Up @@ -55,7 +56,8 @@ object Settings {
choices: Seq[T] = Nil,
prefix: String = "",
aliases: List[String] = Nil,
depends: List[(Setting[_], Any)] = Nil)(private[Settings] val idx: Int) {
depends: List[(Setting[_], Any)] = Nil,
propertyClass: Option[Class[_]] = None)(private[Settings] val idx: Int) {

def withAbbreviation(abbrv: String): Setting[T] =
copy(aliases = aliases :+ abbrv)(idx)
Expand Down Expand Up @@ -112,6 +114,8 @@ object Settings {
def doSet(argRest: String) = ((implicitly[ClassTag[T]], args): @unchecked) match {
case (BooleanTag, _) =>
update(true, args)
case (OptionTag, _) =>
update(Some(propertyClass.get.newInstance), args)
case (ListTag, _) =>
if (argRest.isEmpty) missingArg
else update((argRest split ",").toList, args)
Expand Down Expand Up @@ -255,5 +259,8 @@ object Settings {

def VersionSetting(name: String, descr: String, default: ScalaVersion = NoScalaVersion): Setting[ScalaVersion] =
publish(Setting(name, descr, default))

def OptionSetting[T: ClassTag](name: String, descr: String): Setting[Option[T]] =
publish(Setting(name, descr, None, propertyClass = Some(implicitly[ClassTag[T]].runtimeClass)))
}
}
2 changes: 1 addition & 1 deletion src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -546,7 +546,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
hasImport(c)
}))
def hasOption = ctx.base.settings.language.value exists (s => s == featureName || s == "_")
hasImport || hasOption
hasImport(ctx.withPhase(ctx.typerPhase)) || hasOption
}

/** Is auto-tupling enabled? */
Expand Down
2 changes: 0 additions & 2 deletions src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2424,8 +2424,6 @@ object Types {
x => paramBounds mapConserve (_.subst(this, x).bounds),
x => resType.subst(this, x))

// need to override hashCode and equals to be object identity
// because paramNames by itself is not discriminatory enough
override def equals(other: Any) = other match {
case other: PolyType =>
other.paramNames == this.paramNames && other.paramBounds == this.paramBounds && other.resType == this.resType
Expand Down
Loading

0 comments on commit cdbc163

Please sign in to comment.