aboutsummaryrefslogtreecommitdiff
path: root/src/dotty/tools/dotc/core/pickling/TreePickler.scala
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2015-03-03 10:57:03 +0100
committerDmitry Petrashko <dmitry.petrashko@gmail.com>2015-03-18 11:14:14 +0100
commit60220781967de8c7efafe1e1b6ed09ae482a8582 (patch)
tree4c01f6a3f20f48c0080b69422fb3974350cbb86d /src/dotty/tools/dotc/core/pickling/TreePickler.scala
parentb0d73807db5e441badd48c189ef2bc35771021e7 (diff)
downloaddotty-60220781967de8c7efafe1e1b6ed09ae482a8582.tar.gz
dotty-60220781967de8c7efafe1e1b6ed09ae482a8582.tar.bz2
dotty-60220781967de8c7efafe1e1b6ed09ae482a8582.zip
Keeping track of unpickling definition order in Pickler.
The Unpickler visits nodes in a certain order and (so far) expects to see definitions before uses. This commit makes sure that the Unpickler generates definitions in an order that matches the pickler's behavior. Every definition should be "pre-registered" before it can be used. This still allows for mutual recursion between symbols because preregistering enters all symbols of a scope in bulk before generating any references to these symbols. It would be nice if this was the end of the story, but unfortunately that's not the case. It turned out that dotc produced references that were not legal according to the implemented model, and that are also not legal in the formal type system. Each of these violations referred to a symbol outside its scope. There were two sources: 1) Pattern-bound variables were retained in the type of a case block and, consequently, the type of the enclosing match. This has been fixed by a previous commit a0f8ec21c9ce5381bea780e7be89286653fc676e. 2) Dependent anonymous functions led to (illegal) dependent closures with references to their parameters leaking out in the environment. This has been mostly fixed by the previous commits, in particular 1c70842036b083652c3eeab83aad0b2490674bfe. But there are still two problems remaining, see: 89c8bd8a1eb9fb3f0f09f25bedb68de1ef2e2ae8. We might fix the two problems. But it's inconceivable to me that scalac could also produce only "hygienic" trees that do not have escaping references. There are too many situations where we know this is not true (existential skolems, for a start). So we choose to flag escaping variables in logs instead of treating them as errors, and to deal with the situation in the Unpickler.
Diffstat (limited to 'src/dotty/tools/dotc/core/pickling/TreePickler.scala')
-rw-r--r--src/dotty/tools/dotc/core/pickling/TreePickler.scala64
1 files changed, 44 insertions, 20 deletions
diff --git a/src/dotty/tools/dotc/core/pickling/TreePickler.scala b/src/dotty/tools/dotc/core/pickling/TreePickler.scala
index 5f18a7552..dc98a58f3 100644
--- a/src/dotty/tools/dotc/core/pickling/TreePickler.scala
+++ b/src/dotty/tools/dotc/core/pickling/TreePickler.scala
@@ -26,8 +26,14 @@ class TreePickler(pickler: TastyPickler) {
op
fillRef(lengthAddr, currentAddr, relative = true)
}
+
+ def preRegister(tree: Tree)(implicit ctx: Context): Unit = tree match {
+ case tree: MemberDef =>
+ if (!symRefs.contains(tree.symbol)) symRefs(tree.symbol) = NoAddr
+ case _ =>
+ }
- def registerDef(sym: Symbol) = {
+ def registerDef(sym: Symbol): Unit = {
symRefs(sym) = currentAddr
forwardSymRefs.get(sym) match {
case Some(refs) =>
@@ -44,14 +50,18 @@ class TreePickler(pickler: TastyPickler) {
pickleName(TastyName.Signed(nameIndex(name), params.map(nameIndex), nameIndex(result)))
}
- private def pickleSym(sym: Symbol)(implicit ctx: Context) = symRefs.get(sym) match {
+ private def pickleSymRef(sym: Symbol)(implicit ctx: Context) = symRefs.get(sym) match {
case Some(label) =>
- writeRef(label)
+ if (label != NoAddr) writeRef(label) else pickleForwardSymRef(sym)
case None =>
- val ref = reserveRef(relative = false)
- assert(!sym.is(Flags.Package), sym)
- //assert(!sym.is(Flags.Param) || sym.owner.isClass, sym.showLocated) // TODO enable
- forwardSymRefs(sym) = ref :: forwardSymRefs.getOrElse(sym, Nil)
+ ctx.log(i"pickling reference to as yet undefined $sym in ${sym.owner}", sym.pos)
+ pickleForwardSymRef(sym)
+ }
+
+ private def pickleForwardSymRef(sym: Symbol)(implicit ctx: Context) = {
+ val ref = reserveRef(relative = false)
+ assert(!sym.is(Flags.Package), sym)
+ forwardSymRefs(sym) = ref :: forwardSymRefs.getOrElse(sym, Nil)
}
def pickle(trees: List[Tree])(implicit ctx: Context) = {
@@ -116,7 +126,7 @@ class TreePickler(pickler: TastyPickler) {
throw ex
}
- def pickleNewType(tpe: Type, richTypes: Boolean): Unit = tpe match {
+ def pickleNewType(tpe: Type, richTypes: Boolean): Unit = try { tpe match {
case ConstantType(value) =>
pickleConstant(value)
case tpe: TypeRef if tpe.info.isAlias && tpe.symbol.is(Flags.AliasPreferred) =>
@@ -130,7 +140,7 @@ class TreePickler(pickler: TastyPickler) {
else if (tpe.prefix == NoPrefix) {
def pickleRef() = {
writeByte(if (tpe.isType) TYPEREFdirect else TERMREFdirect)
- pickleSym(sym)
+ pickleSymRef(sym)
}
if (sym is Flags.BindDefinedType) {
registerDef(sym)
@@ -145,7 +155,7 @@ class TreePickler(pickler: TastyPickler) {
}
else {
writeByte(if (tpe.isType) TYPEREFsymbol else TERMREFsymbol)
- pickleSym(sym); pickleType(tpe.prefix)
+ pickleSymRef(sym); pickleType(tpe.prefix)
}
case tpe: TermRefWithSignature =>
writeByte(TERMREF)
@@ -212,6 +222,10 @@ class TreePickler(pickler: TastyPickler) {
writeByte(NOTYPE)
// case NoPrefix => // not sure we need this!
// writeByte(NOPREFIX)
+ }} catch {
+ case ex: AssertionError =>
+ println(i"error while pickling type $tpe")
+ throw ex
}
def pickleMethodic(result: Type, names: List[Name], types: List[Type]) =
@@ -296,6 +310,7 @@ class TreePickler(pickler: TastyPickler) {
withLength { pickleTree(lhs); pickleTree(rhs) }
case Block(stats, expr) =>
writeByte(BLOCK)
+ stats.foreach(preRegister)
withLength { pickleTree(expr); stats.foreach(pickleTree) }
case If(cond, thenp, elsep) =>
writeByte(IF)
@@ -311,7 +326,7 @@ class TreePickler(pickler: TastyPickler) {
withLength { pickleTree(pat); pickleTree(rhs); pickleTreeIfNonEmpty(guard) }
case Return(expr, from) =>
writeByte(RETURN)
- withLength { pickleSym(from.symbol); pickleTreeIfNonEmpty(expr) }
+ withLength { pickleSymRef(from.symbol); pickleTreeIfNonEmpty(expr) }
case Try(block, cases, finalizer) =>
writeByte(TRY)
withLength { pickleTree(block); cases.foreach(pickleTree); pickleTreeIfNonEmpty(finalizer) }
@@ -344,14 +359,14 @@ class TreePickler(pickler: TastyPickler) {
case tree: ValDef =>
pickleDef(VALDEF, tree.symbol, tree.tpt, tree.rhs)
case tree: DefDef =>
- def pickleParams = {
- tree.tparams.foreach(pickleParam)
+ def pickleAllParams = {
+ pickleParams(tree.tparams)
for (vparams <- tree.vparamss) {
writeByte(PARAMS)
- withLength { vparams.foreach(pickleParam) }
+ withLength { pickleParams(vparams) }
}
}
- pickleDef(DEFDEF, tree.symbol, tree.tpt, tree.rhs, pickleParams)
+ pickleDef(DEFDEF, tree.symbol, tree.tpt, tree.rhs, pickleAllParams)
case tree: TypeDef =>
pickleDef(TYPEDEF, tree.symbol, tree.rhs)
case tree: Template =>
@@ -363,7 +378,7 @@ class TreePickler(pickler: TastyPickler) {
case _ => false
}
withLength {
- params.foreach(pickleParam)
+ pickleParams(params)
tree.parents.foreach(pickleTree)
val cinfo @ ClassInfo(_, _, _, _, selfInfo) = tree.symbol.owner.info
if ((selfInfo ne NoType) || !tree.self.isEmpty) {
@@ -373,8 +388,7 @@ class TreePickler(pickler: TastyPickler) {
pickleType(cinfo.selfType)
}
}
- pickleTree(tree.constr)
- rest.foreach(pickleTree)
+ pickleStats(tree.constr :: rest)
}
case Import(expr, selectors) =>
writeByte(IMPORT)
@@ -391,7 +405,7 @@ class TreePickler(pickler: TastyPickler) {
}
case PackageDef(pid, stats) =>
writeByte(PACKAGE)
- withLength { pickleType(pid.tpe); stats.foreach(pickleTree) }
+ withLength { pickleType(pid.tpe); pickleStats(stats) }
case EmptyTree =>
writeByte(EMPTYTREE)
}}
@@ -401,8 +415,8 @@ class TreePickler(pickler: TastyPickler) {
throw ex
}
-
def pickleDef(tag: Int, sym: Symbol, tpt: Tree, rhs: Tree = EmptyTree, pickleParams: => Unit = ()) = {
+ assert(symRefs(sym) == NoAddr)
registerDef(sym)
writeByte(tag)
withLength {
@@ -421,6 +435,16 @@ class TreePickler(pickler: TastyPickler) {
case tree: ValDef => pickleDef(PARAM, tree.symbol, tree.tpt)
case tree: TypeDef => pickleDef(TYPEPARAM, tree.symbol, tree.rhs)
}
+
+ def pickleParams(trees: List[Tree]): Unit = {
+ trees.foreach(preRegister)
+ trees.foreach(pickleParam)
+ }
+
+ def pickleStats(stats: List[Tree]) = {
+ stats.foreach(preRegister)
+ stats.foreach(pickleTree)
+ }
def pickleModifiers(sym: Symbol): Unit = {
import Flags._