Skip to content

Commit 9e6ca45

Browse files
authored
Merge pull request scala#7738 from retronym/backport/7671-java-ident
[backport] Use Java rules for member lookup in .java sources
2 parents 5a17313 + 91bf0c7 commit 9e6ca45

File tree

13 files changed

+178
-28
lines changed

13 files changed

+178
-28
lines changed

src/compiler/scala/tools/nsc/typechecker/Contexts.scala

Lines changed: 41 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ trait Contexts { self: Analyzer =>
5050
val javaAndScalaList = JavaLangPackage :: ScalaPackage :: Nil
5151
val completeList = JavaLangPackage :: ScalaPackage :: PredefModule :: Nil
5252
}
53+
private lazy val NoJavaMemberFound = (NoType, NoSymbol)
5354

5455
def ambiguousImports(imp1: ImportInfo, imp2: ImportInfo) =
5556
LookupAmbiguous(s"it is imported twice in the same scope by\n$imp1\nand $imp2")
@@ -1024,7 +1025,7 @@ trait Contexts { self: Analyzer =>
10241025
imp.importedSymbol(name, requireExplicit, record) filter (s => isAccessible(s, imp.qual.tpe, superAccess = false))
10251026

10261027
private[Contexts] def requiresQualifier(s: Symbol): Boolean = (
1027-
s.owner.isClass
1028+
s.owner.isClass
10281029
&& !s.owner.isPackageClass
10291030
&& !s.isTypeParameterOrSkolem
10301031
&& !s.isExistentiallyBound
@@ -1074,6 +1075,31 @@ trait Contexts { self: Analyzer =>
10741075
}
10751076
}
10761077

1078+
final def javaFindMember(pre: Type, name: Name, qualifies: Symbol => Boolean): (Type, Symbol) = {
1079+
val sym = pre.member(name).filter(qualifies)
1080+
val preSym = pre.typeSymbol
1081+
if (sym.exists || preSym.isPackageClass || !preSym.isClass) (pre, sym)
1082+
else {
1083+
// In Java code, static innner classes, which we model as members of the companion object,
1084+
// can be referenced from an ident in a subclass or by a selection prefixed by the subclass.
1085+
val toSearch = if (preSym.isModuleClass) companionSymbolOf(pre.typeSymbol.sourceModule, this).baseClasses else preSym.baseClasses
1086+
toSearch.iterator.map { bc =>
1087+
val pre1 = bc.typeOfThis
1088+
val found = pre1.decl(name)
1089+
found.filter(qualifies) match {
1090+
case NoSymbol =>
1091+
val pre2 = companionSymbolOf(pre1.typeSymbol, this).typeOfThis
1092+
val found = pre2.decl(name).filter(qualifies)
1093+
found match {
1094+
case NoSymbol => NoJavaMemberFound
1095+
case sym => (pre2, sym)
1096+
}
1097+
case sym => (pre1, sym)
1098+
}
1099+
}.find(_._2 ne NoSymbol).getOrElse(NoJavaMemberFound)
1100+
}
1101+
}
1102+
10771103
} //class Context
10781104

