scala / bug

Scala 2 bug reports only. Please, no questions — proper bug reports only.
https://scala-lang.org
230 stars 21 forks source link

Raw types in Java .class files are sometimes not cooked, depending on symbol initialization order #12856

Open retronym opened 10 months ago

retronym commented 10 months ago

Reproduction steps

// J.java
abstract class Base<P> {
}

class Sub<V> extends Base<PolyClass> {
}

class PolyClass<T> {
}

class J {
  static void m(Base te) {}
}
// Test.scala
class Test {
  // workaround 1: refer to Column before referring to ReferenceDataEnum.
  // Then the call to symbolOf[Column].unsafeTypeParams in ClassFileParser
  // tells it that it polymorphic, and hence the arg-less reference in the
  // `extends TypedEnum<Column>` is a raw type which must be cooked.
  //
  // new PolyClass[AnyRef]()

  // workaround 2: jointly compile J.java with Test.scala
  def foo[A](sub: Sub[A]) = {
    //    error: type mismatch;
    //    found   : <empty>.this.Sub[A&0]
    //    required: <empty>.this.Base[P]( forSome { type P })
    J.m(sub)
  }
}
$ javac -d /tmp/out /Users/jz/code/scala/test/files/pos/java-raw-f-bound/J.java && scalac --scala-version 2.13.8 -cp /tmp/out -d /tmp/out /Users/jz/code/scala/test/files/pos/java-raw-f-bound/*.scala  -explaintypes -Ydebug
[running phase parser on Test.scala]
[running phase namer on Test.scala]
[running phase packageobjects on Test.scala]
[running phase typer on Test.scala]
/Users/jz/code/scala/test/files/pos/java-raw-f-bound/Test.scala:14: error: type mismatch;
 found   : <empty>.this.Sub[A&0]
 required: <empty>.this.Base[P]( forSome { type P })
Note: A&0 <: lang.this.Object, but Java-defined class Base is invariant in type P.
You may wish to investigate a wildcard type such as `_ <: lang.this.Object`. (SLS 3.2.10)
    J.m(rde)
        ^
warning: !!! HK subtype check on ? and <empty>.this.PolyClass, but both don't normalize to polytypes:
  tp1=?                    BoundedWildcardType
  tp2=[T]<empty>.this.PolyClass[T] PolyType
warning: !!! HK subtype check on ?0P and <empty>.this.PolyClass, but both don't normalize to polytypes:
  tp1=?0P                  TypeVar
  tp2=[T]<empty>.this.PolyClass[T] PolyType
2 warnings
1 error

Problem

Compilation should give the same result, irrespective of the order of initialization of symbols. In this case, that result should be successful.

Relevant comment from ClassFileParser


      // we could avoid this if we eagerly created class type param symbols here to expose through the
      // ClassTypeCompleter to satisfy the calls to rawInfo.typeParams from Symbol.typeParams. That would
      // require a refactor of `sigToType`.
      //
      // We would also need to make sure that clazzTParams is populated before member type completers called sig2type.
      clazz.initialize

ClassfileParser.sigToType::processClassType

              // isMonomorphicType is false if the info is incomplete, as it usually is here
              // so have to check unsafeTypeParams.isEmpty before worrying about raw type case below,
              // or we'll create a boatload of needless existentials.
              else if (classSym.isMonomorphicType || {classSym.rawInfo.load(classSym); classSym.unsafeTypeParams.isEmpty}) tp
              else debuglogResult(s"raw type from $classSym") {
                // raw type - existentially quantify all type parameters
                classExistentialType(pre, classSym)
              }
lrytz commented 6 months ago

I looked a bit, tricky stuff :)

The comment about calling sym.initialize in parseClass brings up the idea to know type params before completing the ClassTypeCompleter. That sounds like a valid cleanup (no need to call initialize eagerly).

The issue here however is that PolyClass still has the lazy type before that (ClassfileLoader), so it would not help.

I don't have any other idea than your suggestion to complete classSym before checking for type params, but that causes cycles (I tried narrowing it down to ClassfileLoaders, but still).

lrytz commented 6 months ago

So maybe we need to come from the other end, check where we run into an issue with the raw type and see if we can add cookJavaRawInfo / fullyInitializeSymbol somewhere along the way.

lrytz commented 6 months ago

🤷