From cf4a8d24ccc5f11a629e40c497145c165a5b8b2d Mon Sep 17 00:00:00 2001 From: Rikito Taniguchi Date: Tue, 2 Dec 2025 16:51:49 +0900 Subject: [PATCH 1/2] Fix generic signatures to collect type params from all enclosing classes Previously, `javaSig0` only collected type parameter names from the immediate enclosing class when generating Java generic signatures for methods. However, this isn't enough with nested classes where outer class type parameters could clash with method-level type parameters. Now we iterate all enclosing classes to collect their type parameter names, to avoid name conflicts. Fixes #24619 --- .../tools/dotc/transform/GenericSignatures.scala | 10 +++++++--- compiler/test/dotc/pos-test-pickling.excludelist | 1 + tests/pos/i24134-nested/JavaPartialFunction.scala | 2 ++ tests/pos/i24134-nested/Main.java | 15 +++++++++++++++ 4 files changed, 25 insertions(+), 3 deletions(-) create mode 100644 tests/pos/i24134-nested/JavaPartialFunction.scala create mode 100644 tests/pos/i24134-nested/Main.java diff --git a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala index cf5386ea65e2..da856ef25dec 100644 --- a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala +++ b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala @@ -49,11 +49,15 @@ object GenericSignatures { 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 + // Collect class-level type parameter names from all enclosing classes 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) + var enclosing = sym0.enclosingClass + while (enclosing.exists) { + enclosing.typeParams.foreach { tp => + usedNames += sanitizeName(tp.name) + } + enclosing = enclosing.owner.enclosingClass } } val methodTypeParamRenaming = collection.mutable.Map.empty[String, String] diff --git a/compiler/test/dotc/pos-test-pickling.excludelist b/compiler/test/dotc/pos-test-pickling.excludelist index c9a5b86eb0fe..31febe48e6c0 100644 --- a/compiler/test/dotc/pos-test-pickling.excludelist +++ b/compiler/test/dotc/pos-test-pickling.excludelist @@ -34,6 +34,7 @@ i11982a.scala i17255 i17735.scala i24134 +i24134-nested # Tree is huge and blows stack for printing Text i7034.scala diff --git a/tests/pos/i24134-nested/JavaPartialFunction.scala b/tests/pos/i24134-nested/JavaPartialFunction.scala new file mode 100644 index 000000000000..069b534a41cf --- /dev/null +++ b/tests/pos/i24134-nested/JavaPartialFunction.scala @@ -0,0 +1,2 @@ +class Container[A]: + abstract class JavaPartialFunction[B] extends PartialFunction[A, B] diff --git a/tests/pos/i24134-nested/Main.java b/tests/pos/i24134-nested/Main.java new file mode 100644 index 000000000000..8e03c2de5045 --- /dev/null +++ b/tests/pos/i24134-nested/Main.java @@ -0,0 +1,15 @@ +public class Main { + public static void main(String[] args) { + Container container = new Container<>(); + Container.JavaPartialFunction pf = container.new JavaPartialFunction() { + @Override + public boolean isDefinedAt(String x) { + return x != null && !x.isEmpty(); + } + @Override + public Integer apply(String x) { + return x.length(); + } + }; + } +} From 4afe938d09d31a47c84bbf353a12992b460399d2 Mon Sep 17 00:00:00 2001 From: Rikito Taniguchi Date: Tue, 2 Dec 2025 17:03:06 +0900 Subject: [PATCH 2/2] Refactor javaSig0 to collect type param name in Map[String, Name] --- .../dotc/transform/GenericSignatures.scala | 32 ++++++++----------- 1 file changed, 14 insertions(+), 18 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala index da856ef25dec..64ac05a6882f 100644 --- a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala +++ b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala @@ -50,28 +50,30 @@ object GenericSignatures { val isTraitSignature = sym0.enclosingClass.is(Trait) // Collect class-level type parameter names from all enclosing classes to avoid conflicts with method-level type parameters - val usedNames = collection.mutable.Set.empty[String] + val usedNames = collection.mutable.Map.empty[String, Name] if(sym0.is(Method)) { var enclosing = sym0.enclosingClass while (enclosing.exists) { enclosing.typeParams.foreach { tp => - usedNames += sanitizeName(tp.name) + usedNames(sanitizeName(tp.name)) = tp.name } enclosing = enclosing.owner.enclosingClass } } - val methodTypeParamRenaming = collection.mutable.Map.empty[String, String] - def freshTypeParamName(sanitizedName: String): String = { - if !usedNames.contains(sanitizedName) then sanitizedName + val methodTypeParamRenaming = collection.mutable.Map.empty[Name, Name] + def freshTypeParamName(originalName: Name): Name = { + val sanitizedName = sanitizeName(originalName) + if !usedNames.contains(sanitizedName) then originalName else { var i = 1 var newName = sanitizedName + i while usedNames.contains(newName) do i += 1 newName = sanitizedName + i - methodTypeParamRenaming(sanitizedName) = newName - usedNames += newName - newName + val freshName = newName.toTypeName + methodTypeParamRenaming(originalName) = freshName + usedNames(newName) = freshName + freshName } } @@ -165,8 +167,8 @@ object GenericSignatures { Right(parent)) def tparamSig(param: TypeParamInfo): Unit = { - val freshName = freshTypeParamName(sanitizeName(param.paramName.lastPart)) - builder.append(freshName) + val freshName = freshTypeParamName(param.paramName.lastPart) + builder.append(sanitizeName(freshName)) boundsSig(hiBounds(param.paramInfo.bounds)) } @@ -183,12 +185,6 @@ 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 @@ -278,9 +274,9 @@ object GenericSignatures { if erasedUnderlying.isPrimitiveValueType then jsig(erasedUnderlying, toplevel = toplevel, unboxedVCs = unboxedVCs) else { - val name = sanitizeName(ref.paramName.lastPart) + val name = ref.paramName.lastPart val nameToUse = methodTypeParamRenaming.getOrElse(name, name) - typeParamSigWithName(nameToUse) + typeParamSig(nameToUse) } case ref: TermRef if ref.symbol.isGetter =>