From 725ccddd59a11f57d1fd919a552235146ff3d46a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 25 Jul 2017 20:18:42 +0200 Subject: [PATCH 1/4] Pickle LazyRefs Need to keep cycle breakers --- compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala | 5 ++++- compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala | 1 + compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 5 +++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala index cd0d427f3510..3b04d6d67eb7 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TastyFormat.scala @@ -164,6 +164,7 @@ Standard-Section: "ASTs" TopLevelStat* BIND Length boundName_NameRef bounds_Type // for type-variables defined in a type pattern BYNAMEtype underlying_Type + LAZYref underlying_Type POLYtype Length result_Type NamesTypes METHODtype Length result_Type NamesTypes // needed for refinements TYPELAMBDAtype Length result_Type NamesTypes // variance encoded in front of name: +/-/(nothing) @@ -256,7 +257,7 @@ object TastyFormat { final val OBJECTCLASS = 40 final val SIGNED = 63 - + final val firstInternalTag = 64 final val IMPLMETH = 64 @@ -322,6 +323,7 @@ object TastyFormat { final val PROTECTEDqualified = 105 final val RECtype = 106 final val SINGLETONtpt = 107 + final val LAZYref = 108 final val IDENT = 112 final val IDENTtpt = 113 @@ -512,6 +514,7 @@ object TastyFormat { case DOUBLEconst => "DOUBLEconst" case STRINGconst => "STRINGconst" case RECtype => "RECtype" + case LAZYref => "LAZYref" case IDENT => "IDENT" case IDENTtpt => "IDENTtpt" diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 338a395ab685..885a76e3f763 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -246,6 +246,7 @@ class TreePickler(pickler: TastyPickler) { case tpe: ParamRef => assert(pickleParamRef(tpe), s"orphan parameter reference: $tpe") case tpe: LazyRef => + writeByte(LAZYref) pickleType(tpe.ref) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index d1f800c9926f..8b6fa72529f3 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -293,6 +293,11 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi RecType(rt => registeringType(rt, readType())) case RECthis => RecThis(readTypeRef().asInstanceOf[RecType]) + case LAZYref => + val rdr = fork + def readUnderlying() = rdr.readType() + skipTree() + LazyRef(readUnderlying) case SHARED => val ref = readAddr() typeAtAddr.getOrElseUpdate(ref, forkAt(ref).readType()) From bcbd456c120ecaf63b5c2b178fd8a54bbd3ed60a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 25 Jul 2017 20:22:09 +0200 Subject: [PATCH 2/4] Be more careful reading applied types from Tasty Same principle as reading from Scala2 pickles applied = in the face of F-bounded polymorphism we cannot assume that the type parameters are known when we unpickle a type application. --- .../src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 8b6fa72529f3..5eea9b769aa7 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -996,7 +996,12 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi val refinements = readStats(refineCls, end)(localContext(refineCls)) RefinedTypeTree(parent, refinements, refineCls) case APPLIEDtpt => - AppliedTypeTree(readTpt(), until(end)(readTpt())) + // If we do directly a tpd.AppliedType tree we might get a + // wrong number of arguments in some scenarios reading F-bounded + // types. This came up in #137 of collection strawman. + val tycon = readTpt() + val args = until(end)(readTpt()) + untpd.AppliedTypeTree(tycon, args).withType(tycon.tpe.safeAppliedTo(args.tpes)) case ANDtpt => val tpt1 = readTpt() val tpt2 = readTpt() From b765b95cdccf8486d280f5ca4875786c6988d7ad Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 25 Jul 2017 20:22:49 +0200 Subject: [PATCH 3/4] Survive cyclic reference errors when printing an error message. --- .../tools/dotc/reporting/diagnostic/messages.scala | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala index 3b398d23661c..757da5d83bc7 100644 --- a/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala @@ -23,6 +23,7 @@ import dotty.tools.dotc.ast.Trees import dotty.tools.dotc.ast.untpd.Modifiers import dotty.tools.dotc.core.Flags.{FlagSet, Mutable} import dotty.tools.dotc.core.SymDenotations.SymDenotation +import scala.util.control.NonFatal object messages { @@ -708,10 +709,15 @@ object messages { private val actualArgString = actual.map(_.show).mkString("[", ", ", "]") - private val prettyName = fntpe.termSymbol match { - case NoSymbol => fntpe.show - case symbol => symbol.showFullName - } + private val prettyName = + try + fntpe.termSymbol match { + case NoSymbol => fntpe.show + case symbol => symbol.showFullName + } + catch { + case NonFatal(ex) => fntpe.show + } val msg = hl"""|${NoColor(msgPrefix)} type arguments for $prettyName$expectedArgString From dd661bf2d18d30d2c4758b8b0821f068cdd065b0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 26 Jul 2017 09:53:36 +0200 Subject: [PATCH 4/4] Make LazyRef completers take a context This avoids memory leaks in unpicklers, where lazy refs don't capture the context anymore. Other lazyrefs still need to capture context, though. --- .../tools/dotc/core/TypeApplications.scala | 2 +- compiler/src/dotty/tools/dotc/core/Types.scala | 18 ++++++++---------- .../tools/dotc/core/tasty/TreeUnpickler.scala | 3 +-- .../src/dotty/tools/dotc/typer/Checking.scala | 2 +- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index fb2300ff1829..9397eeb09d7b 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -443,7 +443,7 @@ class TypeApplications(val self: Type) extends AnyVal { case dealiased: TypeBounds => dealiased.derivedTypeBounds(dealiased.lo.appliedTo(args), dealiased.hi.appliedTo(args)) case dealiased: LazyRef => - LazyRef(() => dealiased.ref.appliedTo(args)) + LazyRef(c => dealiased.ref(c).appliedTo(args)) case dealiased: WildcardType => WildcardType(dealiased.optBounds.appliedTo(args).bounds) case dealiased: TypeRef if dealiased.symbol == defn.NothingClass => diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 40fbc80f85f0..0a553f72c1bb 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -2149,25 +2149,23 @@ object Types { } } - case class LazyRef(private var refFn: () => Type) extends UncachedProxyType with ValueType { + case class LazyRef(private var refFn: Context => Type) extends UncachedProxyType with ValueType { private var myRef: Type = null private var computed = false - def ref = { + def ref(implicit ctx: Context) = { if (computed) assert(myRef != null) else { computed = true - myRef = refFn() + myRef = refFn(ctx) + refFn = null } myRef } def evaluating = computed && myRef == null override def underlying(implicit ctx: Context) = ref - override def toString = s"LazyRef($ref)" - override def equals(other: Any) = other match { - case other: LazyRef => this.ref.equals(other.ref) - case _ => false - } - override def hashCode = ref.hashCode + 37 + override def toString = s"LazyRef(...)" + override def equals(other: Any) = this eq other.asInstanceOf[AnyRef] + override def hashCode = System.identityHashCode(this) } // --- Refined Type and RecType ------------------------------------------------ @@ -3768,7 +3766,7 @@ object Types { derivedSuperType(tp, this(thistp), this(supertp)) case tp: LazyRef => - LazyRef(() => this(tp.ref)) + LazyRef(_ => this(tp.ref)) case tp: ClassInfo => mapClassInfo(tp) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 5eea9b769aa7..3015bef6d8e7 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -295,9 +295,8 @@ class TreeUnpickler(reader: TastyReader, nameAtRef: NameRef => TermName, posUnpi RecThis(readTypeRef().asInstanceOf[RecType]) case LAZYref => val rdr = fork - def readUnderlying() = rdr.readType() skipTree() - LazyRef(readUnderlying) + LazyRef(implicit ctx => rdr.readType()) case SHARED => val ref = readAddr() typeAtAddr.getOrElseUpdate(ref, forkAt(ref).readType()) diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 72ce89a33942..130eb8d36754 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -249,7 +249,7 @@ object Checking { } catch { case ex: CyclicReference => ctx.debuglog(i"cycle detected for $tp, $nestedCycleOK, $cycleOK") - if (cycleOK) LazyRef(() => tp) + if (cycleOK) LazyRef(_ => tp) else if (reportErrors) throw ex else tp }