Skip to content

Ycheck failure in covariant java varargs #5140

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
abeln opened this issue Sep 21, 2018 · 5 comments
Closed

Ycheck failure in covariant java varargs #5140

abeln opened this issue Sep 21, 2018 · 5 comments

Comments

@abeln
Copy link
Contributor

abeln commented Sep 21, 2018

This involves Scala code calling a Java varargs methods.

Java

  class Animal {}
  class Dog extends Animal {}
  class J {
    void foo(Animal... animal) {}
  }

Scala

class S {
  val j = new J()
  val x: Array[Dog] = ???
  j.foo(x: _*)
}

Compiling without -Ycheck succeeds, but -Ycheck:refchecks fails:

exception while typing this.j.foo(this.x) of class class dotty.tools.dotc.ast.Trees$Apply # 802
exception while typing @scala.annotation.internal.SourceFile("tests/neg/java-varargs-cov/S.scala") 
  class
 S() extends Object() { 
  val j: J = new J()
  val x: Array[Dog] = ???
  this.j.foo(this.x)
} of class class dotty.tools.dotc.ast.Trees$TypeDef # 804
exception while typing package <empty> {
  @scala.annotation.internal.SourceFile("tests/neg/java-varargs-cov/S.scala") 
    class
   S() extends Object() { 
    val j: J = new J()
    val x: Array[Dog] = ???
    this.j.foo(this.x)
  }
} of class class dotty.tools.dotc.ast.Trees$PackageDef # 805
*** error while checking tests/neg/java-varargs-cov/S.scala after phase refchecks ***
exception occurred while compiling tests/neg/java-varargs-cov/S.scala
Exception in thread "main" java.lang.AssertionError: assertion failed: found:    Array[Dog](S.this.x)
required: Array[Animal]


