Skip to content
This repository
Browse code

SI-6146 More accurate prefixes for sealed subtypes.

When analysing exhaustivity/reachability of type tests
and equality tests, the pattern matcher must construct
a set of sealed subtypes based on the prefix of the
static type of and the set of sealed descendent symbols
of that type.

Previously, it was using `memberType` for this purpose.
In simple cases, this is sufficient:

    scala> class C { class I1; object O { class I2 } }; object D extends C
    defined class C
    defined module D

    scala> typeOf[D.type] memberType typeOf[C#I1].typeSymbol
    res0: u.Type = D.I1

But, as reported in this bug, it fails when there is an
additional level of nesting:

    scala> typeOf[D.type] memberType typeOf[c.O.I2 forSome { val c: C }].typeSymbol
    res5: u.Type = C.O.I2

This commit introduces `nestedMemberType`, which uses `memberType`
recursively up the prefix chain prefix chain.

    scala> nestedMemberType(typeOf[c.O.I2 forSome { val c: C }].typeSymbol, typeOf[D.type], typeOf[C].typeSymbol)
    res6: u.Type = D.O.Id
  • Loading branch information...
commit 55c9b9c280ac9bc36bbac09397c7646f8dcf4583 1 parent 81d8f9d
Jason Zaugg retronym authored
4 src/compiler/scala/tools/nsc/interpreter/IMain.scala
@@ -262,7 +262,9 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
262 262 protected def newCompiler(settings: Settings, reporter: Reporter): ReplGlobal = {
263 263 settings.outputDirs setSingleOutput virtualDirectory
264 264 settings.exposeEmptyPackage.value = true
265   - new Global(settings, reporter) with ReplGlobal
  265 + new Global(settings, reporter) with ReplGlobal {
  266 + override def toString: String = "<global>"
  267 + }
266 268 }
267 269
268 270 /** Parent classloader. Overridable. */
4 src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala
@@ -2818,7 +2818,9 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
2818 2818 // compare to the fully known type `tp` (modulo abstract types),
2819 2819 // so that we can rule out stuff like: sealed trait X[T]; class XInt extends X[Int] --> XInt not valid when enumerating X[String]
2820 2820 // however, must approximate abstract types in
2821   - val subTp = appliedType(pre.memberType(sym), sym.typeParams.map(_ => WildcardType))
  2821 +
  2822 + val memberType = nestedMemberType(sym, pre, tpApprox.typeSymbol.owner)
  2823 + val subTp = appliedType(memberType, sym.typeParams.map(_ => WildcardType))
2822 2824 val subTpApprox = typer.infer.approximateAbstracts(subTp) // TODO: needed?
2823 2825 // patmatDebug("subtp"+(subTpApprox <:< tpApprox, subTpApprox, tpApprox))
2824 2826 if (subTpApprox <:< tpApprox) Some(checkableType(subTp))
51 src/reflect/scala/reflect/internal/Types.scala
@@ -586,9 +586,9 @@ trait Types extends api.Types { self: SymbolTable =>
586 586 * Expands type aliases and converts higher-kinded TypeRefs to PolyTypes.
587 587 * Functions on types are also implemented as PolyTypes.
588 588 *
589   - * Example: (in the below, <List> is the type constructor of List)
590   - * TypeRef(pre, <List>, List()) is replaced by
591   - * PolyType(X, TypeRef(pre, <List>, List(X)))
  589 + * Example: (in the below, `<List>` is the type constructor of List)
  590 + * TypeRef(pre, `<List>`, List()) is replaced by
  591 + * PolyType(X, TypeRef(pre, `<List>`, List(X)))
592 592 */
593 593 def normalize = this // @MAT
594 594
@@ -4974,6 +4974,51 @@ trait Types extends api.Types { self: SymbolTable =>
4974 4974 }
4975 4975 }
4976 4976
  4977 + /**
  4978 + * A more persistent version of `Type#memberType` which does not require
  4979 + * that the symbol is a direct member of the prefix.
  4980 + *
  4981 + * For instance:
  4982 + *
  4983 + * {{{
  4984 + * class C[T] {
  4985 + * sealed trait F[A]
  4986 + * object X {
  4987 + * object S1 extends F[T]
  4988 + * }
  4989 + * class S2 extends F[T]
  4990 + * }
  4991 + * object O extends C[Int] {
  4992 + * def foo(f: F[Int]) = f match {...} // need to enumerate sealed subtypes of the scrutinee here.
  4993 + * }
  4994 + * class S3 extends O.F[String]
  4995 + *
  4996 + * nestedMemberType(<S1>, <O.type>, <C>) = O.X.S1.type
  4997 + * nestedMemberType(<S2>, <O.type>, <C>) = O.S2.type
  4998 + * nestedMemberType(<S3>, <O.type>, <C>) = S3.type
  4999 + * }}}
  5000 + *
  5001 + * @param sym The symbol of the subtype
  5002 + * @param pre The prefix from which the symbol is seen
  5003 + * @param owner
  5004 + */
  5005 + def nestedMemberType(sym: Symbol, pre: Type, owner: Symbol): Type = {
  5006 + def loop(tp: Type): Type =
  5007 + if (tp.isTrivial) tp
  5008 + else if (tp.prefix.typeSymbol isNonBottomSubClass owner) {
  5009 + val widened = tp match {
  5010 + case _: ConstantType => tp // Java enum constants: don't widen to the enum type!
  5011 + case _ => tp.widen // C.X.type widens to C.this.X.type, otherwise `tp asSeenFrom (pre, C)` has no effect.
  5012 + }
  5013 + widened asSeenFrom (pre, tp.typeSymbol.owner)
  5014 + }
  5015 + else loop(tp.prefix) memberType tp.typeSymbol
  5016 +
  5017 + val result = loop(sym.tpeHK)
  5018 + assert(sym.isTerm || result.typeSymbol == sym, s"($result).typeSymbol = ${result.typeSymbol}; expected ${sym}")
  5019 + result
  5020 + }
  5021 +
4977 5022 /** The most deeply nested owner that contains all the symbols
4978 5023 * of thistype or prefixless typerefs/singletype occurrences in given type.
4979 5024 */
1  test/files/pos/t6146.flags
... ... @@ -0,0 +1 @@
  1 +-Xfatal-warnings
60 test/files/pos/t6146.scala
... ... @@ -0,0 +1,60 @@
  1 +// No unreachable or exhaustiveness warnings, please.
  2 +
  3 +//
  4 +// The reported bug
  5 +//
  6 +
  7 +trait AxisCompanion {
  8 + sealed trait Format
  9 + object Format {
  10 + case object Decimal extends Format
  11 + case object Integer extends Format
  12 + // Gives an unrelated warning: The outer reference in this type test cannot be checked at run time.
  13 + //final case class Time( hours: Boolean = false, millis: Boolean = true ) extends Format
  14 + }
  15 +}
  16 +object Axis extends AxisCompanion
  17 +class Axis {
  18 + import Axis._
  19 + def test( f: Format ) = f match {
  20 + case Format.Integer => "Int"
  21 + // case Format.Time( hours, millis ) => "Time"
  22 + case Format.Decimal => "Dec"
  23 + }
  24 +}
  25 +
  26 +
  27 +//
  28 +// Some tricksier variations
  29 +//
  30 +
  31 +trait T1[X] {
  32 + trait T2[Y] {
  33 + sealed trait Format
  34 + object Format {
  35 + case object Decimal extends Format
  36 + case object Integer extends Format
  37 + }
  38 + }
  39 +}
  40 +
  41 +object O1 extends T1[Any] {
  42 + object O2 extends T2[Any] {
  43 +
  44 + }
  45 +}
  46 +
  47 +case object Shorty extends O1.O2.Format
  48 +
  49 +class Test1 {
  50 + import O1.O2._
  51 + val FI: Format.Integer.type = Format.Integer
  52 + def test( f: Format ) = {
  53 + val ff: f.type = f
  54 + ff match {
  55 + case FI => "Int"
  56 + case Format.Decimal => "Dec"
  57 + case Shorty => "Sho"
  58 + }
  59 + }
  60 +}
52 test/files/run/t6146b.check
... ... @@ -0,0 +1,52 @@
  1 +Type in expressions to have them evaluated.
  2 +Type :help for more information.
  3 +
  4 +scala> :power
  5 +** Power User mode enabled - BEEP WHIR GYVE **
  6 +** :phase has been set to 'typer'. **
  7 +** scala.tools.nsc._ has been imported **
  8 +** global._, definitions._ also imported **
  9 +** Try :help, :vals, power.<tab> **
  10 +
  11 +scala> val u = rootMirror.universe; import u._, language._
  12 +u: $r.intp.global.type = <global>
  13 +import u._
  14 +import language._
  15 +
  16 +scala> val S1 = typeOf[c.X.S1.type forSome { val c: C[_] }].typeSymbol.tpeHK
  17 +S1: u.Type = C.X.S1.type
  18 +
  19 +scala> val S2 = typeOf[O.S2].typeSymbol.tpeHK
  20 +S2: u.Type = C.this.S2
  21 +
  22 +scala> val S3 = typeOf[O.S3].typeSymbol.tpeHK
  23 +S3: u.Type = O.S3
  24 +
  25 +scala> val S4 = typeOf[S4].typeSymbol.tpeHK
  26 +S4: u.Type = S4
  27 +
  28 +scala> val F = typeOf[c.F[_] forSome { val c: C[_] }].typeSymbol.tpeHK
  29 +F: u.Type = C.this.F
  30 +
  31 +scala> val fTpe = typeOf[O.type].decl(newTermName("foo")).paramss.head.head.tpe
  32 +fTpe: u.Type = O.F[Int]
  33 +
  34 +scala> def memType(sub: Type, scrut: Type): Type =
  35 + nestedMemberType(sub.typeSymbol, scrut.prefix, scrut.typeSymbol.owner)
  36 +memType: (sub: u.Type, scrut: u.Type)u.Type
  37 +
  38 +scala>
  39 +
  40 +scala> memType(S1, fTpe)
  41 +res0: u.Type = O.X.S1.type
  42 +
  43 +scala> memType(S2, fTpe)
  44 +res1: u.Type = O.S2
  45 +
  46 +scala> memType(S3, fTpe)
  47 +res2: u.Type = O.S3
  48 +
  49 +scala> memType(S4, fTpe)
  50 +res3: u.Type = S4
  51 +
  52 +scala>
39 test/files/run/t6146b.scala
... ... @@ -0,0 +1,39 @@
  1 +import scala.tools.partest.ReplTest
  2 +
  3 +class A {
  4 + sealed trait F[A]
  5 +}
  6 +
  7 +class C[T] extends A {
  8 + sealed trait F[A]
  9 + object X {
  10 + object S1 extends F[T]
  11 + }
  12 + class S2 extends F[T]
  13 +}
  14 +object O extends C[Int] {
  15 + def foo(f: F[Int]) = f match { case X.S1 => }
  16 +
  17 + class S3 extends F[Int]
  18 +}
  19 +class S4 extends O.F[String]
  20 +
  21 +object Test extends ReplTest {
  22 + override def code = """
  23 +:power
  24 +val u = rootMirror.universe; import u._, language._
  25 +val S1 = typeOf[c.X.S1.type forSome { val c: C[_] }].typeSymbol.tpeHK
  26 +val S2 = typeOf[O.S2].typeSymbol.tpeHK
  27 +val S3 = typeOf[O.S3].typeSymbol.tpeHK
  28 +val S4 = typeOf[S4].typeSymbol.tpeHK
  29 +val F = typeOf[c.F[_] forSome { val c: C[_] }].typeSymbol.tpeHK
  30 +val fTpe = typeOf[O.type].decl(newTermName("foo")).paramss.head.head.tpe
  31 +def memType(sub: Type, scrut: Type): Type =
  32 + nestedMemberType(sub.typeSymbol, scrut.prefix, scrut.typeSymbol.owner)
  33 +
  34 +memType(S1, fTpe)
  35 +memType(S2, fTpe)
  36 +memType(S3, fTpe)
  37 +memType(S4, fTpe)
  38 + """.stripMargin.trim
  39 +}

1 comment on commit 55c9b9c

scala-jenkins
Collaborator

Job pr-rangepos-per-commit failed for 55c9b9c (results):


Took 9 s.
sad kitty
to rebuild, comment "PLS REBUILD/pr-rangepos-per-commit@55c9b9c"on PR #2112

Please sign in to comment.
Something went wrong with that request. Please try again.