bug
bug copied to clipboard
Invalid Java signature emitted for intersection types involving type variables
According to JLS 4.4, the type bounds of a type variable must either be a single type variable, or an intersection of a class or interface followed by zero or more interfaces - that is to say, you cannot use type variables in an intersection of a type bounds.
So, the following code will not compile in Java:
interface Foo<T> {
<S extends T & Serializable> void bar();
}
Because T, a type variable, if it is to appear in the type bounds of S, must be there by itself, it may not be intersected with anything else.
However, scalac will emit the above signature if you implement this in Scala:
trait Foo[T] {
def bar[S <: T with Serializable]: Unit
}
scalac shouldn't be emitting invalid signatures, so I believe this is a bug.
As it happens, javac is quite happy to accept and understands the type signature when it encounters it in bytecode, type checking its usage as intended by the Scala code. However, other compilers are not so permissable, for example, IntelliJ does not understand the type signature, and emits errors and warnings for code that depends on non spec conformant inference of the types accepted/returned by the method.
A real consequence of this is this bug in Lagom.
In my opinion, scalacs behaviour in this case should be to, if the first type in the type bounds is a type variable, drop the rest of the type bounds in the Java signature, or, if a type variable is found in the tail of the type bounds intersection, drop it. This approach would be similar to the JLS's approach to how to decide on the type to emit in the binary signature for intersection types.
You are referring to the Java language spec. However, the Java VM spec, 4.3.4 allows the signature we are emitting:
ClassSignature:
FormalTypeParameters(opt) ...
FormalTypeParameters:
< FormalTypeParameter+ >
FormalTypeParameter:
Identifier ClassBound InterfaceBound*
ClassBound:
: FieldTypeSignature(opt)
InterfaceBound:
: FieldTypeSignature
FieldTypeSignature:
ClassTypeSignature
ArrayTypeSignature
TypeVariableSignature
TypeVariableSignature:
T Identifier ;
The signature we emit for method bar from your example is <S:TT;:Lscala/Serializable;>()V matches that grammar.
That said, it might still be a good idea to adapt our signatures to what javac is emitting - interop is their raison d'être anyway. Opinions?