From 48acd08ac7e6346ffa573163fc1993cbdae458cd Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Thu, 1 Oct 2015 20:31:06 +0200 Subject: TypeAssigner#avoid: more precise types for inner classes When we need to avoid `A` in the class `A#B`, we can try to replace `A` by a supertype. Previously, we only tried to replace `A#B` itself by a supertype. Fixes #711. --- tests/pos/escapingRefs.scala | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/pos/escapingRefs.scala (limited to 'tests/pos') diff --git a/tests/pos/escapingRefs.scala b/tests/pos/escapingRefs.scala new file mode 100644 index 000000000..a7960bee4 --- /dev/null +++ b/tests/pos/escapingRefs.scala @@ -0,0 +1,12 @@ +class Outer { + class Inner +} + +object Test { + def test = { + val a: Outer#Inner = { + val o = new Outer + new o.Inner + } + } +} -- cgit v1.2.3 From c9c8f223f832a602f4487f3a6a5b75b8affe6280 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 2 Oct 2015 11:56:44 +0200 Subject: TypeAssigner#avoid: don't miss escaping refs in complex types --- src/dotty/tools/dotc/typer/TypeAssigner.scala | 25 +++++++++++-------------- tests/pos/escapingRefs.scala | 10 +++++++++- 2 files changed, 20 insertions(+), 15 deletions(-) (limited to 'tests/pos') diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 496380b3a..22c062243 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -42,22 +42,19 @@ trait TypeAssigner { def avoid(tp: Type, symsToAvoid: => List[Symbol])(implicit ctx: Context): Type = { val widenMap = new TypeMap { lazy val forbidden = symsToAvoid.toSet - def toAvoid(tp: Type): Boolean = tp match { - case tp: TermRef => - val sym = tp.symbol - sym.exists && ( - sym.owner.isTerm && (forbidden contains sym) - || !(sym.owner is Package) && toAvoid(tp.prefix) - ) - case tp: TypeRef => - forbidden contains tp.symbol - case _ => - false - } + def toAvoid(tp: Type): Boolean = + // TODO: measure the cost of using `existsPart`, and if necessary replace it + // by a `TypeAccumulator` where we have set `stopAtStatic = true`. + tp existsPart { + case tp: NamedType => + forbidden contains tp.symbol + case _ => + false + } def apply(tp: Type): Type = tp match { case tp: TermRef if toAvoid(tp) && variance > 0 => apply(tp.info.widenExpr) - case tp: TypeRef if (forbidden contains tp.symbol) || toAvoid(tp.prefix) => + case tp: TypeRef if toAvoid(tp) => tp.info match { case TypeAlias(ref) => apply(ref) @@ -92,7 +89,7 @@ trait TypeAssigner { } case tp: RefinedType => val tp1 @ RefinedType(parent1, _) = mapOver(tp) - if (tp1.refinedInfo.existsPart(toAvoid) && variance > 0) { + if (toAvoid(tp1.refinedInfo) && variance > 0) { typr.println(s"dropping refinement from $tp1") parent1 } diff --git a/tests/pos/escapingRefs.scala b/tests/pos/escapingRefs.scala index a7960bee4..c18720764 100644 --- a/tests/pos/escapingRefs.scala +++ b/tests/pos/escapingRefs.scala @@ -1,5 +1,7 @@ class Outer { - class Inner + class Inner { + class Inner2 + } } object Test { @@ -8,5 +10,11 @@ object Test { val o = new Outer new o.Inner } + + val b: Outer#Inner#Inner2 = { + val o = new Outer + val i = new o.Inner + new i.Inner2 + } } } -- cgit v1.2.3 From d1014c3a2cf87b783f383e52b8923a42ee1bc31c Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 2 Oct 2015 15:10:03 +0200 Subject: Add pos test about escaping refinements --- tests/pos/escapingRefs.scala | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'tests/pos') diff --git a/tests/pos/escapingRefs.scala b/tests/pos/escapingRefs.scala index c18720764..684dba868 100644 --- a/tests/pos/escapingRefs.scala +++ b/tests/pos/escapingRefs.scala @@ -4,6 +4,8 @@ class Outer { } } +class HasA { type A } + object Test { def test = { val a: Outer#Inner = { @@ -16,5 +18,13 @@ object Test { val i = new o.Inner new i.Inner2 } + + val c: HasA { type A = Int } = { + val h = new HasA { + type A = Int + } + val x: HasA { type A = h.A } = h + x + } } } -- cgit v1.2.3 From 82a6d6f52065ae39a0a162f95e5002a6ab26cc55 Mon Sep 17 00:00:00 2001 From: Guillaume Martres Date: Fri, 2 Oct 2015 23:25:30 +0200 Subject: TypeAssigner#avoid: do not lose type parameters when the base type changes. Fixes #741. --- src/dotty/tools/dotc/typer/TypeAssigner.scala | 27 +++++++++++++++++++++------ tests/pos/escapingRefs.scala | 12 ++++++++++++ 2 files changed, 33 insertions(+), 6 deletions(-) (limited to 'tests/pos') diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 22c062243..7225ede14 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -87,13 +87,28 @@ trait TypeAssigner { case _ => mapOver(tp) } - case tp: RefinedType => - val tp1 @ RefinedType(parent1, _) = mapOver(tp) - if (toAvoid(tp1.refinedInfo) && variance > 0) { - typr.println(s"dropping refinement from $tp1") - parent1 + case tp @ RefinedType(parent, name) if variance > 0 => + // The naive approach here would be to first approximate the parent, + // but if the base type of the approximated parent is different from + // the current base type, then the current refinement won't be valid + // if it's a type parameter refinement. + // Therefore we first approximate the base type, then use `baseArgInfos` + // to get correct refinements for the approximated base type, then + // recursively approximate the resulting type. + val base = tp.unrefine + if (toAvoid(base)) { + val base1 = apply(base) + apply(base1.appliedTo(tp.baseArgInfos(base1.typeSymbol))) + } else { + val parent1 = apply(tp.parent) + val refinedInfo1 = apply(tp.refinedInfo) + if (toAvoid(refinedInfo1)) { + typr.println(s"dropping refinement from $tp") + parent1 + } else { + tp.derivedRefinedType(parent1, name, refinedInfo1) + } } - else tp1 case tp: TypeVar if ctx.typerState.constraint.contains(tp) => val lo = ctx.typerState.constraint.fullLowerBound(tp.origin) val lo1 = avoid(lo, symsToAvoid) diff --git a/tests/pos/escapingRefs.scala b/tests/pos/escapingRefs.scala index 684dba868..1b1deb8de 100644 --- a/tests/pos/escapingRefs.scala +++ b/tests/pos/escapingRefs.scala @@ -6,6 +6,8 @@ class Outer { class HasA { type A } +class Foo[A] + object Test { def test = { val a: Outer#Inner = { @@ -26,5 +28,15 @@ object Test { val x: HasA { type A = h.A } = h x } + + val d: Foo[Int] = { + class Bar[B] extends Foo[B] + new Bar[Int] + } + + val e: Foo[_] = { + class Bar[B] extends Foo[B] + new Bar[Int]: Bar[_ <: Int] + } } } -- cgit v1.2.3