Skip to content

When overrided function return type subtypes parent type in Java, generated Java bytecode is wrong #7955

Closed
@molikto

Description

@molikto

When trying to make this Scala.js test works in Dotty https://github.com/scala-js/scala-js/blob/master/test-suite/shared/src/test/scala/org/scalajs/testsuite/javalib/io/ReadersTest.scala#L63

Scala.js linker fails with

      [error] Referring to non-existent method java.nio.CharBuffer.slice()java.nio.Buffer

I found that the IR generated is different

in Dotty:

    val buf: java.nio.CharBuffer = buf0.slice;Ljava.nio.Buffer().asInstanceOf[java.nio.CharBuffer];

in Scala 2.13:

    val buf: java.nio.CharBuffer = buf0.slice;Ljava.nio.CharBuffer();

minimized code

This is minimized code:

import java.nio.CharBuffer

class test {

val a: CharBuffer = null

val b = a.slice()	
}

The class file seems have wrong type in it's constant pool, and will have a checkcast instruction; see bytecode bellow:

Compilation output
Classfile /Users/molikto/GitHub/dotty/test.class
  Last modified Jan 10, 2020; size 600 bytes
  MD5 checksum 25414eae76502c40f16327ad77826708
  Compiled from "test.scala"
public class test
  minor version: 0
  major version: 52
  flags: (0x0021) ACC_PUBLIC, ACC_SUPER
  this_class: #2                          // test
  super_class: #4                         // java/lang/Object
  interfaces: 0, fields: 2, methods: 3, attributes: 3
Constant pool:
   #1 = Utf8               test
   #2 = Class              #1             // test
   #3 = Utf8               java/lang/Object
   #4 = Class              #3             // java/lang/Object
   #5 = Utf8               test.scala
   #6 = Utf8               a
   #7 = Utf8               Ljava/nio/CharBuffer;
   #8 = Utf8               b
   #9 = Utf8               <init>
  #10 = Utf8               ()V
  #11 = NameAndType        #9:#10         // "<init>":()V
  #12 = Methodref          #4.#11         // java/lang/Object."<init>":()V
  #13 = NameAndType        #6:#7          // a:Ljava/nio/CharBuffer;
  #14 = Fieldref           #2.#13         // test.a:Ljava/nio/CharBuffer;
  #15 = Utf8               ()Ljava/nio/CharBuffer;
  #16 = NameAndType        #6:#15         // a:()Ljava/nio/CharBuffer;
  #17 = Methodref          #2.#16         // test.a:()Ljava/nio/CharBuffer;
  #18 = Utf8               java/nio/CharBuffer
  #19 = Class              #18            // java/nio/CharBuffer
  #20 = Utf8               slice
  #21 = Utf8               ()Ljava/nio/Buffer;
  #22 = NameAndType        #20:#21        // slice:()Ljava/nio/Buffer;
  #23 = Methodref          #19.#22        // java/nio/CharBuffer.slice:()Ljava/nio/Buffer;
  #24 = NameAndType        #8:#7          // b:Ljava/nio/CharBuffer;
  #25 = Fieldref           #2.#24         // test.b:Ljava/nio/CharBuffer;
  #26 = Utf8               this
  #27 = Utf8               Ltest;
  #28 = Utf8               Code
  #29 = Utf8               LineNumberTable
  #30 = Utf8               LocalVariableTable
  #31 = Utf8               SourceFile
  #32 = Utf8               TASTY
  #33 = Utf8               Scala
{
  public test();
    descriptor: ()V
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #12                 // Method java/lang/Object."<init>":()V
         4: aload_0
         5: aconst_null
         6: putfield      #14                 // Field a:Ljava/nio/CharBuffer;
         9: aload_0
        10: aload_0
        11: invokevirtual #17                 // Method a:()Ljava/nio/CharBuffer;
        14: invokevirtual #23                 // Method java/nio/CharBuffer.slice:()Ljava/nio/Buffer;
        17: checkcast     #19                 // class java/nio/CharBuffer
        20: putfield      #25                 // Field b:Ljava/nio/CharBuffer;
        23: return
      LineNumberTable:
        line 3: 0
        line 5: 4
        line 7: 9
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      24     0  this   Ltest;

  public java.nio.CharBuffer a();
    descriptor: ()Ljava/nio/CharBuffer;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #14                 // Field a:Ljava/nio/CharBuffer;
         4: areturn
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Ltest;

  public java.nio.CharBuffer b();
    descriptor: ()Ljava/nio/CharBuffer;
    flags: (0x0001) ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: getfield      #25                 // Field b:Ljava/nio/CharBuffer;
         4: areturn
      LineNumberTable:
        line 7: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Ltest;
}
SourceFile: "test.scala"
Error: unknown attribute
  TASTY: length = 0x10
   00 B5 CF 56 F8 F6 DC 00 00 05 3A 7B D6 3C 8F 00

Error: unknown attribute
  Scala: length = 0x0

This is Tasty file (seems no cast is present here):

Compilation output
     0: PACKAGE(78)
     2:   TERMREFpkg 1 [<empty>]
     4:   IMPORT(6)
     6:     SELECT 2 [nio]
     8:       TERMREFpkg 3 [java]
    10:     IMPORTED 4 [CharBuffer]
    12:   TYPEDEF(66) 5 [test]
    15:     TEMPLATE(48)
    17:       APPLY(7)
    19:         SELECT 11 [<init>[Signed Signature(List(),java.lang.Object)]]
    21:           NEW
    22:             TYPEREF 9 [Object]
    24:               TERMREFpkg 8 [java[Qualified . lang]]
    26:       DEFDEF(6) 6 [<init>]
    29:         PARAMEND
    30:         TYPEREF 12 [Unit]
    32:           TERMREFpkg 13 [scala]
    34:       VALDEF(8) 14 [a]
    37:         IDENTtpt 4 [CharBuffer]
    39:           TYPEREF 4 [CharBuffer]
    41:             TERMREFpkg 15 [java[Qualified . nio]]
    43:         NULLconst
    44:       VALDEF(19) 16 [b]
    47:         TYPEREF 4 [CharBuffer]
    49:           TERMREFpkg 15 [java[Qualified . nio]]
    51:         APPLY(12)
    53:           SELECT 19 [slice[Signed Signature(List(),java.nio.CharBuffer)]]
    55:             SELECT 14 [a]
    57:               QUALTHIS
    58:                 IDENTtpt 5 [test]
    60:                   TYPEREFsymbol 12
    62:                     THIS
    63:                       TYPEREFpkg 1 [<empty>]
    65:     ANNOTATION(13)
    67:       TYPEREF 20 [SourceFile]
    69:         TERMREFpkg 24 [scala[Qualified . annotation][Qualified . internal]]
    71:       APPLY(7)
    73:         SELECT 28 [<init>[Signed Signature(List(java.lang.String),scala.annotation.internal.SourceFile)]]
    75:           NEW
    76:             SHAREDtype 67
    78:         STRINGconst 29 [test.scala]
    80:

It doesn't seems to happen with similar Scala code:


abstract class Buffer {
	def slice(): Buffer
}

abstract class CharBuffer extends Buffer {
	override def slice(): CharBuffer
}

class test {

val a: CharBuffer = null

val b = a.slice()	
}

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions