From fae14bb585033518cda18ea19953c2d51f223e62 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 29 Jan 2018 18:13:27 +0100 Subject: [PATCH 1/3] Fix #3816: Protect against cycles when unpickling hk types --- .../src/dotty/tools/dotc/core/SymDenotations.scala | 14 ++++++++++---- .../dotty/tools/dotc/core/TypeApplications.scala | 6 ++++-- .../tools/dotc/core/tasty/TreeUnpickler.scala | 8 +++++++- .../dotc/core/unpickleScala2/Scala2Unpickler.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Checking.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Namer.scala | 3 ++- 6 files changed, 25 insertions(+), 10 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index d2c1678cf967..9cdab3c0cb67 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -5,6 +5,7 @@ package core import Periods._, Contexts._, Symbols._, Denotations._, Names._, NameOps._, Annotations._ import Types._, Flags._, Decorators._, DenotTransformers._, StdNames._, Scopes._, Comments._ import NameOps._, NameKinds._, Phases._ +import TypeApplications.TypeParamInfo import Scopes.Scope import collection.mutable import collection.BitSet @@ -1915,6 +1916,11 @@ object SymDenotations { override def complete(denot: SymDenotation)(implicit ctx: Context) = self.complete(denot) } + /** The type parameters computed by the completer before completion has finished */ + def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeParamInfo] = + if (sym is Touched) Nil // return `Nil` instead of throwing a cyclic reference + else sym.info.typeParams + def decls: Scope = myDecls def sourceModule(implicit ctx: Context): Symbol = mySourceModuleFn(ctx) def moduleClass(implicit ctx: Context): Symbol = myModuleClassFn(ctx) @@ -1924,12 +1930,12 @@ object SymDenotations { def withModuleClass(moduleClassFn: Context => Symbol): this.type = { myModuleClassFn = moduleClassFn; this } } - /** A subclass of LazyTypes where type parameters can be completed independently of - * the info. + /** A subtrait of LazyTypes where completerTypeParams yields a List[TypeSymbol], which + * should be completed independently of the info. */ trait TypeParamsCompleter extends LazyType { - /** The type parameters computed by the completer before completion has finished */ - def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeSymbol] + override def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeSymbol] = + unsupported("completerTypeParams") // should be abstract, but Scala-2 will then comppute wrong type for it } val NoSymbolFn = (ctx: Context) => NoSymbol diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index 5fef29032fd7..47155e79af52 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -173,8 +173,10 @@ class TypeApplications(val self: Type) extends AnyVal { val tsym = self.symbol if (tsym.isClass) tsym.typeParams else if (!tsym.exists) self.info.typeParams - else if (!tsym.isCompleting) tsym.info.typeParams - else Nil + else tsym.infoOrCompleter match { + case info: LazyType => info.completerTypeParams(tsym) + case info => info.typeParams + } case self: AppliedType => if (self.tycon.typeSymbol.isClass) Nil else self.superType.typeParams diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index fcb4335651aa..7debff863d65 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -745,12 +745,18 @@ class TreeUnpickler(reader: TastyReader, } TypeDef(readTemplate(localCtx)) } else { + sym.info = TypeBounds.empty // needed to avoid cyclic references when unpicklin rhs, see i3816.scala + sym.setFlag(Provisional) val rhs = readTpt()(localCtx) - sym.info = NoCompleter + sym.info = new NoCompleter { + override def completerTypeParams(sym: Symbol)(implicit ctx: Context) = + rhs.tpe.typeParams + } sym.info = rhs.tpe match { case _: TypeBounds | _: ClassInfo => checkNonCyclic(sym, rhs.tpe, reportErrors = false) case _ => TypeAlias(rhs.tpe) } + sym.resetFlag(Provisional) TypeDef(rhs) } case PARAM => diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index 609226feda47..e253c1b536fd 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -614,7 +614,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas /** Force reading type params early, we need them in setClassInfo of subclasses. */ def init()(implicit ctx: Context) = loadTypeParams - def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeSymbol] = + override def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeSymbol] = loadTypeParams } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index e14081207e23..d0a44567662d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -239,7 +239,7 @@ object Checking { } if (isInteresting(pre)) { val pre1 = this(pre, false, false) - if (locked.contains(tp) || tp.symbol.infoOrCompleter == NoCompleter) + if (locked.contains(tp) || tp.symbol.infoOrCompleter.isInstanceOf[NoCompleter]) throw CyclicReference(tp.symbol) locked += tp try checkInfo(tp.info) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index e9dcd406716d..189ecc23d499 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -824,7 +824,7 @@ class Namer { typer: Typer => private[this] var nestedCtx: Context = null assert(!original.isClassDef) - def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeSymbol] = { + override def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeSymbol] = { if (myTypeParams == null) { //println(i"completing type params of $sym in ${sym.owner}") nestedCtx = localContext(sym).setNewScope @@ -1229,6 +1229,7 @@ class Namer { typer: Typer => sym.info = NoCompleter sym.info = checkNonCyclic(sym, unsafeInfo, reportErrors = true) } + sym.resetFlag(Provisional) // Here we pay the price for the cavalier setting info to TypeBounds.empty above. // We need to compensate by reloading the denotation of references that might From 238751fc9d0af10592e3f74064167e49caa65ff1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 29 Jan 2018 21:16:50 +0100 Subject: [PATCH 2/3] Add test case --- tests/pickling/i3816.scala | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 tests/pickling/i3816.scala diff --git a/tests/pickling/i3816.scala b/tests/pickling/i3816.scala new file mode 100644 index 000000000000..443df9fd0c54 --- /dev/null +++ b/tests/pickling/i3816.scala @@ -0,0 +1,4 @@ +trait Iterable { self => + //type CC <: Iterable { type CC = self.CC } + type DD[X] <: Iterable { type DD[Y] = self.DD[Y] } +} From 311a6540161c72b80e8872d6ed8cabaa04d48276 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 2 Feb 2018 17:47:29 +0100 Subject: [PATCH 3/3] Fix typo --- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 9cdab3c0cb67..d5177e9e3dcc 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1935,7 +1935,7 @@ object SymDenotations { */ trait TypeParamsCompleter extends LazyType { override def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeSymbol] = - unsupported("completerTypeParams") // should be abstract, but Scala-2 will then comppute wrong type for it + unsupported("completerTypeParams") // should be abstract, but Scala-2 will then compute the wrong type for it } val NoSymbolFn = (ctx: Context) => NoSymbol