Skip to content

Commit ecbe3d2

Browse files
smarterretronym
andauthored
Merge pull request #12884 from changvvb/migrate-scala2-7671
Use Java rules for member lookup in .java sources Co-Authored-By: Jason Zaugg <[email protected]>
2 parents 3fc60e4 + 7ff48ef commit ecbe3d2

File tree

18 files changed

+194
-45
lines changed

18 files changed

+194
-45
lines changed

compiler/src/dotty/tools/dotc/core/ContextOps.scala

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ package dotty.tools.dotc
22
package core
33

44
import Contexts._, Symbols._, Types._, Flags._, Scopes._, Decorators._, NameOps._
5-
import Denotations._
6-
import SymDenotations.LazyType, Names.Name, StdNames.nme
5+
import Denotations._, SymDenotations._
6+
import Names.Name, StdNames.nme
77
import ast.untpd
88

99
/** Extension methods for contexts where we want to keep the ctx.<methodName> syntax */
@@ -34,14 +34,51 @@ object ContextOps:
3434
if (elem.name == name) return elem.sym.denot // return self
3535
}
3636
val pre = ctx.owner.thisType
37-
pre.findMember(name, pre, required, excluded)
37+
if ctx.isJava then javaFindMember(name, pre, required, excluded)
38+
else pre.findMember(name, pre, required, excluded)
3839
}
3940
else // we are in the outermost context belonging to a class; self is invisible here. See inClassContext.
4041
ctx.owner.findMember(name, ctx.owner.thisType, required, excluded)
4142
else
4243
ctx.scope.denotsNamed(name).filterWithFlags(required, excluded).toDenot(NoPrefix)
4344
}
4445

46+
final def javaFindMember(name: Name, pre: Type, required: FlagSet = EmptyFlags, excluded: FlagSet = EmptyFlags): Denotation =
47+
assert(ctx.isJava)
48+
inContext(ctx) {
49+
50+
val preSym = pre.typeSymbol
51+
52+
// 1. Try to search in current type and parents.
53+
val directSearch = pre.findMember(name, pre, required, excluded)
54+
55+
// 2. Try to search in companion class if current is an object.
56+
def searchCompanionClass = if preSym.is(Flags.Module) then
57+
preSym.companionClass.thisType.findMember(name, pre, required, excluded)
58+
else NoDenotation
59+
60+
// 3. Try to search in companion objects of super classes.
61+
// In Java code, static inner classes, which we model as members of the companion object,
62+
// can be referenced from an ident in a subclass or by a selection prefixed by the subclass.
63+
def searchSuperCompanionObjects =
64+
val toSearch = if preSym.is(Flags.Module) then
65+
if preSym.companionClass.exists then
66+
preSym.companionClass.asClass.baseClasses
67+
else Nil
68+
else
69+
preSym.asClass.baseClasses
70+
71+
toSearch.iterator.map { bc =>
72+
val pre1 = bc.companionModule.namedType
73+
pre1.findMember(name, pre1, required, excluded)
74+
}.find(_.exists).getOrElse(NoDenotation)
75+
76+
if preSym.isClass then
77+
directSearch orElse searchCompanionClass orElse searchSuperCompanionObjects
78+
else
79+
directSearch
80+
}
81+
4582
/** A fresh local context with given tree and owner.
4683
* Owner might not exist (can happen for self valdefs), in which case
4784
* no owner is set in result context

compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala

Lines changed: 3 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -751,35 +751,8 @@ object JavaParsers {
751751
makeTemplate(List(), statics, List(), false)).withMods((cdef.mods & Flags.RetainedModuleClassFlags).toTermFlags)
752752
}
753753

754-
def importCompanionObject(cdef: TypeDef): Tree =
755-
Import(
756-
Ident(cdef.name.toTermName).withSpan(NoSpan),
757-
ImportSelector(Ident(nme.WILDCARD)) :: Nil)
758-
759-
// Importing the companion object members cannot be done uncritically: see
760-
// ticket #2377 wherein a class contains two static inner classes, each of which
761-
// has a static inner class called "Builder" - this results in an ambiguity error
762-
// when each performs the import in the enclosing class's scope.
763-
//
764-
// To address this I moved the import Companion._ inside the class, as the first
765-
// statement. This should work without compromising the enclosing scope, but may (?)
766-
// end up suffering from the same issues it does in scala - specifically that this
767-
// leaves auxiliary constructors unable to access members of the companion object
768-
// as unqualified identifiers.
769-
def addCompanionObject(statics: List[Tree], cdef: TypeDef): List[Tree] = {
770-
// if there are no statics we can use the original cdef, but we always
771-
// create the companion so import A._ is not an error (see ticket #1700)
772-
val cdefNew =
773-
if (statics.isEmpty) cdef
774-
else {
775-
val template = cdef.rhs.asInstanceOf[Template]
776-
cpy.TypeDef(cdef)(cdef.name,
777-
cpy.Template(template)(body = importCompanionObject(cdef) :: template.body))
778-
.withMods(cdef.mods)
779-
}
780-
781-
List(makeCompanionObject(cdefNew, statics), cdefNew)
782-
}
754+
def addCompanionObject(statics: List[Tree], cdef: TypeDef): List[Tree] =
755+
List(makeCompanionObject(cdef, statics), cdef)
783756

784757
def importDecl(): List[Tree] = {
785758
val start = in.offset
@@ -901,16 +874,7 @@ object JavaParsers {
901874
members) ++= decls
902875
}
903876
}
904-
def forwarders(sdef: Tree): List[Tree] = sdef match {
905-
case TypeDef(name, _) if (parentToken == INTERFACE) =>
906-
var rhs: Tree = Select(Ident(parentName.toTermName), name)
907-
List(TypeDef(name, rhs).withMods(Modifiers(Flags.Protected)))
908-
case _ =>
909-
List()
910-
}
911-
val sdefs = statics.toList
912-
val idefs = members.toList ::: (sdefs flatMap forwarders)
913-
(sdefs, idefs)
877+
(statics.toList, members.toList)
914878
}
915879
def annotationParents: List[Select] = List(
916880
scalaAnnotationDot(tpnme.Annotation),

compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ package typer
44

55
import core._
66
import ast._
7-
import Contexts._, Constants._, Types._, Symbols._, Names._, Flags._, Decorators._
7+
import Contexts._, ContextOps._, Constants._, Types._, Symbols._, Names._, Flags._, Decorators._
88
import ErrorReporting._, Annotations._, Denotations._, SymDenotations._, StdNames._
99
import util.Spans._
1010
import util.SrcPos
@@ -145,7 +145,12 @@ trait TypeAssigner {
145145
// this is exactly what Erasure will do.
146146
case _ =>
147147
val pre = maybeSkolemizePrefix(qualType, name)
148-
val mbr = qualType.findMember(name, pre)
148+
val mbr =
149+
if ctx.isJava then
150+
ctx.javaFindMember(name, pre)
151+
else
152+
qualType.findMember(name, pre)
153+
149154
if reallyExists(mbr) then qualType.select(name, mbr)
150155
else if qualType.isErroneous || name.toTermName == nme.ERROR then UnspecifiedErrorType
151156
else NoType

compiler/src/dotty/tools/dotc/typer/Typer.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,9 @@ class Typer extends Namer
367367
if (qualifies(defDenot)) {
368368
val found =
369369
if (isSelfDenot(defDenot)) curOwner.enclosingClass.thisType
370-
else {
370+
else if (ctx.isJava && defDenot.symbol.isStatic) {
371+
defDenot.symbol.namedType
372+
} else {
371373
val effectiveOwner =
372374
if (curOwner.isTerm && defDenot.symbol.maybeOwner.isType)
373375
// Don't mix NoPrefix and thisType prefixes, since type comparer

compiler/test/dotc/pos-test-pickling.blacklist

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,4 @@ i2797a
6666
# GADT cast applied to singleton type difference
6767
i4176-gadt.scala
6868

69+
java-inherited-type1

compiler/test/dotc/run-test-pickling.blacklist

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,5 @@ varargs-abstract
3535
zero-arity-case-class.scala
3636
i12194.scala
3737
i12753
38+
t6138
39+
t6138-2
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
object Client {
2+
def test= {
3+
Test.Outer.Nested.sig
4+
Test.Outer.Nested.sig1
5+
Test.Outer.Nested.sig2
6+
val o = new Test.Outer
7+
new o.Nested1().sig
8+
new o.Nested1().sig1
9+
new o.Nested1().sig2
10+
}
11+
12+
def test1 = {
13+
val t = new Test
14+
val o = new t.Outer1
15+
new o.Nested1().sig
16+
new o.Nested1().sig1
17+
new o.Nested1().sig2
18+
}
19+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
public class Test {
2+
static class OuterBase implements OuterBaseInterface {
3+
static class StaticInner {}
4+
class Inner {}
5+
}
6+
interface OuterBaseInterface {
7+
interface InnerFromInterface {}
8+
}
9+
public static class Outer extends OuterBase {
10+
public static class Nested {
11+
public static P<StaticInner, Inner, InnerFromInterface> sig; // was: "type StaticInner", "not found: type Inner", "not found: type InnerFromInterface"
12+
public static P<Outer.StaticInner, Outer.Inner, Outer.InnerFromInterface> sig1; // was: "type StaticInner is not a member of Test.Outer"
13+
public static P<OuterBase.StaticInner, OuterBase.Inner, OuterBaseInterface.InnerFromInterface> sig2;
14+
15+
}
16+
public class Nested1 {
17+
public P<StaticInner, Inner, InnerFromInterface> sig; // was: "not found: type StaticInner"
18+
public P<Outer.StaticInner, Outer.Inner, Outer.InnerFromInterface> sig1; // was: "type StaticInner is not a member of Test.Outer"
19+
public P<OuterBase.StaticInner, OuterBase.Inner, OuterBaseInterface.InnerFromInterface> sig2;
20+
}
21+
}
22+
public class Outer1 extends OuterBase {
23+
public class Nested1 {
24+
public P<StaticInner, Inner, InnerFromInterface> sig; // was: "not found: type StaticInner"
25+
public P<Outer.StaticInner, Outer.Inner, Outer.InnerFromInterface> sig1; // was: "type StaticInner is not a member of Test.Outer"
26+
public P<OuterBase.StaticInner, OuterBase.Inner, OuterBaseInterface.InnerFromInterface> sig2;
27+
}
28+
}
29+
public static class P<A, B, C>{}
30+
}

tests/pos/java-inherited-type1/J.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class J extends S {
2+
// These references all work in Javac because `object O { class I }` erases to `O$I`
3+
4+
void select1(S1.Inner1 i) { new S1.Inner1(); }
5+
void ident(Inner i) {}
6+
7+
void ident1(Inner1 i) {}
8+
void select(S.Inner i) { new S.Inner(); }
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
class S extends S1
2+
object S {
3+
class Inner
4+
}
5+
6+
class S1
7+
object S1 {
8+
class Inner1
9+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
object Test {
2+
val j = new J
3+
// force completion of these signatures
4+
j.ident(null);
5+
j.ident1(null);
6+
j.select(null);
7+
j.select1(null);
8+
9+
val message:TestMessage = null
10+
val builder:TestMessage.Builder = message.toBuilder
11+
builder.setName("name")
12+
13+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
abstract class AbstractMessage {
2+
public static abstract class Builder<BuilderType extends Builder<BuilderType>> {
3+
}
4+
}
5+
6+
class TestMessage extends AbstractMessage {
7+
8+
public Builder toBuilder() {
9+
return null;
10+
}
11+
12+
public static class Builder extends AbstractMessage.Builder<Builder> {
13+
public Builder setName(String name) {
14+
return this;
15+
}
16+
}
17+
}

tests/run/t6138-2.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Foo$Bar was instantiated!

tests/run/t6138-2/JavaClass.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
public class JavaClass {
2+
// This is defined in ScalaClass
3+
public static final Foo.Bar bar = new Foo.Bar();
4+
}

tests/run/t6138-2/ScalaClass.scala

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/* Similar to t10490 -- but defines `Foo` in the object.
2+
* Placing this test within t10490 makes it work without a fix, that's why it's independent.
3+
* Note that this was already working, we add it to make sure we don't regress
4+
*/
5+
6+
class Foo
7+
object Foo {
8+
class Bar {
9+
override def toString: String = "Foo$Bar was instantiated!"
10+
}
11+
}
12+
13+
object Test {
14+
def main(args: Array[String]): Unit = {
15+
// JavaClass is the user of the Scala defined classes
16+
println(JavaClass.bar)
17+
}
18+
}

tests/run/t6138/JavaClass.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
public class JavaClass {
2+
// This is defined in ScalaClass
3+
public static final Foo.Bar bar = (new Foo()).new Bar();
4+
}

tests/run/t6138/ScalaClass.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
class Foo {
2+
class Bar {
3+
override def toString: String = "Foo$Bar was instantiated!"
4+
}
5+
}
6+
7+
object Test {
8+
def main(args: Array[String]): Unit = {
9+
// JavaClass is the user of the Scala defined classes
10+
println(JavaClass.bar)
11+
//println(JavaClass.baz)
12+
}
13+
}

tests/run/t6238.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Foo$Bar was instantiated!

0 commit comments

Comments
 (0)