Skip to content

Commit e30411d

Browse files
oderskytgodzik
authored andcommitted
Support cleanup actions in class completers
Needed to break the loop between completion of class and companion object. If we try to complete the class first, and completion needs the companion object (for instance for processing an import) then the companion object completion would consult the companion class info for constructor that need a constructor proxy in the object. This can lead to a cyclic reference. We now break the cycle by delaying adding constructor proxies in this case to be the last completion action of the companion class.
1 parent 2a967a0 commit e30411d

File tree

5 files changed

+43
-3
lines changed

5 files changed

+43
-3
lines changed

compiler/src/dotty/tools/dotc/core/NamerOps.scala

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,25 @@ import TypeApplications.EtaExpansion
99
/** Operations that are shared between Namer and TreeUnpickler */
1010
object NamerOps:
1111

12+
/** A completer supporting cleanup actions.
13+
* Needed to break the loop between completion of class and companion object.
14+
* If we try to complete the class first, and completion needs the companion
15+
* object (for instance for processing an import) then the companion object
16+
* completion would consult the companion class info for constructor that
17+
* need a constructor proxy in the object. This can lead to a cyclic reference.
18+
* We break the cycle by delaying adding constructor proxies to be a cleanuo
19+
* action instead.
20+
*/
21+
trait CompleterWithCleanup extends LazyType:
22+
private var cleanupActions: List[() => Unit] = Nil
23+
def addCleanupAction(op: () => Unit): Unit =
24+
cleanupActions = op :: cleanupActions
25+
def cleanup(): Unit =
26+
if cleanupActions.nonEmpty then
27+
cleanupActions.reverse.foreach(_())
28+
cleanupActions = Nil
29+
end CompleterWithCleanup
30+
1231
/** The type of the constructed instance is returned
1332
*
1433
* @param ctor the constructor
@@ -118,8 +137,14 @@ object NamerOps:
118137
ApplyProxyCompleter(constr),
119138
cls.privateWithin,
120139
constr.coord)
121-
for dcl <- cls.info.decls do
140+
def doAdd() = for dcl <- cls.info.decls do
122141
if dcl.isConstructor then scope.enter(proxy(dcl))
142+
cls.infoOrCompleter match
143+
case completer: CompleterWithCleanup if cls.is(Touched) =>
144+
// Taking the info would lead to a cyclic reference here - delay instead until cleanup of `cls`
145+
completer.addCleanupAction(doAdd)
146+
case _ =>
147+
doAdd()
123148
scope
124149
end addConstructorApplies
125150

compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ class TreeUnpickler(reader: TastyReader,
127127
}
128128
}
129129

130-
class Completer(reader: TastyReader)(using @constructorOnly _ctx: Context) extends LazyType {
130+
class Completer(reader: TastyReader)(using @constructorOnly _ctx: Context)
131+
extends LazyType, CompleterWithCleanup {
131132
import reader.*
132133
val owner = ctx.owner
133134
val mode = ctx.mode
@@ -147,6 +148,8 @@ class TreeUnpickler(reader: TastyReader,
147148
case ex: CyclicReference => throw ex
148149
case ex: AssertionError => fail(ex)
149150
case ex: Exception => fail(ex)
151+
finally
152+
cleanup()
150153
}
151154

152155
class TreeReader(val reader: TastyReader) {

compiler/src/dotty/tools/dotc/typer/Namer.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1089,7 +1089,8 @@ class Namer { typer: Typer =>
10891089
end typeSig
10901090
}
10911091

1092-
class ClassCompleter(cls: ClassSymbol, original: TypeDef)(ictx: Context) extends Completer(original)(ictx) {
1092+
class ClassCompleter(cls: ClassSymbol, original: TypeDef)(ictx: Context)
1093+
extends Completer(original)(ictx), CompleterWithCleanup {
10931094
withDecls(newScope(using ictx))
10941095

10951096
protected implicit val completerCtx: Context = localContext(cls)
@@ -1691,6 +1692,7 @@ class Namer { typer: Typer =>
16911692
processExports(using localCtx)
16921693
defn.patchStdLibClass(cls)
16931694
addConstructorProxies(cls)
1695+
cleanup()
16941696
}
16951697
}
16961698

tests/pos/i22436/atest.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
object Case1 {
2+
def myProps(transport: ProtocolTransport): Unit = ???
3+
}

tests/pos/i22436/defs.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
object ProtocolTransport
2+
3+
import ProtocolTransport.*
4+
5+
@annotation.nowarn()
6+
class ProtocolTransport()
7+

0 commit comments

Comments
 (0)