From 3fea9472c0d068bc08ae764429ca6b4bca95bcd8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 17 Oct 2014 15:35:11 +0200 Subject: 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 --- src/dotty/tools/dotc/typer/TypeAssigner.scala | 16 +++++++++++++++- src/dotty/tools/dotc/typer/Typer.scala | 11 +---------- test/dotc/tests.scala | 1 + tests/neg/t1569-failedAvoid.scala | 9 +++++++++ tests/pos/avoid.scala | 10 ++++++++++ tests/pos/t1569.scala | 5 ----- tests/pos/t1569a.scala | 12 ++++++++++++ 7 files changed, 48 insertions(+), 16 deletions(-) create mode 100644 tests/neg/t1569-failedAvoid.scala create mode 100644 tests/pos/avoid.scala delete mode 100644 tests/pos/t1569.scala create mode 100644 tests/pos/t1569a.scala diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index ccf67b55b..cb6fefab1 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -49,7 +49,21 @@ trait TypeAssigner { case TypeAlias(ref) => apply(ref) case info: ClassInfo => - mapOver(info.instantiatedParents.reduceLeft(ctx.typeComparer.andType(_, _))) + val parentType = info.instantiatedParents.reduceLeft(ctx.typeComparer.andType(_, _)) + def addRefinement(parent: Type, decl: Symbol) = { + val inherited = parentType.findMember(decl.name, info.cls.thisType, Private) + val inheritedInfo = inherited.atSignature(decl.info .signature).info + if (inheritedInfo.exists && decl.info <:< inheritedInfo && !(inheritedInfo <:< decl.info)) + typr.echo( + i"add ref $parent $decl --> ", + RefinedType(parent, decl.name, decl.info)) + else + parent + } + val refinableDecls = info.decls.filterNot( + sym => sym.is(TypeParamAccessor | Private) || sym.isConstructor) + val fullType = (parentType /: refinableDecls)(addRefinement) + mapOver(fullType) case _ => mapOver(tp) } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 80eb5965c..1cab2fa46 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -415,17 +415,8 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def escapingRefs(block: Block)(implicit ctx: Context): collection.Set[NamedType] = { var hoisted: Set[Symbol] = Set() lazy val locals = localSyms(block.stats).toSet - def isLocal(sym: Symbol): Boolean = - (locals contains sym) && !isHoistableClass(sym) - def isHoistableClass(sym: Symbol) = - sym.isClass && { - (hoisted contains sym) || { - hoisted += sym - !classLeaks(sym.asClass) - } - } def leakingTypes(tp: Type): collection.Set[NamedType] = - tp namedPartsWith (tp => isLocal(tp.symbol)) + tp namedPartsWith (tp => locals.contains(tp.symbol)) def typeLeaks(tp: Type): Boolean = leakingTypes(tp).nonEmpty def classLeaks(sym: ClassSymbol): Boolean = (ctx.owner is Method) || // can't hoist classes out of method bodies diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 614dc9527..f93f36ced 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -99,6 +99,7 @@ class tests extends CompilerTest { @Test def neg_variances = compileFile(negDir, "variances", xerrors = 2) @Test def neg_badAuxConstr = compileFile(negDir, "badAuxConstr", xerrors = 2) @Test def neg_typetest = compileFile(negDir, "typetest", xerrors = 1) + @Test def neg_t1569_failedAvoid = compileFile(negDir, "t1569-failedAvoid", xerrors = 1) @Test def dotc = compileDir(dotcDir + "tools/dotc", twice)(allowDeepSubtypes) @Test def dotc_ast = compileDir(dotcDir + "tools/dotc/ast", twice) @Test def dotc_config = compileDir(dotcDir + "tools/dotc/config", twice) diff --git a/tests/neg/t1569-failedAvoid.scala b/tests/neg/t1569-failedAvoid.scala new file mode 100644 index 000000000..9d0fbb37a --- /dev/null +++ b/tests/neg/t1569-failedAvoid.scala @@ -0,0 +1,9 @@ +// This was t1569.scala. +// It fails in dotty because the expected type of the anonymous function in the last line +// is fully determined (C). So that type is taken as the type of the anonymous function. +// See pos/t1569a.scala for related examples that work. +object Bug { + class C { type T } + def foo(x: Int)(y: C)(z: y.T): Unit = {} + foo(3)(new C { type T = String })("hello") +} diff --git a/tests/pos/avoid.scala b/tests/pos/avoid.scala new file mode 100644 index 000000000..51471feaa --- /dev/null +++ b/tests/pos/avoid.scala @@ -0,0 +1,10 @@ +abstract class C { + def y: Any +} + +object test { + val x = new C{ + def y: String = "abc" + } + val z: String = x.y +} diff --git a/tests/pos/t1569.scala b/tests/pos/t1569.scala deleted file mode 100644 index a7200a6d1..000000000 --- a/tests/pos/t1569.scala +++ /dev/null @@ -1,5 +0,0 @@ -object Bug { - class C { type T } - def foo(x: Int)(y: C)(z: y.T): Unit = {} - foo(3)(new C { type T = String })("hello") -} diff --git a/tests/pos/t1569a.scala b/tests/pos/t1569a.scala new file mode 100644 index 000000000..6cc3619a4 --- /dev/null +++ b/tests/pos/t1569a.scala @@ -0,0 +1,12 @@ +object Bug { + class C[T] { type TT = T } + def foo[U](x: Int)(y: C[U])(z: y.TT): Unit = {} + foo(3)(new C[String])("hello") +} + +object Bug2 { + class C { type T } + class D extends C { type T = String } + def foo(x: Int)(y: C)(z: y.T): Unit = {} + foo(3)(new D)("hello") +} -- cgit v1.2.3