diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 1bd17d6c9778..a5bf09325c29 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -141,30 +141,31 @@ object SymDenotations { } final def completeFrom(completer: LazyType)(using Context): Unit = - if (Config.showCompletions) { - println(i"${" " * indent}completing ${if (isType) "type" else "val"} $name") - indent += 1 - - if (myFlags.is(Touched)) throw CyclicReference(this) - myFlags |= Touched - - // completions.println(s"completing ${this.debugString}") - try atPhase(validFor.firstPhaseId)(completer.complete(this)) - catch { - case ex: CyclicReference => - println(s"error while completing ${this.debugString}") - throw ex + if completer.needsCompletion(this) then + if (Config.showCompletions) { + println(i"${" " * indent}completing ${if (isType) "type" else "val"} $name") + indent += 1 + + if (myFlags.is(Touched)) throw CyclicReference(this) + myFlags |= Touched + + // completions.println(s"completing ${this.debugString}") + try atPhase(validFor.firstPhaseId)(completer.complete(this)) + catch { + case ex: CyclicReference => + println(s"error while completing ${this.debugString}") + throw ex + } + finally { + indent -= 1 + println(i"${" " * indent}completed $name in $owner") + } } - finally { - indent -= 1 - println(i"${" " * indent}completed $name in $owner") + else { + if (myFlags.is(Touched)) throw CyclicReference(this) + myFlags |= Touched + atPhase(validFor.firstPhaseId)(completer.complete(this)) } - } - else { - if (myFlags.is(Touched)) throw CyclicReference(this) - myFlags |= Touched - atPhase(validFor.firstPhaseId)(completer.complete(this)) - } protected[dotc] def info_=(tp: Type): Unit = { /* // DEBUG @@ -2517,6 +2518,13 @@ object SymDenotations { def withModuleClass(moduleClassFn: Context ?=> Symbol): this.type = { myModuleClassFn = moduleClassFn; this } override def toString: String = getClass.toString + + /** A hook that is called before trying to complete a symbol with its + * associated cycle detection via the Touched flag. This is overridden + * for Type definitions in Namer, where we make sure that owners are + * completed before nested types. + */ + def needsCompletion(symd: SymDenotation)(using Context): Boolean = true } object LazyType: diff --git a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala index d92549acfbf7..d3dc0713a1ed 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeApplications.scala @@ -211,9 +211,13 @@ class TypeApplications(val self: Type) extends AnyVal { /** If `self` is a generic class, its type parameter symbols, otherwise Nil */ final def typeParamSymbols(using Context): List[TypeSymbol] = typeParams match { - case (_: Symbol) :: _ => - assert(typeParams.forall(_.isInstanceOf[Symbol])) - typeParams.asInstanceOf[List[TypeSymbol]] + case tparams @ (_: Symbol) :: _ => + assert(tparams.forall(_.isInstanceOf[Symbol])) + tparams.asInstanceOf[List[TypeSymbol]] + // Note: Two successive calls to typeParams can yield different results here because + // of different completion status. I.e. the first call might produce some symbols, + // whereas the second call gives some LambdaParams. This was observed + // for ticket0137.scala case _ => Nil } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index b9373c5b8da3..fc4faa87f2be 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -841,6 +841,20 @@ class Namer { typer: Typer => private var nestedCtx: Context = null assert(!original.isClassDef) + /** If completion of the owner of the to be completed symbol has not yet started, + * complete the owner first and check again. This prevents cyclic references + * where we need to copmplete a type parameter that has an owner that is not + * yet completed. Test case is pos/i10967.scala. + */ + override def needsCompletion(symd: SymDenotation)(using Context): Boolean = + val owner = symd.owner + !owner.exists + || owner.is(Touched) + || { + owner.ensureCompleted() + !symd.isCompleted + } + override def completerTypeParams(sym: Symbol)(using Context): List[TypeSymbol] = if myTypeParams == null then //println(i"completing type params of $sym in ${sym.owner}") diff --git a/tests/pos/i10638.scala b/tests/pos/i10638.scala new file mode 100644 index 000000000000..66abb338b03d --- /dev/null +++ b/tests/pos/i10638.scala @@ -0,0 +1,9 @@ +package woes + +trait Txn[T <: Txn[T]] + +trait Impl[Repr[~ <: Txn[~]]] { + final type Ext = Extension[Repr] // Huh! +} + +trait Extension[Repr[~ <: Txn[~]]] \ No newline at end of file diff --git a/tests/pos/i10967.scala b/tests/pos/i10967.scala new file mode 100644 index 000000000000..c545b159d6eb --- /dev/null +++ b/tests/pos/i10967.scala @@ -0,0 +1,9 @@ +trait SomeTrait + +trait CollBase[A <: SomeTrait, +CC1[A2 <: SomeTrait]] { + val companion: CollCompanion[CC1] +} + +trait Coll[A <: SomeTrait] extends CollBase[A, Coll] + +trait CollCompanion[+CC2[A <: SomeTrait]] \ No newline at end of file