bug icon indicating copy to clipboard operation
bug copied to clipboard

AbstractMethodError when inheriting trait from java abstract class and referencing `val` from the trait

Open unkarjedy opened this issue 4 years ago • 5 comments

reproduction steps

using Scala (2.13.6),

trait ScalaTrait {
  def foo1: String
  def foo2: String = "default foo value 2"

  val val1: String
  val val2: String = "default val value 2"
}
public abstract class JavaAbstractClass implements ScalaTrait {
    public String javaFoo1() { return foo1(); }
    public String javaFoo2() { return foo2(); }

    public String javaVal1() { return val1(); }
    public String javaVal2() { return val2(); }
}
import scala.util.Try

object ScalaClass extends JavaAbstractClass {
  override def foo1: String = "implemented foo in scala 1"
  override val val1: String = "implemented val in scala 1"

  def main(args: Array[String]): Unit = {
    println(Try(javaFoo1()))
    println(Try(javaFoo2()))

    println(Try(javaVal1()))
    println(Try(javaVal2()))
  }
}

problem

The program compiles fine, but an exception is thrown at runtime:

Success(implemented foo in scala 1)
Success(default foo value 2)
Success(implemented val in scala 1)
Exception in thread "main" java.lang.AbstractMethodError: Method ScalaClass$.val2()Ljava/lang/String; is abstract
	at ScalaClass$.val2(ScalaClass.scala)
	at JavaAbstractClass.javaVal2(JavaAbstractClass.java:6)
	at ScalaClass$.$anonfun$main$4(ScalaClass.scala:12)
	at scala.util.Try$.apply(Try.scala:210)
	at ScalaClass$.main(ScalaClass.scala:12)
	at ScalaClass.main(ScalaClass.scala)

Expected result:

whether compilation failure or no runtime error

NOTE: I have to use an intermediate java class in my Scala classes hierarchy to workaround some other compiler issues.

unkarjedy avatar Sep 07 '21 18:09 unkarjedy

similar issues: https://github.com/scala/bug/issues/10534 https://github.com/scala/bug/issues/12224

unkarjedy avatar Sep 07 '21 18:09 unkarjedy

Same in Scala 3.

These are valid issued, but I'm not sure if they will be considered high priority by core contributors any time soon.

NOTE: I have to use an intermediate java class in my Scala classes hierarchy to workaround some other compiler issues.

This sounds more important - is there a ticket for that?

lrytz avatar Sep 08 '21 12:09 lrytz

is there a ticket for that

I am not sure, it's hard to create a minimal reproducible example. It is mainly related to Kotlin interop. We have some piece of library code, which is written in Kotlin and I didn't manage to use it in Scala. Sometimes scala compiler can't resolve an overloaded method from Kotlin which uses extensions as parameters (when Java can). Sometimes it tails with

java.lang.StackOverflowError
	at scala.reflect.internal.tpe.TypeMaps$SubstMap.apply(TypeMaps.scala:754)
	at scala.reflect.internal.tpe.TypeMaps$SubstSymMap.apply(TypeMaps.scala:820)
	at scala.reflect.internal.Types$TypeRef.mapOver(Types.scala:2371)
	at scala.reflect.internal.tpe.TypeMaps$SubstMap.apply(TypeMaps.scala:754)
	at scala.reflect.internal.tpe.TypeMaps$SubstSymMap.apply(TypeMaps.scala:820)
	at scala.reflect.internal.Types$TypeBounds.mapOver(Types.scala:1592)
	at scala.reflect.internal.tpe.TypeMaps$SubstMap.apply(TypeMaps.scala:754)
	at scala.reflect.internal.tpe.TypeMaps$SubstSymMap.apply(TypeMaps.scala:820)
	at scala.reflect.internal.tpe.TypeMaps$SubstSymMap.apply(TypeMaps.scala:788)
	at scala.reflect.internal.Symbols$Symbol.modifyInfo(Symbols.scala:1594)
	at scala.reflect.internal.Symbols.$anonfun$cloneSymbols$2(Symbols.scala:3770)
	at scala.reflect.internal.Symbols.cloneSymbols(Symbols.scala:3768)
	at scala.reflect.internal.Symbols.cloneSymbols$(Symbols.scala:3764)
	at scala.reflect.internal.SymbolTable.cloneSymbols(SymbolTable.scala:28)
	at scala.reflect.internal.tpe.TypeMaps$TypeMap.mapOver(TypeMaps.scala:140)
	at scala.reflect.internal.Types$ExistentialType.mapOver(Types.scala:3225)
	at scala.reflect.internal.tpe.TypeMaps$SubstMap.apply(TypeMaps.scala:754)
	at scala.reflect.internal.tpe.TypeMaps$SubstMap.apply(TypeMaps.scala:667)
	at scala.reflect.internal.Types$TypeRef.mapOver(Types.scala:2380)
	at scala.reflect.internal.tpe.TypeMaps$SubstMap.apply(TypeMaps.scala:754)
	at scala.reflect.internal.Types$Type.subst(Types.scala:747)
	at scala.reflect.internal.Types$Type.instantiateTypeParams(Types.scala:493)
	at scala.reflect.internal.Types$TypeRef.seenFromOwnerInstantiated$1(Types.scala:2499)
	at scala.reflect.internal.Types$TypeRef.relativize(Types.scala:2503)
	at scala.reflect.internal.Types$TypeRef.$anonfun$baseTypeSeqImpl$2(Types.scala:2629)
	at scala.reflect.internal.BaseTypeSeqs$BaseTypeSeq.map(BaseTypeSeqs.scala:157)
	at scala.reflect.internal.Types$TypeRef.baseTypeSeqImpl(Types.scala:2629)
	at scala.reflect.internal.Types.defineBaseTypeSeqOfTypeRef(Types.scala:2803)
	at scala.reflect.internal.Types.defineBaseTypeSeqOfTypeRef$(Types.scala:2794)
	at scala.reflect.internal.SymbolTable.defineBaseTypeSeqOfTypeRef(SymbolTable.scala:28)
	at scala.reflect.internal.Types$TypeRef.baseTypeSeq(Types.scala:2636)
	at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getClassParts$1(Implicits.scala:1348)
	at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getParts$1(Implicits.scala:1378)
	at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.$anonfun$companionImplicitMap$12(Implicits.scala:1379)
	at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getParts$1(Implicits.scala:1379)
	at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getClassParts$1(Implicits.scala:1351)
	at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getParts$1(Implicits.scala:1378)
	at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.$anonfun$companionImplicitMap$12(Implicits.scala:1379)
	at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getParts$1(Implicits.scala:1379)
	at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getClassParts$1(Implicits.scala:1351)
	at scala.tools.nsc.typechecker.Implicits$ImplicitSearch.getParts$1(Implicits.scala:1378)

