From 9671bc42eb3d0029bdba2c3709d26d07ef92d039 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 25 Dec 2019 11:45:22 +0100 Subject: [PATCH] Fix #7820: Break cycle when computing completerTypeParams The following no longer crashes: class C { type F[X <: F[_, _], Y] } But it is still flagged as a cyclic reference error, whereas nsc accepts it. I believe it would be tricky/risky to change dotc's algorithms to accept it as well, and I am not sure it's necessary. Generally, we want to get away from F-bounds, so just accepting the most common use case (F-bounds in method type parameters) is hopefully OK. --- .../src/dotty/tools/dotc/typer/Namer.scala | 37 +++++++++---------- tests/neg/i7820.scala | 3 ++ 2 files changed, 21 insertions(+), 19 deletions(-) create mode 100644 tests/neg/i7820.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 304fd835b146..a74f6d210b3e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -944,28 +944,27 @@ class Namer { typer: Typer => private var nestedCtx: Context = null assert(!original.isClassDef) - override def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeSymbol] = { - if (myTypeParams == null) { + override def completerTypeParams(sym: Symbol)(implicit ctx: Context): List[TypeSymbol] = + if myTypeParams == null then //println(i"completing type params of $sym in ${sym.owner}") nestedCtx = localContext(sym).setNewScope - myTypeParams = { - implicit val ctx = nestedCtx - def typeParamTrees(tdef: Tree): List[TypeDef] = tdef match { - case TypeDef(_, original) => - original match { - case LambdaTypeTree(tparams, _) => tparams - case original: DerivedFromParamTree => typeParamTrees(original.watched) - case _ => Nil - } - case _ => Nil - } - val tparams = typeParamTrees(original) - completeParams(tparams) - tparams.map(symbolOfTree(_).asType) - } - } + given Context = nestedCtx + + def typeParamTrees(tdef: Tree): List[TypeDef] = tdef match + case TypeDef(_, original) => + original match + case LambdaTypeTree(tparams, _) => tparams + case original: DerivedFromParamTree => typeParamTrees(original.watched) + case _ => Nil + case _ => Nil + + val tparams = typeParamTrees(original) + index(tparams) + myTypeParams = tparams.map(symbolOfTree(_).asType) + for param <- tparams do typedAheadExpr(param) + end if myTypeParams - } + end completerTypeParams override protected def typeSig(sym: Symbol): Type = typeDefSig(original, sym, completerTypeParams(sym)(ictx))(nestedCtx) diff --git a/tests/neg/i7820.scala b/tests/neg/i7820.scala new file mode 100644 index 000000000000..cc803aa1f3d6 --- /dev/null +++ b/tests/neg/i7820.scala @@ -0,0 +1,3 @@ +trait A1 { type F[X <: F[_, _], Y] } // error: cyclic reference involving type F +trait A2 { type F[X <: F, Y] } // error: cyclic reference involving type F +trait A3 { type F[X >: F, Y] } // error: cyclic reference involving type F