tree = this.x
	at scala.Predef$.assert(Predef.scala:219)
	at dotty.tools.dotc.transform.TreeChecker$Checker.adapt(TreeChecker.scala:460)
	at dotty.tools.dotc.typer.ProtoTypes$FunProto.typedArg(ProtoTypes.scala:302)
	at dotty.tools.dotc.typer.Applications$ApplyToUntyped.typedArg(Applications.scala:663)
	at dotty.tools.dotc.typer.Applications$ApplyToUntyped.typedArg(Applications.scala:661)
	at dotty.tools.dotc.typer.Applications$Application.addTyped$1(Applications.scala:434)
	at dotty.tools.dotc.typer.Applications$Application.matchArgs(Applications.scala:472)
	at dotty.tools.dotc.typer.Applications$Application.init(Applications.scala:255)
	at dotty.tools.dotc.typer.Applications$TypedApply.<init>(Applications.scala:564)
	at dotty.tools.dotc.typer.Applications$ApplyToUntyped.<init>(Applications.scala:662)
	at dotty.tools.dotc.typer.Applications.simpleApply$1(Applications.scala:722)
	at dotty.tools.dotc.typer.Applications.$anonfun$typedApply$7(Applications.scala:747)
	at dotty.tools.dotc.typer.Typer.tryEither(Typer.scala:1983)
	at dotty.tools.dotc.typer.Applications.$anonfun$typedApply$1(Applications.scala:748)
	at dotty.tools.dotc.util.Stats$.track(Stats.scala:37)
	at dotty.tools.dotc.typer.Applications.realApply$1(Applications.scala:692)
	at dotty.tools.dotc.typer.Applications.typedApply(Applications.scala:793)
	at dotty.tools.dotc.typer.Applications.typedApply$(Applications.scala:690)
	at dotty.tools.dotc.typer.Typer.typedApply(Typer.scala:82)
	at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:1817)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:1868)
	at dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:112)
	at dotty.tools.dotc.transform.TreeChecker$Checker.typedUnadapted(TreeChecker.scala:282)
	at dotty.tools.dotc.typer.Typer.$anonfun$typed$2(Typer.scala:1899)
	at dotty.tools.dotc.reporting.trace$.apply(trace.scala:40)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:1895)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:1911)
	at dotty.tools.dotc.transform.TreeChecker$Checker.typed(TreeChecker.scala:270)
	at dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:1951)
	at dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:1964)
	at dotty.tools.dotc.transform.TreeChecker$Checker.typedStats(TreeChecker.scala:446)
	at dotty.tools.dotc.typer.Typer.$anonfun$typedClassDef$1(Typer.scala:1564)
	at dotty.tools.dotc.util.Stats$.track(Stats.scala:37)
	at dotty.tools.dotc.typer.Typer.typedClassDef(Typer.scala:1487)
	at dotty.tools.dotc.transform.TreeChecker$Checker.typedClassDef(TreeChecker.scala:399)
	at dotty.tools.dotc.typer.Typer.typedNamed$1(Typer.scala:1808)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:1867)
	at dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:112)
	at dotty.tools.dotc.transform.TreeChecker$Checker.typedUnadapted(TreeChecker.scala:282)
	at dotty.tools.dotc.typer.Typer.$anonfun$typed$2(Typer.scala:1899)
	at dotty.tools.dotc.reporting.trace$.apply(trace.scala:40)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:1895)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:1911)
	at dotty.tools.dotc.transform.TreeChecker$Checker.typed(TreeChecker.scala:270)
	at dotty.tools.dotc.typer.Typer.traverse$1(Typer.scala:1930)
	at dotty.tools.dotc.typer.Typer.typedStats(Typer.scala:1964)
	at dotty.tools.dotc.transform.TreeChecker$Checker.typedStats(TreeChecker.scala:446)
	at dotty.tools.dotc.typer.Typer.$anonfun$typedPackageDef$1(Typer.scala:1674)
	at dotty.tools.dotc.util.Stats$.track(Stats.scala:37)
	at dotty.tools.dotc.typer.Typer.typedPackageDef(Typer.scala:1667)
	at dotty.tools.dotc.typer.Typer.typedUnnamed$1(Typer.scala:1847)
	at dotty.tools.dotc.typer.Typer.typedUnadapted(Typer.scala:1868)
	at dotty.tools.dotc.typer.ReTyper.typedUnadapted(ReTyper.scala:112)
	at dotty.tools.dotc.transform.TreeChecker$Checker.typedUnadapted(TreeChecker.scala:282)
	at dotty.tools.dotc.typer.Typer.$anonfun$typed$2(Typer.scala:1899)
	at dotty.tools.dotc.reporting.trace$.apply(trace.scala:40)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:1895)
	at dotty.tools.dotc.typer.Typer.typed(Typer.scala:1911)
	at dotty.tools.dotc.transform.TreeChecker$Checker.typed(TreeChecker.scala:270)
	at dotty.tools.dotc.typer.Typer.typedExpr(Typer.scala:1975)
	at dotty.tools.dotc.transform.TreeChecker.check(TreeChecker.scala:132)
	at dotty.tools.dotc.transform.TreeChecker.run(TreeChecker.scala:104)
	at dotty.tools.dotc.core.Phases$Phase.$anonfun$runOn$1(Phases.scala:298)
	at scala.collection.immutable.List.map(List.scala:283)
	at dotty.tools.dotc.core.Phases$Phase.runOn(Phases.scala:296)
	at dotty.tools.dotc.core.Phases$Phase.runOn$(Phases.scala:295)
	at dotty.tools.dotc.transform.TreeChecker.runOn(TreeChecker.scala:48)
	at dotty.tools.dotc.Run.$anonfun$compileUnits$3(Run.scala:174)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:12)
	at dotty.tools.dotc.util.Stats$.trackTime(Stats.scala:49)
	at dotty.tools.dotc.Run.$anonfun$compileUnits$2(Run.scala:171)
	at dotty.tools.dotc.Run.$anonfun$compileUnits$2$adapted(Run.scala:169)
	at scala.collection.IndexedSeqOptimized.foreach(IndexedSeqOptimized.scala:32)
	at scala.collection.IndexedSeqOptimized.foreach$(IndexedSeqOptimized.scala:29)
	at scala.collection.mutable.ArrayOps$ofRef.foreach(ArrayOps.scala:194)
	at dotty.tools.dotc.Run.runPhases$1(Run.scala:169)
	at dotty.tools.dotc.Run.$anonfun$compileUnits$1(Run.scala:194)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:12)
	at dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:90)
	at dotty.tools.dotc.Run.compileUnits(Run.scala:149)
	at dotty.tools.dotc.Run.compileSources(Run.scala:136)
	at dotty.tools.dotc.Run.compile(Run.scala:120)
	at dotty.tools.dotc.Driver.doCompile(Driver.scala:31)
	at dotty.tools.dotc.Driver.process(Driver.scala:134)
	at dotty.tools.dotc.Driver.process(Driver.scala:103)
	at dotty.tools.dotc.Driver.process(Driver.scala:115)
	at dotty.tools.dotc.Driver.main(Driver.scala:142)

