Skip to content

Ycheck error in FirstTransform #6655

Closed
@abeln

Description

@abeln
class Test {
  def foo(): Unit = {
    val x: String|Null = ???
    if (x != null) {
      val y = x.length
    } else {
      val y = x
    }
  }
}

dotc -Ycheck:firstTransform kk.scala

Expected: the code should typecheck.
Got: exception

} of class class dotty.tools.dotc.ast.Trees$PackageDef # 1147
*** error while checking kk.scala after phase completeJavaEnums ***
exception occurred while compiling kk.scala
java.lang.AssertionError: assertion failed: non member selection of method length in class String from (String | Null)(x) in x.length while compiling kk.scala
Exception in thread "main" java.lang.AssertionError: assertion failed: non member selection of method length in class String from (String | Null)(x) in x.length
	at dotty.DottyPredef$.assertFail(DottyPredef.scala:16)
	at dotty.tools.dotc.transform.FirstTransform.checkPostCondition(FirstTransform.scala:53)
	at dotty.tools.dotc.transform.TreeChecker.dotty$tools$dotc$transform$TreeChecker$Checker$$_$typedUnadapted$$anonfun$1(TreeChecker.scala:284)
	at dotty.runtime.function.JProcedure1.apply(JProcedure1.java:15)
	at dotty.runtime.function.JProcedure1.apply(JProcedure1.java:10)
	at scala.collection.immutable.List.foreach(List.scala:392)
	at dotty.tools.dotc.transform.TreeChecker$Checker.typedUnadapted(TreeChecker.scala:284)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2239)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:2251)
	at dotty.tools.dotc.transform.TreeChecker$Checker.typed(TreeChecker.scala:255)

If we look at the failed assertion, it's

  override def checkPostCondition(tree: Tree)(implicit ctx: Context): Unit = {
    tree match {
      case Select(qual, name) if !name.is(OuterSelectName) && tree.symbol.exists =>
        assert(
          qual.tpe.derivesFrom(tree.symbol.owner) ||
            tree.symbol.is(JavaStatic) && qual.tpe.derivesFrom(tree.symbol.enclosingClass),
          i"non member selection of ${tree.symbol.showLocated} from ${qual.tpe} in $tree")

Here qual.tpe is String|Null while tree.symbol.owner is String.
I think the problem is that probably String|Null <: String, but Null does not deriveFrom String.
Indeed, looking at SymDenotations it seems like Null is special-cased in isSubClass but not in derivesFrom. This seems to be by design

    /** Is this a subclass of the given class `base`? */
    def isSubClass(base: Symbol)(implicit ctx: Context): Boolean = false

    /** Is this a subclass of `base`,
     *  and is the denoting symbol also different from `Null` or `Nothing`?
     *  @note  erroneous classes are assumed to derive from all other classes
     *         and all classes derive from them.
     */
    def derivesFrom(base: Symbol)(implicit ctx: Context): Boolean = fals

So maybe we should just switch to using isSubClass?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions