Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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
@retronym retronym authored
View
4 src/compiler/scala/tools/nsc/interpreter/IMain.scala
@@ -262,7 +262,9 @@ class IMain(initialSettings: Settings, protected val out: JPrintWriter) extends
protected def newCompiler(settings: Settings, reporter: Reporter): ReplGlobal = {
settings.outputDirs setSingleOutput virtualDirectory
settings.exposeEmptyPackage.value = true
- new Global(settings, reporter) with ReplGlobal
+ new Global(settings, reporter) with ReplGlobal {
+ override def toString: String = "<global>"
+ }
}
/** Parent classloader. Overridable. */
View
4 src/compiler/scala/tools/nsc/typechecker/PatternMatching.scala
@@ -2818,7 +2818,9 @@ trait PatternMatching extends Transform with TypingTransformers with ast.TreeDSL
// compare to the fully known type `tp` (modulo abstract types),
// so that we can rule out stuff like: sealed trait X[T]; class XInt extends X[Int] --> XInt not valid when enumerating X[String]
// however, must approximate abstract types in
- val subTp = appliedType(pre.memberType(sym), sym.typeParams.map(_ => WildcardType))
+
+ val memberType = nestedMemberType(sym, pre, tpApprox.typeSymbol.owner)
+ val subTp = appliedType(memberType, sym.typeParams.map(_ => WildcardType))
val subTpApprox = typer.infer.approximateAbstracts(subTp) // TODO: needed?
// patmatDebug("subtp"+(subTpApprox <:< tpApprox, subTpApprox, tpApprox))
if (subTpApprox <:< tpApprox) Some(checkableType(subTp))
View
51 src/reflect/scala/reflect/internal/Types.scala
@@ -586,9 +586,9 @@ trait Types extends api.Types { self: SymbolTable =>
* Expands type aliases and converts higher-kinded TypeRefs to PolyTypes.
* Functions on types are also implemented as PolyTypes.
*
- * Example: (in the below, <List> is the type constructor of List)
- * TypeRef(pre, <List>, List()) is replaced by
- * PolyType(X, TypeRef(pre, <List>, List(X)))
+ * Example: (in the below, `<List>` is the type constructor of List)
+ * TypeRef(pre, `<List>`, List()) is replaced by
+ * PolyType(X, TypeRef(pre, `<List>`, List(X)))
*/
def normalize = this // @MAT
@@ -4974,6 +4974,51 @@ trait Types extends api.Types { self: SymbolTable =>
}
}
+ /**
+ * A more persistent version of `Type#memberType` which does not require
+ * that the symbol is a direct member of the prefix.
+ *
+ * For instance:
+ *
+ * {{{
+ * class C[T] {
+ * sealed trait F[A]
+ * object X {
+ * object S1 extends F[T]
+ * }
+ * class S2 extends F[T]
+ * }
+ * object O extends C[Int] {
+ * def foo(f: F[Int]) = f match {...} // need to enumerate sealed subtypes of the scrutinee here.
+ * }
+ * class S3 extends O.F[String]
+ *
+ * nestedMemberType(<S1>, <O.type>, <C>) = O.X.S1.type
+ * nestedMemberType(<S2>, <O.type>, <C>) = O.S2.type
+ * nestedMemberType(<S3>, <O.type>, <C>) = S3.type
+ * }}}
+ *
+ * @param sym The symbol of the subtype
+ * @param pre The prefix from which the symbol is seen
+ * @param owner
+ */
+ def nestedMemberType(sym: Symbol, pre: Type, owner: Symbol): Type = {
+ def loop(tp: Type): Type =
+ if (tp.isTrivial) tp
+ else if (tp.prefix.typeSymbol isNonBottomSubClass owner) {
+ val widened = tp match {
+ case _: ConstantType => tp // Java enum constants: don't widen to the enum type!
+ case _ => tp.widen // C.X.type widens to C.this.X.type, otherwise `tp asSeenFrom (pre, C)` has no effect.
+ }
+ widened asSeenFrom (pre, tp.typeSymbol.owner)
+ }
+ else loop(tp.prefix) memberType tp.typeSymbol
+
+ val result = loop(sym.tpeHK)
+ assert(sym.isTerm || result.typeSymbol == sym, s"($result).typeSymbol = ${result.typeSymbol}; expected ${sym}")
+ result
+ }
+
/** The most deeply nested owner that contains all the symbols
* of thistype or prefixless typerefs/singletype occurrences in given type.
*/
View
1  test/files/pos/t6146.flags
@@ -0,0 +1 @@
+-Xfatal-warnings
View
60 test/files/pos/t6146.scala
@@ -0,0 +1,60 @@
+// No unreachable or exhaustiveness warnings, please.
+
+//
+// The reported bug
+//
+
+trait AxisCompanion {
+ sealed trait Format
+ object Format {
+ case object Decimal extends Format
+ case object Integer extends Format
+ // Gives an unrelated warning: The outer reference in this type test cannot be checked at run time.
+ //final case class Time( hours: Boolean = false, millis: Boolean = true ) extends Format
+ }
+}
+object Axis extends AxisCompanion
+class Axis {
+ import Axis._
+ def test( f: Format ) = f match {
+ case Format.Integer => "Int"
+ // case Format.Time( hours, millis ) => "Time"
+ case Format.Decimal => "Dec"
+ }
+}
+
+
+//
+// Some tricksier variations
+//
+
+trait T1[X] {
+ trait T2[Y] {
+ sealed trait Format
+ object Format {
+ case object Decimal extends Format
+ case object Integer extends Format
+ }
+ }
+}
+
+object O1 extends T1[Any] {
+ object O2 extends T2[Any] {
+
+ }
+}
+
+case object Shorty extends O1.O2.Format
+
+class Test1 {
+ import O1.O2._
+ val FI: Format.Integer.type = Format.Integer
+ def test( f: Format ) = {
+ val ff: f.type = f
+ ff match {
+ case FI => "Int"
+ case Format.Decimal => "Dec"
+ case Shorty => "Sho"
+ }
+ }
+}
View
52 test/files/run/t6146b.check
@@ -0,0 +1,52 @@
+Type in expressions to have them evaluated.
+Type :help for more information.
+
+scala> :power
+** Power User mode enabled - BEEP WHIR GYVE **
+** :phase has been set to 'typer'. **
+** scala.tools.nsc._ has been imported **
+** global._, definitions._ also imported **
+** Try :help, :vals, power.<tab> **
+
+scala> val u = rootMirror.universe; import u._, language._
+u: $r.intp.global.type = <global>
+import u._
+import language._
+
+scala> val S1 = typeOf[c.X.S1.type forSome { val c: C[_] }].typeSymbol.tpeHK
+S1: u.Type = C.X.S1.type
+
+scala> val S2 = typeOf[O.S2].typeSymbol.tpeHK
+S2: u.Type = C.this.S2
+
+scala> val S3 = typeOf[O.S3].typeSymbol.tpeHK
+S3: u.Type = O.S3
+
+scala> val S4 = typeOf[S4].typeSymbol.tpeHK
+S4: u.Type = S4
+
+scala> val F = typeOf[c.F[_] forSome { val c: C[_] }].typeSymbol.tpeHK
+F: u.Type = C.this.F
+
+scala> val fTpe = typeOf[O.type].decl(newTermName("foo")).paramss.head.head.tpe
+fTpe: u.Type = O.F[Int]
+
+scala> def memType(sub: Type, scrut: Type): Type =
+ nestedMemberType(sub.typeSymbol, scrut.prefix, scrut.typeSymbol.owner)
+memType: (sub: u.Type, scrut: u.Type)u.Type
+
+scala>
+
+scala> memType(S1, fTpe)
+res0: u.Type = O.X.S1.type
+
+scala> memType(S2, fTpe)
+res1: u.Type = O.S2
+
+scala> memType(S3, fTpe)
+res2: u.Type = O.S3
+
+scala> memType(S4, fTpe)
+res3: u.Type = S4
+
+scala>
View
39 test/files/run/t6146b.scala
@@ -0,0 +1,39 @@
+import scala.tools.partest.ReplTest
+
+class A {
+ sealed trait F[A]
+}
+
+class C[T] extends A {
+ sealed trait F[A]
+ object X {
+ object S1 extends F[T]
+ }
+ class S2 extends F[T]
+}
+object O extends C[Int] {
+ def foo(f: F[Int]) = f match { case X.S1 => }
+
+ class S3 extends F[Int]
+}
+class S4 extends O.F[String]
+
+object Test extends ReplTest {
+ override def code = """
+:power
+val u = rootMirror.universe; import u._, language._
+val S1 = typeOf[c.X.S1.type forSome { val c: C[_] }].typeSymbol.tpeHK
+val S2 = typeOf[O.S2].typeSymbol.tpeHK
+val S3 = typeOf[O.S3].typeSymbol.tpeHK
+val S4 = typeOf[S4].typeSymbol.tpeHK
+val F = typeOf[c.F[_] forSome { val c: C[_] }].typeSymbol.tpeHK
+val fTpe = typeOf[O.type].decl(newTermName("foo")).paramss.head.head.tpe
+def memType(sub: Type, scrut: Type): Type =
+ nestedMemberType(sub.typeSymbol, scrut.prefix, scrut.typeSymbol.owner)
+
+memType(S1, fTpe)
+memType(S2, fTpe)
+memType(S3, fTpe)
+memType(S4, fTpe)
+ """.stripMargin.trim
+}

1 comment on commit 55c9b9c

@scala-jenkins

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.