Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't type parameter in Kotlin have any other bounds if it's bounded by another type parameter?

Tags:

types

kotlin

Here is the minimal demo code that shows this problem:

interface A  fun <T1, T2> test() where T2 : T1, T2 : A {} 

When I try to compile it, compiler will complain:

Error:(81, 25) Kotlin: Type parameter cannot have any other bounds if it's bounded by another type parameter

I read Kotlin Language Specification, but only find the following bound restriction:

A type-parameter cannot specify itself as its own bound, and several type-parameters cannot specify each other as a bound in a cyclic manner.

It doesn't explain the restriction I meet.

I explore Kotlin's issue tracker, and I find an issue about this restriction: Allow to inherit a type parameter from another type parameter and a class : KT-13768. However, this issue has been rejected by the following reason (update on May 6th, 2017: this issue has been reopened by Stanislav Erokhin):

I don't think we can compile the code correctly to JVM if we remove this restriction.

By Andrey Breslav

So the question is: why can't we compile the code correctly to JVM if we remove this restriction?

The same demo works in Scala:

trait A  def test[T1, T2 <: T1 with A](): Unit = {} 

It indicates that Scala can compile the code correctly to JVM. Why can't Kotlin? Is it a restriction to guarantee a decidable subtyping in Kotlin (I guess. Subtyping is undecidable for Scala (Scala has a Turing-complete type system). Kotlin may want decidable subtyping like C#.)?

Update after answered by @erokhins (https://stackoverflow.com/a/43807444/7964561):

There are some subtle issues when supporting something forbidden by Java but allowed by JVM, especially in Java interoperability. I find an interesting issue when digging into bytecode generated by scalac. I modify Scala code in demo as follow:

trait A  trait B  def test[T1 <: B, T2 <: T1 with A](t1: T1, t2: T2): Unit = {}  class AB extends A with B 

Scalac will generate the following signature:

// signature <T1::LB;T2:TT1;:LA;>(TT1;TT2;)V // descriptor: (LB;LB;)V public <T1 extends B, T2 extends T1 & A> void test(T1, T2); 

Invoke test with test(new AB, new AB) in Scala will succeed, since Scalas invoke signature (LB;LB;)V; but invoke with test(new AB(), new AB()); in Java will fail, since Java invokes signature (LB;Ljava/lang/Object;)V, causing java.lang.NoSuchMethodError in runtime. It means scalac generates something cannot be invoked in Java after relaxing this restriction. Kotlin may meet the same issue after relaxing it.

like image 955
uchuhimo Avatar asked May 04 '17 18:05

uchuhimo


People also ask

What is type parameter in Kotlin?

The type parameter lets you specify exactly that—instead of “This variable holds a list,” you can say something like “This variable holds a list of strings.” Kotlin's syntax for saying “a list of strings” looks the same as in Java: List<String> . You can also declare multiple type parameters for a class.

What is type erasure Kotlin?

Type Erasure. As with Java, Kotlin's generics are erased at runtime. That is, an instance of a generic class doesn't preserve its type parameters at runtime. For example, if we create a Set<String> and put a few strings into it, at runtime we're only able to see it as a Set.


1 Answers

This restrictions was made because java(language) has it:

  interface A {}   // Error:(7, 26) java: a type variable may not be followed by other bounds   <T1, T2 extends T1 & A> void test() {}  

And we suppose that this forbidden also in bytecode level. I dig into it and seems like it is allowed, and scalac generate the following signature:

  // access flags 0x1   // signature <T1:Ljava/lang/Object;T2:TT1;:LA;>()V   // declaration: void test<T1, T2T1 extends A>()   public test()V 

So, we probably can support such cases it in the future versions of kotlin.

P.S. As far as I know Kotlin has decidable subtyping, and decidability isn't affected by this.

like image 158
erokhins Avatar answered Oct 12 '22 17:10

erokhins