I don't think that it's worth reporting, taking into account that many Java-interop issues are not considered high priority.

unkarjedy avatar Sep 08 '21 13:09 unkarjedy

looks like the exception is from https://github.com/scala/bug/issues/11775

unkarjedy avatar Sep 08 '21 13:09 unkarjedy

   println(Try(val2))

also AbstractMethodError when it extends Java class.

I'm interested in this question. Recently, I happened to read relevant issue. Let me explain my understanding. There may be some mistakes. I'm still a beginner.

Extends Java class

Combined with Scala code, the following may be a reasonable step.

  1. Implement val2 method
  2. Implementation sandbox$ScalaTrait$_setter_$val2_= method
  3. emit val2 field
  4. return val2 field by val2 method
  5. The static constructor of trait should be called, and constructor call sandbox$ScalaTrait$_setter_$val2_= to set val2 .
  trait ScalaTrait {
    val val2: String = "default val value 2"
  }

  object Test extends JavaAbstractClass {

    def main(args: Array[String]): Unit = {
      println(Try(javaVal2()))
    }
  }
 abstract trait ScalaTrait extends Object {
    <accessor> <sub_synth> protected[this] def sandbox$ScalaTrait$_setter_$val2_=(x$1: String): Unit;
    <stable> <accessor> <sub_synth> def val2(): String;
    def /*ScalaTrait*/$init$(): Unit = {
      ScalaTrait.this.sandbox$ScalaTrait$_setter_$val2_=(("default val value 2": String));
      ()
    }
  };
  object Test extends sandbox.JavaAbstractClass {
    def main(args: Array[String]): Unit = scala.Predef.println(scala.util.Try.apply({
      (() => Test.this.$anonfun$main$1())
    }));
    final <artifact> private[this] def $anonfun$main$1(): String = Test.javaVal2();
    def <init>(): sandbox.Test.type = {
      Test.super.<init>();
      ()
    }
  }

Not extends Java class

It's Ok

  trait ScalaTrait {
    val val2: String = "default val value 2"
  }

  object Test extends ScalaTrait {

    def main(args: Array[String]): Unit = {
      println(Try(val2))
    }
  }
  abstract trait ScalaTrait extends Object {
    <accessor> <sub_synth> protected[this] def sandbox$ScalaTrait$_setter_$val2_=(x$1: String): Unit;
    <stable> <accessor> <sub_synth> def val2(): String;
    def /*ScalaTrait*/$init$(): Unit = {
      ScalaTrait.this.sandbox$ScalaTrait$_setter_$val2_=(("default val value 2": String));
      ()
    }
  };
  object Test extends Object with sandbox.ScalaTrait {
    override <stable> <accessor> def val2(): String = (Test.this.val2: String);
    private[this] var val2: String = _;
    override <accessor> protected[this] def sandbox$ScalaTrait$_setter_$val2_=(x$1: String): Unit = Test.this.val2 = (x$1: String);
    def main(args: Array[String]): Unit = scala.Predef.println(scala.util.Try.apply({
      (() => Test.this.$anonfun$main$1())
    }));
    final <artifact> private[this] def $anonfun$main$1(): String = Test.val2();
    def <init>(): sandbox.Test.type = {
      Test.super.<init>();
      Test.super./*ScalaTrait*/$init$();
      ()
    }
  }

jxnu-liguobin avatar Sep 29 '21 10:09 jxnu-liguobin