Skip to content
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

scalac 3 can't compile code that worked with scalac 2 and Java, when extending Java class with lots of covariant overrides #18764

Closed
lukaseder opened this issue Oct 26, 2023 · 2 comments · Fixed by #18953

Comments

@lukaseder
Copy link

Compiler version

3.3.1

Minimized code

The bug arises when using jOOQ generated code that compiles against the jOOQ runtime library. Download https://repo1.maven.org/maven2/org/jooq/jooq/3.18.7/jooq-3.18.7.jar, and then compile the following minimal reproducer:

import org.jooq.impl.TableRecordImpl

class TRecord extends TableRecordImpl[TRecord](null) {}

Compile with:

scalac -classpath jooq-3.18.7.jar Test.scala -explain

Output

-- [E164] Declaration Error: Test.scala:3:6 ------------------------------------
3 |class TRecord extends TableRecordImpl[TRecord](null) {}
  |      ^
  |error overriding method with in trait QualifiedRecord of type [T](x$0: org.jooq.Field[T], x$1: T): TRecord;
  |  method with² in class AbstractRecord of type [T](x$0: org.jooq.Field[T²], x$1: T²): org.jooq.Record has incompatible type;
  |other members with override errors are:: method original
  |
  |where:    T     is a type variable with constraint <: Object
  |          T²    is a type variable with constraint <: Object
  |          with  is a method in trait QualifiedRecord
  |          with² is a method in class AbstractRecord
  |-----------------------------------------------------------------------------
  | Explanation (enabled by `-explain`)
  |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  | I tried to show that
  |   [T](x$0: org.jooq.Field[T], x$1: T): org.jooq.Record
  | conforms to
  |   [T](x$0: org.jooq.Field[T], x$1: T): TRecord
  | but the comparison trace ended with `false`:
  |
  |   ==> [T](x$0: org.jooq.Field[T], x$1: T): org.jooq.Record  <:  [T](x$0: org.jooq.Field[T], x$1: T): TRecord
  |     ==> type bounds []  <:  type bounds [] in frozen constraint
  |     <== type bounds []  <:  type bounds [] in frozen constraint = true
  |     ==> (x$0: org.jooq.Field[T], x$1: T): org.jooq.Record  <:  (x$0: org.jooq.Field[T], x$1: T): TRecord
  |       ==> org.jooq.Record  <:  TRecord
  |       <== org.jooq.Record  <:  TRecord = false
  |     <== (x$0: org.jooq.Field[T], x$1: T): org.jooq.Record  <:  (x$0: org.jooq.Field[T], x$1: T): TRecord = false
  |   <== [T](x$0: org.jooq.Field[T], x$1: T): org.jooq.Record  <:  [T](x$0: org.jooq.Field[T], x$1: T): TRecord = false
  |
  | The tests were made under the empty constraint
   -----------------------------------------------------------------------------
1 error found

Expectation

The code should compile just fine as it did with 2.13.12, for example, or in Scala. I think the lookup algorithm trips somewhere as it seems to find only the override [T](x$0: org.jooq.Field[T], x$1: T): org.jooq.Record, which is overridden covariantly to produce compatible versions.

@lukaseder lukaseder added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Oct 26, 2023
@bishabosha bishabosha added area:typer area:f-bounds and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Oct 26, 2023
@lukaseder
Copy link
Author

The interesting thing is that it almost seems as if covariance was confused with contravariance (e.g. because two type variables were switched)?

  | I tried to show that
  |   [T](x$0: org.jooq.Field[T], x$1: T): org.jooq.Record
  | conforms to
  |   [T](x$0: org.jooq.Field[T], x$1: T): TRecord

I don't know what conforms to really means here, but I'd say, things should be the other way round.

Meanwhile, I think I've found a workaround that I could apply to the jOOQ library to work around this problem (see also jOOQ/jOOQ#15760 (comment)):

  • Remove final modifier from AbstractQualifiedRecord::with and TableRecordImpl::original methods
  • Override the methods in the generated classes as follows
import org.jooq._
import org.jooq.impl._

class TRecord extends TableRecordImpl[TRecord](null) {
  override def `with`[T](field: org.jooq.Field[T], value: T): TRecord = super.`with`(field, value)
  override def `with`[T, U](field: org.jooq.Field[T], value: U, converter: Converter[_ <: T, _ >: U]): TRecord = super.`with`(field, value, converter)
  override def original(): TRecord = super.original()
}

Now, the code compiles.

@odersky
Copy link
Contributor

odersky commented Oct 28, 2023

This can probably be fixed by tweaking canBeHandledByParent in OverridingPairs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants