From 22d98d6603a66589f63f699d5b0114c578b0483a Mon Sep 17 00:00:00 2001 From: i10416 Date: Sat, 24 Feb 2024 15:13:58 +0900 Subject: [PATCH 1/7] fix: cannot find Scala companion module from Java To find Scala companion mudule from Java in mixed sources, we should strip module suffix `$`. This provides workaround for #17255, but it requires some refinment to fix it because not-fully-qualified type like the following example still fails to compile due to missing symbol. ```java package example; public class Bar { private static final Foo$ MOD = Foo$.MODULE; } ``` This is because `pre` in `javaFindMember` for `Foo` in the case above is ``, not `example` and therefore `pre.findMember` looks for `.Foo` instead of `example.Foo`. I'm not sure whether the qualifier is intentionally dropped. References - https://github.com/lampepfl/dotty/pull/12884 - https://github.com/scala/scala/pull/7671 --- compiler/src/dotty/tools/dotc/core/ContextOps.scala | 12 +++++++++--- compiler/test/dotc/pos-test-pickling.blacklist | 1 + tests/pos/i17255/Bar.java | 6 ++++++ tests/pos/i17255/Foo.scala | 5 +++++ 4 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 tests/pos/i17255/Bar.java create mode 100644 tests/pos/i17255/Foo.scala diff --git a/compiler/src/dotty/tools/dotc/core/ContextOps.scala b/compiler/src/dotty/tools/dotc/core/ContextOps.scala index 55fb31fd1916..8b47fe1cd872 100644 --- a/compiler/src/dotty/tools/dotc/core/ContextOps.scala +++ b/compiler/src/dotty/tools/dotc/core/ContextOps.scala @@ -55,11 +55,17 @@ object ContextOps: final def javaFindMember(name: Name, pre: Type, lookInCompanion: Boolean, required: FlagSet = EmptyFlags, excluded: FlagSet = EmptyFlags): Denotation = assert(ctx.isJava) inContext(ctx) { - + import dotty.tools.dotc.core.NameOps.* val preSym = pre.typeSymbol - // 1. Try to search in current type and parents. - val directSearch = pre.findMember(name, pre, required, excluded) + val directSearch = + if name.isTypeName && name.endsWith(StdNames.str.MODULE_SUFFIX) then + pre.findMember(name.stripModuleClassSuffix, pre, required, excluded) match + case NoDenotation => NoDenotation + case symDenot: SymDenotation => + symDenot.companionModule.denot + else + pre.findMember(name, pre, required, excluded) // 2. Try to search in companion class if current is an object. def searchCompanionClass = if lookInCompanion && preSym.is(Flags.Module) then diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index b0da78f0a1eb..32f8cdef1386 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -29,6 +29,7 @@ i16649-irrefutable.scala strict-pattern-bindings-3.0-migration.scala i17186b.scala i11982a.scala +i17255 # Tree is huge and blows stack for printing Text i7034.scala diff --git a/tests/pos/i17255/Bar.java b/tests/pos/i17255/Bar.java new file mode 100644 index 000000000000..35269af2179f --- /dev/null +++ b/tests/pos/i17255/Bar.java @@ -0,0 +1,6 @@ +package example; + + +public class Bar { + private static final example.Foo$ MOD = example.Foo$.MODULE$; +} \ No newline at end of file diff --git a/tests/pos/i17255/Foo.scala b/tests/pos/i17255/Foo.scala new file mode 100644 index 000000000000..9608b43b430f --- /dev/null +++ b/tests/pos/i17255/Foo.scala @@ -0,0 +1,5 @@ +package example + +case class Foo(i: Int) + +object Foo \ No newline at end of file From 4cb34e5b6134c3bccc25dc61ff53da80f992aaa3 Mon Sep 17 00:00:00 2001 From: i10416 Date: Sat, 24 Feb 2024 18:42:53 +0900 Subject: [PATCH 2/7] tweak: try the original name first and then fallback to module Avoid skipping searchin a member by the original name even when name is likely companion module name. --- .../src/dotty/tools/dotc/core/ContextOps.scala | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/ContextOps.scala b/compiler/src/dotty/tools/dotc/core/ContextOps.scala index 8b47fe1cd872..3f6b49253192 100644 --- a/compiler/src/dotty/tools/dotc/core/ContextOps.scala +++ b/compiler/src/dotty/tools/dotc/core/ContextOps.scala @@ -59,13 +59,16 @@ object ContextOps: val preSym = pre.typeSymbol // 1. Try to search in current type and parents. val directSearch = - if name.isTypeName && name.endsWith(StdNames.str.MODULE_SUFFIX) then - pre.findMember(name.stripModuleClassSuffix, pre, required, excluded) match - case NoDenotation => NoDenotation - case symDenot: SymDenotation => - symDenot.companionModule.denot - else - pre.findMember(name, pre, required, excluded) + def asModule = + if name.isTypeName && name.endsWith(StdNames.str.MODULE_SUFFIX) then + pre.findMember(name.stripModuleClassSuffix, pre, required, excluded) match + case NoDenotation => NoDenotation + case symDenot: SymDenotation => + symDenot.companionModule.denot + else NoDenotation + pre.findMember(name, pre, required, excluded) match + case NoDenotation => asModule + case denot => denot // 2. Try to search in companion class if current is an object. def searchCompanionClass = if lookInCompanion && preSym.is(Flags.Module) then From 4992b4bd8893e3407050260f57e64a1c7db2035a Mon Sep 17 00:00:00 2001 From: 110416 Date: Sun, 25 Feb 2024 19:32:47 +0900 Subject: [PATCH 3/7] Update compiler/src/dotty/tools/dotc/core/ContextOps.scala Co-authored-by: Jamie Thompson --- compiler/src/dotty/tools/dotc/core/ContextOps.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/ContextOps.scala b/compiler/src/dotty/tools/dotc/core/ContextOps.scala index 3f6b49253192..a27f3cef1460 100644 --- a/compiler/src/dotty/tools/dotc/core/ContextOps.scala +++ b/compiler/src/dotty/tools/dotc/core/ContextOps.scala @@ -61,7 +61,7 @@ object ContextOps: val directSearch = def asModule = if name.isTypeName && name.endsWith(StdNames.str.MODULE_SUFFIX) then - pre.findMember(name.stripModuleClassSuffix, pre, required, excluded) match + pre.findMember(name.stripModuleClassSuffix.moduleClassName, pre, required, excluded) match case NoDenotation => NoDenotation case symDenot: SymDenotation => symDenot.companionModule.denot From 1d65c5bde5bb463d0cca0d5ecfbfea4c4d7ba15b Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 25 Feb 2024 21:16:00 +0900 Subject: [PATCH 4/7] fix: imported symbol missing for companion module --- compiler/src/dotty/tools/dotc/core/ContextOps.scala | 5 ++++- tests/pos/i17255/Baz.java | 7 +++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 tests/pos/i17255/Baz.java diff --git a/compiler/src/dotty/tools/dotc/core/ContextOps.scala b/compiler/src/dotty/tools/dotc/core/ContextOps.scala index a27f3cef1460..427cfa47c7c1 100644 --- a/compiler/src/dotty/tools/dotc/core/ContextOps.scala +++ b/compiler/src/dotty/tools/dotc/core/ContextOps.scala @@ -41,7 +41,10 @@ object ContextOps: else pre.findMember(name, pre, required, excluded) } else // we are in the outermost context belonging to a class; self is invisible here. See inClassContext. - ctx.owner.findMember(name, ctx.owner.thisType, required, excluded) + if ctx.isJava then + javaFindMember(name, ctx.owner.thisType, lookInCompanion = true,required, excluded) + else + ctx.owner.findMember(name, ctx.owner.thisType, required, excluded) else ctx.scope.denotsNamed(name).filterWithFlags(required, excluded).toDenot(NoPrefix) } diff --git a/tests/pos/i17255/Baz.java b/tests/pos/i17255/Baz.java new file mode 100644 index 000000000000..6dbb1078949d --- /dev/null +++ b/tests/pos/i17255/Baz.java @@ -0,0 +1,7 @@ +package example; + +import example.Foo$; + +public class Baz { + private static final Foo$ MOD = Foo$.MODULE$; +} \ No newline at end of file From d84b3ca9c80bbc9e9df9f017836c5881967e013c Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 25 Feb 2024 21:21:19 +0900 Subject: [PATCH 5/7] test: add a test to confirm scala/scala#10644 is forward-ported --- compiler/test/dotc/run-test-pickling.blacklist | 2 ++ tests/run/i17255/J.java | 13 +++++++++++++ tests/run/i17255/Module.scala | 10 ++++++++++ 3 files changed, 25 insertions(+) create mode 100644 tests/run/i17255/J.java create mode 100644 tests/run/i17255/Module.scala diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index 9f19b439135c..954a64db1b66 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -44,3 +44,5 @@ t6138 t6138-2 i12656.scala trait-static-forwarder +i17255 + diff --git a/tests/run/i17255/J.java b/tests/run/i17255/J.java new file mode 100644 index 000000000000..3dfb775423f3 --- /dev/null +++ b/tests/run/i17255/J.java @@ -0,0 +1,13 @@ +package p; + +public class J { + public static J j = new J(); + public static p.J f() { + return p.J.j; + } + public static p.Module$ module() { + return p.Module$.MODULE$; + } + + public String toString() { return "J"; } +} \ No newline at end of file diff --git a/tests/run/i17255/Module.scala b/tests/run/i17255/Module.scala new file mode 100644 index 000000000000..746da44de799 --- /dev/null +++ b/tests/run/i17255/Module.scala @@ -0,0 +1,10 @@ +package p { + object Module { + override def toString = "Module" + } +} + +object Test extends App { + assert(p.J.f().toString == "J") + assert(p.J.module().toString == "Module") +} \ No newline at end of file From cbfdf5720aac07c75aad15db63fa26a790ff5bbf Mon Sep 17 00:00:00 2001 From: i10416 Date: Sun, 25 Feb 2024 21:46:30 +0900 Subject: [PATCH 6/7] fix(test): skip a test to avoid linking error due to java source --- tests/run/i17255/Module.scala | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/run/i17255/Module.scala b/tests/run/i17255/Module.scala index 746da44de799..f433adbf0ad6 100644 --- a/tests/run/i17255/Module.scala +++ b/tests/run/i17255/Module.scala @@ -1,3 +1,5 @@ +// scalajs: --skip + package p { object Module { override def toString = "Module" From 96c91da83a32fd44b38df10b26789dd278eefc55 Mon Sep 17 00:00:00 2001 From: 110416 Date: Wed, 28 Feb 2024 11:39:35 +0900 Subject: [PATCH 7/7] Apply suggestions from code review Test imported type as well as fully-qualified type to check `findRef` codepath. Co-authored-by: Jamie Thompson --- tests/run/i17255/J.java | 3 +++ tests/run/i17255/Module.scala | 1 + 2 files changed, 4 insertions(+) diff --git a/tests/run/i17255/J.java b/tests/run/i17255/J.java index 3dfb775423f3..3c6d64d75ab9 100644 --- a/tests/run/i17255/J.java +++ b/tests/run/i17255/J.java @@ -5,6 +5,9 @@ public class J { public static p.J f() { return p.J.j; } + public static Module$ module2() { + return p.Module$.MODULE$; + } public static p.Module$ module() { return p.Module$.MODULE$; } diff --git a/tests/run/i17255/Module.scala b/tests/run/i17255/Module.scala index f433adbf0ad6..e681264093da 100644 --- a/tests/run/i17255/Module.scala +++ b/tests/run/i17255/Module.scala @@ -9,4 +9,5 @@ package p { object Test extends App { assert(p.J.f().toString == "J") assert(p.J.module().toString == "Module") + assert(p.J.module2().toString == "Module") } \ No newline at end of file