Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import core.Types.*
import core.classfile.ClassfileConstants

import config.Printers.transforms
import dotty.tools.dotc.core.NameOps.isContextFunction
import reporting.trace
import java.lang.StringBuilder

Expand Down Expand Up @@ -529,7 +530,14 @@ object GenericSignatures {
@tailrec def recur(tpe: Type): Type = tpe match
case mtd: MethodType =>
vparams ++= mtd.paramInfos.filterNot(_.hasAnnotation(defn.ErasedParamAnnot))
recur(mtd.resType)
mtd.resType match
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
mtd.resType match
mtd.resType.dealias match

otherwise, it doesn't catch the following case?

object Test:
   trait Ctx
   type Handler = Ctx ?=> String
   def foo(x: Int): Handler = "hello"

// Returned context functions are erased by putting their parameters into the method's parameters,
// so we must duplicate that logic here
case AppliedType(tycon, args) if tycon.typeSymbol.name.isContextFunction =>
vparams ++= args.take(args.length - 1)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to filter out .filterNot(_.hasAnnotation(defn.ErasedParamAnnot)) similar to for normal parameters, otherwise, erased type parameter will remain in generic signature, while it is removed in descriptor ?

import language.experimental.erasedDefinitions

class CanSerialize extends compiletime.Erased

object Test:
  trait Ctx
  def foo(x: Int): CanSerialize ?=> Ctx ?=> String = "foo"

(I didn't know about this feature until I see the vparams ++= mtd.paramInfos.filterNot(_.hasAnnotation(defn.ErasedParamAnnot)) line above :p
https://docs.scala-lang.org/scala3/reference/experimental/erased-defs.html

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it seems that for method that contains Erased parameter, the result type will be RefinedType instead of AppliedType.

We'd like to pattern match against

case defn.FunctionTypeOfMethod(mt: MethodType) if mt.isContextualMethod

?

recur(args.last)
case _ =>
recur(mtd.resType)
case PolyType(tps, tpe) =>
tparams ++= tps
recur(tpe)
Expand Down
6 changes: 6 additions & 0 deletions tests/run/returned-context-function-signature.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
public void CtxFns$.puts(java.lang.Object,CtxFns$Context)
class java.lang.Object,class CtxFns$Context -> void
T,class CtxFns$Context -> void
private java.lang.Object CtxFns$.writeReplace()
-> class java.lang.Object
-> class java.lang.Object
17 changes: 17 additions & 0 deletions tests/run/returned-context-function-signature.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// scalajs: --skip
// (this is a JVM-only test)

object CtxFns {
abstract class Context:
def puts[T](t: T): Unit

def puts[T](t: T): Context ?=> Unit = summon[Context].puts(t)
}

object Test:
def main(args: Array[String]): Unit =
classOf[CtxFns.type].getDeclaredMethods.sortBy(_.getName).foreach(m =>
println(m)
println(m.getParameterTypes().mkString(",") + " -> " + m.getReturnType.toString)
println(m.getGenericParameterTypes().mkString(",") + " -> " + m.getGenericReturnType.toString)
)
Loading