Skip to content
Merged
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
53 changes: 43 additions & 10 deletions compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,36 @@ object GenericSignatures {

@noinline
private final def javaSig0(sym0: Symbol, info: Type)(using Context): Option[String] = {
// This works as long as mangled names are always valid valid Java identifiers,
// if we change our name encoding, we'll have to `throw new UnknownSig` here for
// names which are not valid Java identifiers (see git history of this method).
def sanitizeName(name: Name): String = name.mangledString

val builder = new StringBuilder(64)
val isTraitSignature = sym0.enclosingClass.is(Trait)

// Collect class-level type parameter names to avoid conflicts with method-level type parameters
val usedNames = collection.mutable.Set.empty[String]
if(sym0.is(Method)) {
sym0.enclosingClass.typeParams.foreach { tp =>
usedNames += sanitizeName(tp.name)
}
}
val methodTypeParamRenaming = collection.mutable.Map.empty[String, String]
def freshTypeParamName(sanitizedName: String): String = {
if !usedNames.contains(sanitizedName) then sanitizedName
else {
var i = 1
var newName = sanitizedName + i
while usedNames.contains(newName) do
i += 1
newName = sanitizedName + i
methodTypeParamRenaming(sanitizedName) = newName
usedNames += newName
newName
}
}

def superSig(cls: Symbol, parents: List[Type]): Unit = {
def isInterfaceOrTrait(sym: Symbol) = sym.is(PureInterface) || sym.is(Trait)

Expand Down Expand Up @@ -133,15 +160,16 @@ object GenericSignatures {
else
Right(parent))

def paramSig(param: TypeParamInfo): Unit = {
builder.append(sanitizeName(param.paramName.lastPart))
def tparamSig(param: TypeParamInfo): Unit = {
val freshName = freshTypeParamName(sanitizeName(param.paramName.lastPart))
builder.append(freshName)
boundsSig(hiBounds(param.paramInfo.bounds))
}

def polyParamSig(tparams: List[TypeParamInfo]): Unit =
if (tparams.nonEmpty) {
builder.append('<')
tparams.foreach(paramSig)
tparams.foreach(tparamSig)
builder.append('>')
}

Expand All @@ -151,6 +179,12 @@ object GenericSignatures {
builder.append(';')
}

def typeParamSigWithName(sanitizedName: String): Unit = {
builder.append(ClassfileConstants.TVAR_TAG)
builder.append(sanitizedName)
builder.append(';')
}

def methodResultSig(restpe: Type): Unit = {
val finalType = restpe.finalResultType
val sym = finalType.typeSymbol
Expand All @@ -160,11 +194,6 @@ object GenericSignatures {
jsig(finalType)
}

// This works as long as mangled names are always valid valid Java identifiers,
// if we change our name encoding, we'll have to `throw new UnknownSig` here for
// names which are not valid Java identifiers (see git history of this method).
def sanitizeName(name: Name): String = name.mangledString

// Anything which could conceivably be a module (i.e. isn't known to be
// a type parameter or similar) must go through here or the signature is
// likely to end up with Foo<T>.Empty where it needs Foo<T>.Empty$.
Expand Down Expand Up @@ -244,7 +273,11 @@ object GenericSignatures {
// don't emit type param name if the param is upper-bounded by a primitive type (including via a value class)
if erasedUnderlying.isPrimitiveValueType then
jsig(erasedUnderlying, toplevel = toplevel, unboxedVCs = unboxedVCs)
else typeParamSig(ref.paramName.lastPart)
else {
val name = sanitizeName(ref.paramName.lastPart)
val nameToUse = methodTypeParamRenaming.getOrElse(name, name)
typeParamSigWithName(nameToUse)
}

case ref: TermRef if ref.symbol.isGetter =>
// If the type of a val is a TermRef to another val, generating the generic signature
Expand All @@ -258,7 +291,7 @@ object GenericSignatures {

case ref: SingletonType =>
// Singleton types like `x.type` need to be widened to their underlying type
// For example, `def identity[A](x: A): x.type` should have signature
// For example, `def identity[A](x: A): x.type` should have signature
// with return type `A` (not `java.lang.Object`)
jsig(ref.underlying, toplevel = toplevel, unboxedVCs = unboxedVCs)

Expand Down
1 change: 1 addition & 0 deletions compiler/test/dotc/pos-test-pickling.excludelist
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ i17186b.scala
i11982a.scala
i17255
i17735.scala
i24134

# Tree is huge and blows stack for printing Text
i7034.scala
Expand Down
20 changes: 20 additions & 0 deletions tests/pos/i24134/JavaPartialFunction.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
final class Flow[In, Out]:
def collect[T](pf: PartialFunction[Out, T]): Flow[In, T] = ???

object Flow:
def create[T](): Flow[T, T] = ???


abstract class Message:
def asTextMessage: TextMessage

abstract class TextMessage extends Message

abstract class JavaPartialFunction[A, B] extends PartialFunction[A, B]:
@throws(classOf[Exception]) // required, compiles without annotation
def apply(x: A, isCheck: Boolean): B

final def isDefinedAt(x: A): Boolean = ???
final override def apply(x: A): B = ???
final override def applyOrElse[A1 <: A, B1 >: B](x: A1, default: A1 => B1): B1 = ???

18 changes: 18 additions & 0 deletions tests/pos/i24134/JavaTestServer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
public class JavaTestServer {
public static Flow<Message, Message> greeter() {
return Flow.<Message>create()
.collect(
new JavaPartialFunction<Message, Message>() {
@Override
public Message apply(Message msg, boolean isCheck) throws Exception {
if (isCheck) throw new RuntimeException();
else return handleTextMessage(msg.asTextMessage());
}
});
}

public static TextMessage handleTextMessage(TextMessage msg) {
return null;
}
}

Loading