@abeln
Copy link
Contributor Author

abeln commented Sep 21, 2018

The first question here is what is the intended behaviour. Specifically, is this supposed to compile? Notice it's unsound, because we're passing an Array[Dog] that Java can later populate with an Animal.

Looking at the typer, the following happens while typing j.foo(x: _*):

  1. x is typed with a prototype of Seq[Any]
  2. for that work, an implicit conversion is added: this.j.foo(wrapRefArray[Dog](this.x): Dog*)
  3. an ascription of : Dog*, which is RepeatedParamType[Dog] is also added
  4. RP[Dog] is checked against the param type, which is RP[Animal]
  5. since RP is covariant, the checks succeeds
  6. however, somehow Ycheck is able to see through the RP type into the "real" type of Array[Dog] and Array[Animal], and that only happens after refChecks (in particular, setting -Ycheck:elimRepeated succeeds)

I don't yet understand why step 6 happens. This is relevant to the nullability work we're doing, because we add |Null inside Java-declared array types, so
void foo(String... x)
becomes
void foo(String|Null... x) => void foo(x: RepeatedParam[String|Null]|Null)

so this type of covariance + arrays + java interop comes up more often in the non-null world.

@allanrenucci
Copy link
Contributor

Does scalac reject this program?

There is an attempt at changing how we typecheck wildcard star args in #4787. Ideally we should not type them as Seq if the method is java defined

@odersky
Copy link
Contributor

odersky commented Oct 7, 2018

I believe if it fails to Ycheck after refchecks it should not get the area:typer flag.

@allanrenucci
Copy link
Contributor

I believe if it fails to Ycheck after refchecks it should not get the area:typer flag.

It happens to fail Ycheck later in the pipeline but this is a problem in typer. The program should not typecheck

@abeln
Copy link
Contributor Author

abeln commented Nov 6, 2018

@allanrenucci scala 2.12.6 accepts the program. Do we know the behaviour we want? Fix the Ycheck error or disallow in the typer?

abeln added a commit to abeln/dotty that referenced this issue Nov 7, 2018
Allow passing `Array[Dog]` to a Java varargs that expects an
`Array[Animal]`. This types because Java vargs are represented as
`RepeatedParam[+T]`, which is covariant.

However, after `elimRepeated`, the `RP`s go away and the true type
(Array) is revealed, which was causing a Ycheck error because arrays are
invariant before erasure.

Fix the Ycheck error by casting `Array[Dog]` to `Array[Animal]`. This is
unsound but consistent with the typer behaviour and what scalac does.
allanrenucci added a commit that referenced this issue Nov 9, 2018
Fix #5140: Ycheck failure in covariant java varargs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants