Skip to content

Commit 3fea947

Browse files
committed
Avoid hoisting local classes
The patch disables hoisting of classes local to a block into the result type of the block. Instead, we widen the result type of the block to one which reflects all refinements made to the parents type of the local class. Test cases in avoid.scala, t1569.scala. The original t1569.scala no longer works. Why is explained in neg/t1569-failedAvoid.scala
1 parent 25a8937 commit 3fea947

File tree

7 files changed

+48
-16
lines changed

7 files changed

+48
-16
lines changed

src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,21 @@ trait TypeAssigner {
4949
case TypeAlias(ref) =>
5050
apply(ref)
5151
case info: ClassInfo =>
52-
mapOver(info.instantiatedParents.reduceLeft(ctx.typeComparer.andType(_, _)))
52+
val parentType = info.instantiatedParents.reduceLeft(ctx.typeComparer.andType(_, _))
53+
def addRefinement(parent: Type, decl: Symbol) = {
54+
val inherited = parentType.findMember(decl.name, info.cls.thisType, Private)
55+
val inheritedInfo = inherited.atSignature(decl.info .signature).info
56+
if (inheritedInfo.exists && decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info))
57+
typr.echo(
58+
i"add ref $parent $decl --> ",
59+
RefinedType(parent, decl.name, decl.info))
60+
else
61+
parent
62+
}
63+
val refinableDecls = info.decls.filterNot(
64+
sym => sym.is(TypeParamAccessor | Private) || sym.isConstructor)
65+
val fullType = (parentType /: refinableDecls)(addRefinement)
66+
mapOver(fullType)
5367
case _ =>
5468
mapOver(tp)
5569
}

src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -415,17 +415,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
415415
def escapingRefs(block: Block)(implicit ctx: Context): collection.Set[NamedType] = {
416416
var hoisted: Set[Symbol] = Set()
417417
lazy val locals = localSyms(block.stats).toSet
418-
def isLocal(sym: Symbol): Boolean =
419-
(locals contains sym) && !isHoistableClass(sym)
420-
def isHoistableClass(sym: Symbol) =
421-
sym.isClass && {
422-
(hoisted contains sym) || {
423-
hoisted += sym
424-
!classLeaks(sym.asClass)
425-
}
426-
}
427418
def leakingTypes(tp: Type): collection.Set[NamedType] =
428-
tp namedPartsWith (tp => isLocal(tp.symbol))
419+
tp namedPartsWith (tp => locals.contains(tp.symbol))
429420
def typeLeaks(tp: Type): Boolean = leakingTypes(tp).nonEmpty
430421
def classLeaks(sym: ClassSymbol): Boolean =
431422
(ctx.owner is Method) || // can't hoist classes out of method bodies

test/dotc/tests.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ class tests extends CompilerTest {
9999
@Test def neg_variances = compileFile(negDir, "variances", xerrors = 2)
100100
@Test def neg_badAuxConstr = compileFile(negDir, "badAuxConstr", xerrors = 2)
101101
@Test def neg_typetest = compileFile(negDir, "typetest", xerrors = 1)
102+
@Test def neg_t1569_failedAvoid = compileFile(negDir, "t1569-failedAvoid", xerrors = 1)
102103
@Test def dotc = compileDir(dotcDir + "tools/dotc", twice)(allowDeepSubtypes)
103104
@Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast", twice)
104105
@Test def dotc_config = compileDir(dotcDir + "tools/dotc/config", twice)

tests/neg/t1569-failedAvoid.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// This was t1569.scala.
2+
// It fails in dotty because the expected type of the anonymous function in the last line
3+
// is fully determined (C). So that type is taken as the type of the anonymous function.
4+
// See pos/t1569a.scala for related examples that work.
5+
object Bug {
6+
class C { type T }
7+
def foo(x: Int)(y: C)(z: y.T): Unit = {}
8+
foo(3)(new C { type T = String })("hello")
9+
}

tests/pos/avoid.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
abstract class C {
2+
def y: Any
3+
}
4+
5+
object test {
6+
val x = new C{
7+
def y: String = "abc"
8+
}
9+
val z: String = x.y
10+
}

tests/pos/t1569.scala

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/pos/t1569a.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
object Bug {
2+
class C[T] { type TT = T }
3+
def foo[U](x: Int)(y: C[U])(z: y.TT): Unit = {}
4+
foo(3)(new C[String])("hello")
5+
}
6+
7+
object Bug2 {
8+
class C { type T }
9+
class D extends C { type T = String }
10+
def foo(x: Int)(y: C)(z: y.T): Unit = {}
11+
foo(3)(new D)("hello")
12+
}

0 commit comments

Comments
 (0)