10791105
/** Find the symbol of a simple name starting from this context.
@@ -1107,7 +1133,7 @@ trait Contexts { self: Analyzer =>
11071133
}
11081134
)
11091135
def finishDefSym(sym: Symbol, pre0: Type): NameLookup =
1110-
if (thisContext.requiresQualifier(sym))
1136+
if (!thisContext.unit.isJava && thisContext.requiresQualifier(sym))
11111137
finish(gen.mkAttributedQualifier(pre0), sym)
11121138
else
11131139
finish(EmptyTree, sym)
@@ -1119,15 +1145,19 @@ trait Contexts { self: Analyzer =>
11191145
)
11201146
)
11211147
def lookupInPrefix(name: Name) = {
1122-
val sym = pre.member(name).filter(qualifies)
1123-
def isNonPackageNoModuleClass(sym: Symbol) =
1124-
sym.isClass && !sym.isModuleClass && !sym.isPackageClass
1125-
if (!sym.exists && thisContext.unit.isJava && isNonPackageNoModuleClass(pre.typeSymbol)) {
1126-
// TODO factor out duplication with Typer::inCompanionForJavaStatic
1127-
val pre1 = companionSymbolOf(pre.typeSymbol, thisContext).typeOfThis
1128-
pre1.member(name).filter(qualifies).andAlso(_ => pre = pre1)
1129-
} else sym
1148+
if (thisContext.unit.isJava) {
1149+
thisContext.javaFindMember(pre, name, qualifies) match {
1150+
case (_, NoSymbol) =>
1151+
NoSymbol
1152+
case (pre1, sym) =>
1153+
pre = pre1
1154+
sym
1155+
}
1156+
} else {
1157+
pre.member(name).filter(qualifies)
1158+
}
11301159
}
1160+
11311161
def accessibleInPrefix(s: Symbol) =
11321162
thisContext.isAccessible(s, pre, superAccess = false)
11331163

@@ -1237,8 +1267,7 @@ trait Contexts { self: Analyzer =>
12371267
}
12381268

12391269
// At this point only one or the other of defSym and impSym might be set.
1240-
if (defSym.exists)
1241-
finishDefSym(defSym, pre)
1270+
if (defSym.exists) finishDefSym(defSym, pre)
12421271
else if (impSym.exists) {
12431272
// If we find a competitor imp2 which imports the same name, possible outcomes are:
12441273
//

src/compiler/scala/tools/nsc/typechecker/Typers.scala

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -554,7 +554,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
554554
* @return modified tree and new prefix type
555555
*/
556556
private def makeAccessible(tree: Tree, sym: Symbol, pre: Type, site: Tree): (Tree, Type) =
557-
if (context.isInPackageObject(sym, pre.typeSymbol)) {
557+
if (!unit.isJava && context.isInPackageObject(sym, pre.typeSymbol)) {
558558
if (pre.typeSymbol == ScalaPackageClass && sym.isTerm) {
559559
// short cut some aliases. It seems pattern matching needs this
560560
// to notice exhaustiveness and to generate good code when
@@ -671,16 +671,16 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
671671
}
672672
}
673673

674-
/** The member with given name of given qualifier tree */
675-
def member(qual: Tree, name: Name) = {
674+
/** The member with given name of given qualifier type */
675+
def member(qual: Type, name: Name): Symbol = {
676676
def callSiteWithinClass(clazz: Symbol) = context.enclClass.owner hasTransOwner clazz
677-
val includeLocals = qual.tpe match {
677+
val includeLocals = qual match {
678678
case ThisType(clazz) if callSiteWithinClass(clazz) => true
679679
case SuperType(clazz, _) if callSiteWithinClass(clazz.typeSymbol) => true
680680
case _ => phase.next.erasedTypes
681681
}
682-
if (includeLocals) qual.tpe member name
683-
else qual.tpe nonLocalMember name
682+
if (includeLocals) qual member name
683+
else qual nonLocalMember name
684684
}
685685

686686
def silent[T](op: Typer => T,
@@ -1160,7 +1160,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
11601160

11611161
def vanillaAdapt(tree: Tree) = {
11621162
def applyPossible = {
1163-
def applyMeth = member(adaptToName(tree, nme.apply), nme.apply)
1163+
def applyMeth = member(adaptToName(tree, nme.apply).tpe, nme.apply)
11641164
def hasPolymorphicApply = applyMeth.alternatives exists (_.tpe.typeParams.nonEmpty)
11651165
def hasMonomorphicApply = applyMeth.alternatives exists (_.tpe.paramSectionCount > 0)
11661166

@@ -1364,7 +1364,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
13641364
* If no conversion is found, return `qual` unchanged.
13651365
*/
13661366
def adaptToName(qual: Tree, name: Name) =
1367-
if (member(qual, name) != NoSymbol) qual
1367+
if (member(qual.tpe, name) != NoSymbol) qual
13681368
else adaptToMember(qual, HasMember(name))
13691369

13701370
private def validateNoCaseAncestor(clazz: Symbol) = {
@@ -3382,6 +3382,8 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
33823382
if (!context.owner.isPackageClass)
33833383
checkNoDoubleDefs(scope)
33843384

3385+
// Note that Java units don't have synthetics, but there's no point in making a special case (for performance or correctness),
3386+
// as we only type check Java units when running Scaladoc on Java sources.
33853387
addSynthetics(stats1, scope)
33863388
}
33873389
}
@@ -5011,11 +5013,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
50115013

50125014
// For Java, instance and static members are in the same scope, but we put the static ones in the companion object
50135015
// so, when we can't find a member in the class scope, check the companion
5014-
def inCompanionForJavaStatic(pre: Type, cls: Symbol, name: Name): Symbol =
5015-
if (!(context.unit.isJava && cls.isClass && !cls.isModuleClass)) NoSymbol else {
5016-
val companion = companionSymbolOf(cls, context)
5017-
if (!companion.exists) NoSymbol
5018-
else member(gen.mkAttributedRef(pre, companion), name) // assert(res.isStatic, s"inCompanionForJavaStatic($pre, $cls, $name) = $res ${res.debugFlagString}")
5016+
def inCompanionForJavaStatic(cls: Symbol, name: Name): Symbol =
5017+
if (!(context.unit.isJava && cls.isClass)) NoSymbol else {
5018+
context.javaFindMember(cls.typeOfThis, name, _ => true)._2
50195019
}
50205020

50215021
/* Attribute a selection where `tree` is `qual.name`.
@@ -5034,7 +5034,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
50345034
wrapErrors(t, (_.typed1(t, mode, pt)))
50355035
}
50365036

5037-
val sym = tree.symbol orElse member(qual, name) orElse inCompanionForJavaStatic(qual.tpe.prefix, qual.symbol, name)
5037+
val sym = tree.symbol orElse member(qual.tpe, name) orElse inCompanionForJavaStatic(qual.symbol, name)
50385038
if ((sym eq NoSymbol) && name != nme.CONSTRUCTOR && mode.inAny(EXPRmode | PATTERNmode)) {
50395039
// symbol not found? --> try to convert implicitly to a type that does have the required
50405040
// member. Added `| PATTERNmode` to allow enrichment in patterns (so we can add e.g., an
@@ -5151,7 +5151,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
51515151
if (name.isTypeName) {
51525152
val qualTyped = typedTypeSelectionQualifier(tree.qualifier, WildcardType)
51535153
val qualStableOrError =
5154-
if (qualTyped.isErrorTyped || treeInfo.admitsTypeSelection(qualTyped)) qualTyped
5154+
if (qualTyped.isErrorTyped || unit.isJava || treeInfo.admitsTypeSelection(qualTyped)) qualTyped
51555155
else UnstableTreeError(qualTyped)
51565156
typedSelect(tree, qualStableOrError, name)
51575157
} else {
@@ -5205,6 +5205,11 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
52055205
}
52065206
// ignore current variable scope in patterns to enforce linearity
52075207
val startContext = if (mode.typingPatternOrTypePat) context.outer else context
5208+
5209+
def asTypeName = if (mode.inAll(MonoQualifierModes) && unit.isJava && name.isTermName) {
5210+
startContext.lookupSymbol(name.toTypeName, qualifies).symbol
5211+
} else NoSymbol
5212+
52085213
val nameLookup = tree.symbol match {
52095214
case NoSymbol => startContext.lookupSymbol(name, qualifies)
52105215
case sym => LookupSucceeded(EmptyTree, sym)
@@ -5214,7 +5219,7 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
52145219
case LookupAmbiguous(msg) => issue(AmbiguousIdentError(tree, name, msg))
52155220
case LookupInaccessible(sym, msg) => issue(AccessError(tree, sym, context, msg))
52165221
case LookupNotFound =>
5217-
inEmptyPackage orElse lookupInRoot(name) match {
5222+
asTypeName orElse inEmptyPackage orElse lookupInRoot(name) match {
52185223
case NoSymbol => issue(SymbolNotFoundError(tree, name, context.owner, startContext))
52195224
case sym => typed1(tree setSymbol sym, mode, pt)
52205225
}
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+
}
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: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
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+
}

test/files/run/t10490-2.check

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Foo$Bar was instantiated!
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+
}
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+
}

0 commit comments

Comments
 (0)