scala / scala3

The Scala 3 compiler, also known as Dotty.
https://dotty.epfl.ch
Apache License 2.0
5.89k stars 1.06k forks source link

Java Generic Lambda conversion causes runtime crash #19725

Open lenguyenthanh opened 9 months ago

lenguyenthanh commented 9 months ago

Compiler version

3.3.1

Minimized code

Full context here in lila-ws repository: https://github.com/lichess-org/lila-ws/pull/525/commits/7f47bc39a8b36a0f076b24dd75da17150502040e

This code below crash the runtime:

  private def shutdown(channel: Channel, code: Int, reason: String): Unit =
    channel.writeAndFlush(CloseWebSocketFrame(code, reason)).addListener(_ => channel.close())

The fix is annotating the lambda with type:

  private def shutdown(channel: Channel, code: Int, reason: String): Unit =
    val close: GenericFutureListener[ChannelFuture] = _ => channel.close()
    channel.writeAndFlush(CloseWebSocketFrame(code, reason)).addListener(close)

Stacktrace:

WARN i.n.c.DefaultChannelPipeline [KQueueEventLoopGroup-6-3] An exceptionCaught() event was fired, and it reached at the tail of the pipeline. It usually means the last handler in the pipeline did not handle the exc eption. java.lang.BootstrapMethodError: bootstrap method initialization exception at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:188) at java.base/java.lang.invoke.CallSite.makeSite(CallSite.java:316) at java.base/java.lang.invoke.MethodHandleNatives.linkCallSiteImpl(MethodHandleNatives.java:274) at java.base/java.lang.invoke.MethodHandleNatives.linkCallSite(MethodHandleNatives.java:264) at lila.ws.netty.FrameHandler.shutdown(FrameHandler.scala:62) at lila.ws.netty.FrameHandler.channelRead0(FrameHandler.scala:27) at lila.ws.netty.FrameHandler.channelRead0(FrameHandler.scala:17) at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:99) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:93) at io.netty.handler.codec.http.websocketx.Utf8FrameValidator.channelRead(Utf8FrameValidator.java:89) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) at io.netty.channel.SimpleChannelInboundHandler.channelRead(SimpleChannelInboundHandler.java:102) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346) at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) at io.netty.channel.kqueue.AbstractKQueueStreamChannel$KQueueStreamUnsafe.readReady(AbstractKQueueStreamChannel.java:544) at io.netty.channel.kqueue.AbstractKQueueChannel$AbstractKQueueUnsafe.readReady(AbstractKQueueChannel.java:387) at io.netty.channel.kqueue.KQueueEventLoop.processReady(KQueueEventLoop.java:218) at io.netty.channel.kqueue.KQueueEventLoop.run(KQueueEventLoop.java:296) at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) at java.base/java.lang.Thread.run(Thread.java:1583) Caused by: java.lang.invoke.LambdaConversionException: Type mismatch for dynamic parameter 0: class scala.runtime.Nothing$ is not a subtype of interface io.netty.util.concurrent.Future at java.base/java.lang.invoke.AbstractValidatingLambdaMetafactory.checkDescriptor(AbstractValidatingLambdaMetafactory.java:327) at java.base/java.lang.invoke.AbstractValidatingLambdaMetafactory.validateMetafactoryArgs(AbstractValidatingLambdaMetafactory.java:313) at java.base/java.lang.invoke.LambdaMetafactory.altMetafactory(LambdaMetafactory.java:535) at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:147) ... 40 common frames omitted

Expectation

Either compile error or We shouldn't need to annotate the type.

mbovel commented 7 months ago

This issue was picked for the Scala Issue Spree of tomorrow, April 9th. @hamzaremmal, @Sporarum, @jan-pieter will be working on it. If you have any insight into the issue or guidance on how to fix it, please leave it here.

hamzaremmal commented 7 months ago

It looks very similar to #20114

He-Pin commented 7 months ago

java.lang.BootstrapMethodError: bootstrap method initialization exception at java.base/java.lang.invoke.BootstrapMethodInvoker.invoke(BootstrapMethodInvoker.java:188) at

Seems like the compiler insert a Nothing type, because Nothing is a sub type of Future, the listener needs a ? extends Future<?> as input, so scala compiler inser a Nothing?

For the lambda we need insert a Future<?> instead.

I still remember when using scala 2.12.x with completionStage, I have to use stage.[Unit]handle(...) for the compiler to work properly.