Skip to content

Commit a56afcd

Browse files
committed
Type#contains should peer into RefinementTypeRef-s
Usually, `contains` should not look into class symbol infos. For instance, we expect that: ``` scala> trait C { def foo: Int }; typeOf[C].contains(IntClass) defined trait C res1: Boolean = false ``` We do, however, look at the decls of a `RefinedType` in contains: ``` scala> typeOf[{ def foo: Int }].contains(IntClass) res2: Boolean = true ``` Things get a little vague, however, when we consider a type ref to the refinement class symbol of a refined type. ``` scala> TypeRef(NoPrefix, typeOf[{ def foo: Int }].typeSymbol, Nil) res3: $r.intp.global.Type = AnyRef{def foo: Int} scala> .contains(IntClass) res4: Boolean = false ``` These show up in the first element of the base type seq of a refined type, e.g: ``` scala> typeOf[{ def foo: Int }].typeSymbol.tpe_* res5: $r.intp.global.Type = AnyRef{def foo: Int} scala> typeOf[{ def foo: Int }].baseTypeSeq(0).getClass res7: Class[_ <: $r.intp.global.Type] = class scala.reflect.internal.Types$RefinementTypeRef scala> typeOf[{ def foo: Int }].typeSymbol.tpe_*.getClass res6: Class[_ <: $r.intp.global.Type] = class scala.reflect.internal.Types$RefinementTypeRef ``` This commit takes the opinion that a `RefinementTypeRef` should be transparent with respect to `contains`. This paves the way for fixing the base type sequences of existential types over refinement types. The implementation of `ContainsCollector` was already calling `normalize`, which goes from `RefinementTypeRef` to `RefinedType`. This commit maps over the result, which looks in the parents and decls.
1 parent c141254 commit a56afcd

File tree

4 files changed

+36
-6
lines changed

4 files changed

+36
-6
lines changed

src/compiler/scala/tools/nsc/typechecker/MethodSynthesis.scala

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -321,13 +321,20 @@ trait MethodSynthesis {
321321
// starts compiling (instead of failing like it's supposed to) because the typer
322322
// expects to be able to identify escaping locals in typedDefDef, and fails to
323323
// spot that brand of them. In other words it's an artifact of the implementation.
324+
//
325+
// JZ: ... or we could go back to uniformly using explicit result types in all cases
326+
// if we fix `dropExistential`. More details https://github.com/scala/scala-dev/issues/165
324327
val getterTp = derivedSym.tpe_*.finalResultType
325-
val tpt = getterTp.widen match {
326-
// Range position errors ensue if we don't duplicate this in some
327-
// circumstances (at least: concrete vals with existential types.)
328-
case _: ExistentialType => TypeTree() setOriginal (tree.tpt.duplicate setPos tree.tpt.pos.focus)
329-
case _ if tree.mods.isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field
330-
case _ => TypeTree(getterTp)
328+
// Range position errors ensue if we don't duplicate this in some
329+
// circumstances (at least: concrete vals with existential types.)
330+
def inferredTpt = TypeTree() setOriginal (tree.tpt.duplicate setPos tree.tpt.pos.focus)
331+
val tpt = getterTp match {
332+
case _: ExistentialType => inferredTpt
333+
case _ => getterTp.widen match {
334+
case _: ExistentialType => inferredTpt
335+
case _ if tree.mods.isDeferred => TypeTree() setOriginal tree.tpt // keep type tree of original abstract field
336+
case _ => TypeTree(getterTp)
337+
}
331338
}
332339
tpt setPos tree.tpt.pos.focus
333340
}

src/reflect/scala/reflect/internal/tpe/TypeMaps.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,9 @@ private[internal] trait TypeMaps {
10121012
case _ =>
10131013
tp.normalize match {
10141014
case TypeRef(_, sym1, _) if (sym == sym1) => result = true
1015+
case refined: RefinedType =>
1016+
mapOver(tp.prefix)
1017+
mapOver(refined)
10151018
case SingleType(_, sym1) if (sym == sym1) => result = true
10161019
case _ => mapOver(tp)
10171020
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
trait Test1 {
2+
abstract class Setting
3+
def Bool: Setting
4+
5+
class C[T <: Setting](val s: T)
6+
val setting1 = null.asInstanceOf[_1.s.type forSome { val _1: C[Setting] }]
7+
// the derived accessor for this val was not using an inferred type, as was
8+
// the intention of the implementation in MethodSynthesis.
9+
}

test/junit/scala/reflect/internal/TypesTest.scala

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,15 @@ class TypesTest {
5858
Assert.fail(xs.mkString("\n"))
5959
}
6060
}
61+
62+
@Test
63+
def testRefinementContains(): Unit = {
64+
val refinement = typeOf[{def foo: Int}]
65+
assert(refinement.isInstanceOf[RefinedType])
66+
assert(refinement.contains(IntClass))
67+
val elem0 = refinement.baseTypeSeq(0)
68+
assert(elem0.isInstanceOf[RefinementTypeRef])
69+
assert(elem0.contains(IntClass))
70+
}
71+
6172
}

0 commit comments

Comments
 (0)