Skip to content

Commit 51ea65a

Browse files
authored
Fix java typer problems with inner class references and raw types (#19747)
Tests changes against source dependency, Tasty dependency, and class dependency. fixes #19619
2 parents be8abfa + faf8547 commit 51ea65a

22 files changed

+813
-16
lines changed

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,10 @@ object ContextOps:
3434
if (elem.name == name) return elem.sym.denot // return self
3535
}
3636
val pre = ctx.owner.thisType
37-
if ctx.isJava then javaFindMember(name, pre, required, excluded)
37+
if ctx.isJava then
38+
// Note: I didn't verify if there exists a code path that would require `lookInCompanion = true`,
39+
// it is just to preserve the original behavior.
40+
javaFindMember(name, pre, lookInCompanion = true, required, excluded)
3841
else pre.findMember(name, pre, required, excluded)
3942
}
4043
else // we are in the outermost context belonging to a class; self is invisible here. See inClassContext.
@@ -43,7 +46,13 @@ object ContextOps:
4346
ctx.scope.denotsNamed(name).filterWithFlags(required, excluded).toDenot(NoPrefix)
4447
}
4548

46-
final def javaFindMember(name: Name, pre: Type, required: FlagSet = EmptyFlags, excluded: FlagSet = EmptyFlags): Denotation =
49+
/** Look in the prefix with Java semantics.
50+
* @param lookInCompanion If true, try in the companion class of a module as a fallback.
51+
* Note: originally this was used to type Select nodes in Java code,
52+
* but that is no longer the case.
53+
* It is preserved in case it is necessary for denotNamed, but this is unverified.
54+
*/
55+
final def javaFindMember(name: Name, pre: Type, lookInCompanion: Boolean, required: FlagSet = EmptyFlags, excluded: FlagSet = EmptyFlags): Denotation =
4756
assert(ctx.isJava)
4857
inContext(ctx) {
4958

@@ -53,7 +62,7 @@ object ContextOps:
5362
val directSearch = pre.findMember(name, pre, required, excluded)
5463

5564
// 2. Try to search in companion class if current is an object.
56-
def searchCompanionClass = if preSym.is(Flags.Module) then
65+
def searchCompanionClass = if lookInCompanion && preSym.is(Flags.Module) then
5766
preSym.companionClass.thisType.findMember(name, pre, required, excluded)
5867
else NoDenotation
5968

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1512,6 +1512,13 @@ object SymDenotations {
15121512
def namedType(using Context): NamedType =
15131513
if (isType) typeRef else termRef
15141514

1515+
/** Like typeRef, but the prefix is widened.
1516+
*
1517+
* See tests/neg/i19619/Test.scala
1518+
*/
1519+
def javaTypeRef(using Context) =
1520+
TypeRef(maybeOwner.reachablePrefix.widen, symbol)
1521+
15151522
/** Like typeRef, but objects in the prefix are represented by their singleton type,
15161523
* this means we output `pre.O.member` rather than `pre.O$.this.member`.
15171524
*

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

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1542,17 +1542,19 @@ class Namer { typer: Typer =>
15421542
end parentType
15431543

15441544
/** Check parent type tree `parent` for the following well-formedness conditions:
1545-
* (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix)
1545+
* (1) It must be a class type with a stable prefix (unless `isJava`) (@see checkClassTypeWithStablePrefix)
15461546
* (2) If may not derive from itself
15471547
* (3) The class is not final
15481548
* (4) If the class is sealed, it is defined in the same compilation unit as the current class
1549+
*
1550+
* @param isJava If true, the parent type is in Java mode, and we do not require a stable prefix
15491551
*/
1550-
def checkedParentType(parent: untpd.Tree): Type = {
1552+
def checkedParentType(parent: untpd.Tree, isJava: Boolean): Type = {
15511553
val ptype = parentType(parent)(using completerCtx.superCallContext).dealiasKeepAnnots
15521554
if (cls.isRefinementClass) ptype
15531555
else {
15541556
val pt = checkClassType(ptype, parent.srcPos,
1555-
traitReq = parent ne parents.head, stablePrefixReq = true)
1557+
traitReq = parent ne parents.head, stablePrefixReq = !isJava)
15561558
if (pt.derivesFrom(cls)) {
15571559
val addendum = parent match {
15581560
case Select(qual: Super, _) if Feature.migrateTo3 =>
@@ -1621,7 +1623,9 @@ class Namer { typer: Typer =>
16211623
val parentTypes = defn.adjustForTuple(cls, cls.typeParams,
16221624
defn.adjustForBoxedUnit(cls,
16231625
addUsingTraits(
1624-
ensureFirstIsClass(cls, parents.map(checkedParentType(_)))
1626+
locally:
1627+
val isJava = ctx.isJava
1628+
ensureFirstIsClass(cls, parents.map(checkedParentType(_, isJava)))
16251629
)
16261630
)
16271631
)

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,10 @@ trait TypeAssigner {
156156
val pre = maybeSkolemizePrefix(qualType, name)
157157
val mbr =
158158
if ctx.isJava then
159-
ctx.javaFindMember(name, pre)
159+
// don't look in the companion class here if qual is a module,
160+
// we use backtracking to instead change the qual to the companion class
161+
// if this fails.
162+
ctx.javaFindMember(name, pre, lookInCompanion = false)
160163
else
161164
qualType.findMember(name, pre)
162165

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

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -461,6 +461,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
461461
if (isSelfDenot(defDenot)) curOwner.enclosingClass.thisType
462462
else if (ctx.isJava && defDenot.symbol.isStatic) {
463463
defDenot.symbol.namedType
464+
}
465+
else if (ctx.isJava && defDenot.symbol.isClass) {
466+
// in a java context a raw identifier to a class should have a widened prefix.
467+
defDenot.symbol.javaTypeRef
464468
} else {
465469
val effectiveOwner =
466470
if (curOwner.isTerm && defDenot.symbol.maybeOwner.isType)
@@ -4271,14 +4275,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
42714275
val tree1 =
42724276
if ((pt eq AnyTypeConstructorProto) || tp.typeParamSymbols.isEmpty) tree
42734277
else {
4274-
val tp1 =
4275-
if (ctx.isJava)
4276-
// Cook raw type
4277-
AppliedType(tree.tpe, tp.typeParams.map(Function.const(TypeBounds.empty)))
4278-
else
4279-
// Eta-expand higher-kinded type
4280-
tree.tpe.etaExpand(tp.typeParamSymbols)
4281-
tree.withType(tp1)
4278+
if (ctx.isJava)
4279+
// Cook raw type
4280+
val typeArgs = tp.typeParams.map(Function.const(TypeBounds.empty))
4281+
val tree1 = AppliedTypeTree(tree, typeArgs.map(TypeTree(_)))
4282+
val tp1 = AppliedType(tree.tpe, typeArgs)
4283+
tree1.withType(tp1)
4284+
else
4285+
// Eta-expand higher-kinded type
4286+
val tp1 = tree.tpe.etaExpand(tp.typeParamSymbols)
4287+
tree.withType(tp1)
42824288
}
42834289
if (ctx.mode.is(Mode.Pattern) || ctx.mode.isQuotedPattern || tree1.tpe <:< pt) tree1
42844290
else err.typeMismatch(tree1, pt)
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
package a;
2+
3+
public class InnerClass {
4+
5+
public class Inner<U> {
6+
public U innerField;
7+
8+
public Inner(U innerField) {
9+
this.innerField = innerField;
10+
}
11+
12+
public U getInnerField() {
13+
return innerField;
14+
}
15+
}
16+
17+
public class Outer<U> {
18+
19+
public class Nested<V> {
20+
21+
public U outerField;
22+
public V innerField;
23+
24+
public Nested(U outerField, V innerField) {
25+
this.outerField = outerField;
26+
this.innerField = innerField;
27+
}
28+
29+
public U getOuterField() {
30+
return outerField;
31+
}
32+
33+
public V getInnerField() {
34+
return innerField;
35+
}
36+
}
37+
}
38+
39+
public <U> Inner<U> createInner(U innerField) {
40+
return new Inner<>(innerField);
41+
}
42+
43+
public <U, V> Outer<U>.Nested<V> createNested(U outerField, V innerField) {
44+
Outer<U> outer = new Outer<>();
45+
return outer.new Nested<>(outerField, innerField);
46+
}
47+
48+
public static <U> InnerClass.Inner<U> createInnerStatic(U innerField) {
49+
InnerClass innerClass = new InnerClass();
50+
return innerClass.new Inner<>(innerField);
51+
}
52+
53+
public static <U, V> InnerClass.Outer<U>.Nested<V> createNestedStatic(U outerField, V innerField) {
54+
InnerClass innerClass = new InnerClass();
55+
InnerClass.Outer<U> outer = innerClass.new Outer<>();
56+
return outer.new Nested<>(outerField, innerField);
57+
}
58+
59+
public static <U, V> void consumeNestedStatic(InnerClass.Outer<U>.Nested<V> nested) {
60+
}
61+
62+
public static <U, V> void consumeNestedStatic2(Outer<U>.Nested<V> nested) {
63+
}
64+
65+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package a;
2+
3+
public class InnerClassGen<T> {
4+
5+
public class Inner<U> {
6+
public T rootField;
7+
public U innerField;
8+
9+
public Inner(T rootField, U innerField) {
10+
this.rootField = rootField;
11+
this.innerField = innerField;
12+
}
13+
14+
public T getRootField() {
15+
return rootField;
16+
}
17+
18+
public U getInnerField() {
19+
return innerField;
20+
}
21+
}
22+
23+
public class Outer<U> {
24+
25+
public class Nested<V> {
26+
public T rootField;
27+
public U outerField;
28+
public V innerField;
29+
30+
public Nested(T rootField, U outerField, V innerField) {
31+
this.rootField = rootField;
32+
this.outerField = outerField;
33+
this.innerField = innerField;
34+
}
35+
36+
public T getRootField() {
37+
return rootField;
38+
}
39+
40+
public U getOuterField() {
41+
return outerField;
42+
}
43+
44+
public V getInnerField() {
45+
return innerField;
46+
}
47+
}
48+
}
49+
50+
public static class OuterStatic<U> {
51+
public static class NestedStatic<V> {
52+
}
53+
}
54+
55+
public <U> Inner<U> createInner(T rootField, U innerField) {
56+
return new Inner<>(rootField, innerField);
57+
}
58+
59+
public <U, V> Outer<U>.Nested<V> createNested(T rootField, U outerField, V innerField) {
60+
Outer<U> outer = new Outer<>();
61+
return outer.new Nested<>(rootField, outerField, innerField);
62+
}
63+
64+
public static <T, U> InnerClassGen<T>.Inner<U> createInnerStatic(T rootField, U innerField) {
65+
InnerClassGen<T> innerClassGen = new InnerClassGen<>();
66+
return innerClassGen.new Inner<>(rootField, innerField);
67+
}
68+
69+
public static <T, U, V> InnerClassGen<T>.Outer<U>.Nested<V> createNestedStatic(T rootField, U outerField, V innerField) {
70+
InnerClassGen<T> innerClassGen = new InnerClassGen<>();
71+
InnerClassGen<T>.Outer<U> outer = innerClassGen.new Outer<>();
72+
return outer.new Nested<>(rootField, outerField, innerField);
73+
}
74+
75+
public static <T, U, V> void consumeNestedStatic(InnerClassGen<T>.Outer<U>.Nested<V> nested) {
76+
}
77+
78+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package a;
2+
3+
public class InnerClassSub extends InnerClass {
4+
5+
public class InnerSub<U> extends Inner<U> {
6+
public InnerSub(U innerField) {
7+
super(innerField);
8+
}
9+
}
10+
11+
public class OuterSub<U> extends Outer<U> {
12+
public OuterSub() {
13+
super();
14+
}
15+
}
16+
17+
public <U> Inner<U> createInnerSub(U innerField) {
18+
return new InnerSub<>(innerField);
19+
}
20+
21+
public <U, V> Outer<U>.Nested<V> createNestedSub(U outerField, V innerField) {
22+
OuterSub<U> outer = new OuterSub<>();
23+
return outer.new Nested<>(outerField, innerField);
24+
}
25+
26+
public <U> InnerClass.Inner<U> createInnerSub2(U innerField) {
27+
return new InnerSub<>(innerField);
28+
}
29+
30+
public <U, V> InnerClass.Outer<U>.Nested<V> createNestedSub2(U outerField, V innerField) {
31+
OuterSub<U> outer = new OuterSub<>();
32+
return outer.new Nested<>(outerField, innerField);
33+
}
34+
35+
public static <U> InnerClass.Inner<U> createInnerStatic(U innerField) {
36+
InnerClassSub innerClass = new InnerClassSub();
37+
return innerClass.new Inner<>(innerField);
38+
}
39+
40+
public static <U, V> InnerClass.Outer<U>.Nested<V> createNestedStatic(U outerField, V innerField) {
41+
InnerClassSub innerClass = new InnerClassSub();
42+
InnerClassSub.Outer<U> outer = innerClass.new Outer<>();
43+
return outer.new Nested<>(outerField, innerField);
44+
}
45+
46+
public static <U, V> void consumeNestedStatic(InnerClass.Outer<U>.Nested<V> nested) {
47+
}
48+
49+
public static <U, V> void consumeNestedStatic2(Outer<U>.Nested<V> nested) {
50+
}
51+
52+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
package a;
2+
3+
public class RawTypes {
4+
5+
public class C<T> {
6+
public class D<U> {
7+
}
8+
}
9+
10+
public static void mii_Raw_Raw(RawTypes.C.D d) {
11+
}
12+
13+
public static void mii_Raw_Raw2(C.D d) {
14+
}
15+
16+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// THIS FILE EXISTS SO THAT `A.java` WILL BE COMPILED BY SCALAC
2+
package a

0 commit comments

Comments
 (0)