Skip to content

Commit

Permalink
Fix java typer problems with inner class references and raw types (sc…
Browse files Browse the repository at this point in the history
…ala#19747)

Tests changes against source dependency, Tasty dependency, and class
dependency.

fixes scala#19619
  • Loading branch information
bishabosha committed Feb 22, 2024
2 parents be8abfa + faf8547 commit 51ea65a
Show file tree
Hide file tree
Showing 22 changed files with 813 additions and 16 deletions.
15 changes: 12 additions & 3 deletions compiler/src/dotty/tools/dotc/core/ContextOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ object ContextOps:
if (elem.name == name) return elem.sym.denot // return self
}
val pre = ctx.owner.thisType
if ctx.isJava then javaFindMember(name, pre, required, excluded)
if ctx.isJava then
// Note: I didn't verify if there exists a code path that would require `lookInCompanion = true`,
// it is just to preserve the original behavior.
javaFindMember(name, pre, lookInCompanion = true, required, excluded)
else pre.findMember(name, pre, required, excluded)
}
else // we are in the outermost context belonging to a class; self is invisible here. See inClassContext.
Expand All @@ -43,7 +46,13 @@ object ContextOps:
ctx.scope.denotsNamed(name).filterWithFlags(required, excluded).toDenot(NoPrefix)
}

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

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

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

Expand Down
7 changes: 7 additions & 0 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1512,6 +1512,13 @@ object SymDenotations {
def namedType(using Context): NamedType =
if (isType) typeRef else termRef

/** Like typeRef, but the prefix is widened.
*
* See tests/neg/i19619/Test.scala
*/
def javaTypeRef(using Context) =
TypeRef(maybeOwner.reachablePrefix.widen, symbol)

/** Like typeRef, but objects in the prefix are represented by their singleton type,
* this means we output `pre.O.member` rather than `pre.O$.this.member`.
*
Expand Down
12 changes: 8 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1542,17 +1542,19 @@ class Namer { typer: Typer =>
end parentType

/** Check parent type tree `parent` for the following well-formedness conditions:
* (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix)
* (1) It must be a class type with a stable prefix (unless `isJava`) (@see checkClassTypeWithStablePrefix)
* (2) If may not derive from itself
* (3) The class is not final
* (4) If the class is sealed, it is defined in the same compilation unit as the current class
*
* @param isJava If true, the parent type is in Java mode, and we do not require a stable prefix
*/
def checkedParentType(parent: untpd.Tree): Type = {
def checkedParentType(parent: untpd.Tree, isJava: Boolean): Type = {
val ptype = parentType(parent)(using completerCtx.superCallContext).dealiasKeepAnnots
if (cls.isRefinementClass) ptype
else {
val pt = checkClassType(ptype, parent.srcPos,
traitReq = parent ne parents.head, stablePrefixReq = true)
traitReq = parent ne parents.head, stablePrefixReq = !isJava)
if (pt.derivesFrom(cls)) {
val addendum = parent match {
case Select(qual: Super, _) if Feature.migrateTo3 =>
Expand Down Expand Up @@ -1621,7 +1623,9 @@ class Namer { typer: Typer =>
val parentTypes = defn.adjustForTuple(cls, cls.typeParams,
defn.adjustForBoxedUnit(cls,
addUsingTraits(
ensureFirstIsClass(cls, parents.map(checkedParentType(_)))
locally:
val isJava = ctx.isJava
ensureFirstIsClass(cls, parents.map(checkedParentType(_, isJava)))
)
)
)
Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,10 @@ trait TypeAssigner {
val pre = maybeSkolemizePrefix(qualType, name)
val mbr =
if ctx.isJava then
ctx.javaFindMember(name, pre)
// don't look in the companion class here if qual is a module,
// we use backtracking to instead change the qual to the companion class
// if this fails.
ctx.javaFindMember(name, pre, lookInCompanion = false)
else
qualType.findMember(name, pre)

Expand Down
22 changes: 14 additions & 8 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,10 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
if (isSelfDenot(defDenot)) curOwner.enclosingClass.thisType
else if (ctx.isJava && defDenot.symbol.isStatic) {
defDenot.symbol.namedType
}
else if (ctx.isJava && defDenot.symbol.isClass) {
// in a java context a raw identifier to a class should have a widened prefix.
defDenot.symbol.javaTypeRef
} else {
val effectiveOwner =
if (curOwner.isTerm && defDenot.symbol.maybeOwner.isType)
Expand Down Expand Up @@ -4271,14 +4275,16 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
val tree1 =
if ((pt eq AnyTypeConstructorProto) || tp.typeParamSymbols.isEmpty) tree
else {
val tp1 =
if (ctx.isJava)
// Cook raw type
AppliedType(tree.tpe, tp.typeParams.map(Function.const(TypeBounds.empty)))
else
// Eta-expand higher-kinded type
tree.tpe.etaExpand(tp.typeParamSymbols)
tree.withType(tp1)
if (ctx.isJava)
// Cook raw type
val typeArgs = tp.typeParams.map(Function.const(TypeBounds.empty))
val tree1 = AppliedTypeTree(tree, typeArgs.map(TypeTree(_)))
val tp1 = AppliedType(tree.tpe, typeArgs)
tree1.withType(tp1)
else
// Eta-expand higher-kinded type
val tp1 = tree.tpe.etaExpand(tp.typeParamSymbols)
tree.withType(tp1)
}
if (ctx.mode.is(Mode.Pattern) || ctx.mode.isQuotedPattern || tree1.tpe <:< pt) tree1
else err.typeMismatch(tree1, pt)
Expand Down
65 changes: 65 additions & 0 deletions sbt-test/pipelining/Yjava-tasty-paths/a/InnerClass.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package a;

public class InnerClass {

public class Inner<U> {
public U innerField;

public Inner(U innerField) {
this.innerField = innerField;
}

public U getInnerField() {
return innerField;
}
}

public class Outer<U> {

public class Nested<V> {

public U outerField;
public V innerField;

public Nested(U outerField, V innerField) {
this.outerField = outerField;
this.innerField = innerField;
}

public U getOuterField() {
return outerField;
}

public V getInnerField() {
return innerField;
}
}
}

public <U> Inner<U> createInner(U innerField) {
return new Inner<>(innerField);
}

public <U, V> Outer<U>.Nested<V> createNested(U outerField, V innerField) {
Outer<U> outer = new Outer<>();
return outer.new Nested<>(outerField, innerField);
}

public static <U> InnerClass.Inner<U> createInnerStatic(U innerField) {
InnerClass innerClass = new InnerClass();
return innerClass.new Inner<>(innerField);
}

public static <U, V> InnerClass.Outer<U>.Nested<V> createNestedStatic(U outerField, V innerField) {
InnerClass innerClass = new InnerClass();
InnerClass.Outer<U> outer = innerClass.new Outer<>();
return outer.new Nested<>(outerField, innerField);
}

public static <U, V> void consumeNestedStatic(InnerClass.Outer<U>.Nested<V> nested) {
}

public static <U, V> void consumeNestedStatic2(Outer<U>.Nested<V> nested) {
}

}
78 changes: 78 additions & 0 deletions sbt-test/pipelining/Yjava-tasty-paths/a/InnerClassGen.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package a;

public class InnerClassGen<T> {

public class Inner<U> {
public T rootField;
public U innerField;

public Inner(T rootField, U innerField) {
this.rootField = rootField;
this.innerField = innerField;
}

public T getRootField() {
return rootField;
}

public U getInnerField() {
return innerField;
}
}

public class Outer<U> {

public class Nested<V> {
public T rootField;
public U outerField;
public V innerField;

public Nested(T rootField, U outerField, V innerField) {
this.rootField = rootField;
this.outerField = outerField;
this.innerField = innerField;
}

public T getRootField() {
return rootField;
}

public U getOuterField() {
return outerField;
}

public V getInnerField() {
return innerField;
}
}
}

public static class OuterStatic<U> {
public static class NestedStatic<V> {
}
}

public <U> Inner<U> createInner(T rootField, U innerField) {
return new Inner<>(rootField, innerField);
}

public <U, V> Outer<U>.Nested<V> createNested(T rootField, U outerField, V innerField) {
Outer<U> outer = new Outer<>();
return outer.new Nested<>(rootField, outerField, innerField);
}

public static <T, U> InnerClassGen<T>.Inner<U> createInnerStatic(T rootField, U innerField) {
InnerClassGen<T> innerClassGen = new InnerClassGen<>();
return innerClassGen.new Inner<>(rootField, innerField);
}

public static <T, U, V> InnerClassGen<T>.Outer<U>.Nested<V> createNestedStatic(T rootField, U outerField, V innerField) {
InnerClassGen<T> innerClassGen = new InnerClassGen<>();
InnerClassGen<T>.Outer<U> outer = innerClassGen.new Outer<>();
return outer.new Nested<>(rootField, outerField, innerField);
}

public static <T, U, V> void consumeNestedStatic(InnerClassGen<T>.Outer<U>.Nested<V> nested) {
}

}
52 changes: 52 additions & 0 deletions sbt-test/pipelining/Yjava-tasty-paths/a/InnerClassSub.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package a;

public class InnerClassSub extends InnerClass {

public class InnerSub<U> extends Inner<U> {
public InnerSub(U innerField) {
super(innerField);
}
}

public class OuterSub<U> extends Outer<U> {
public OuterSub() {
super();
}
}

public <U> Inner<U> createInnerSub(U innerField) {
return new InnerSub<>(innerField);
}

public <U, V> Outer<U>.Nested<V> createNestedSub(U outerField, V innerField) {
OuterSub<U> outer = new OuterSub<>();
return outer.new Nested<>(outerField, innerField);
}

public <U> InnerClass.Inner<U> createInnerSub2(U innerField) {
return new InnerSub<>(innerField);
}

public <U, V> InnerClass.Outer<U>.Nested<V> createNestedSub2(U outerField, V innerField) {
OuterSub<U> outer = new OuterSub<>();
return outer.new Nested<>(outerField, innerField);
}

public static <U> InnerClass.Inner<U> createInnerStatic(U innerField) {
InnerClassSub innerClass = new InnerClassSub();
return innerClass.new Inner<>(innerField);
}

public static <U, V> InnerClass.Outer<U>.Nested<V> createNestedStatic(U outerField, V innerField) {
InnerClassSub innerClass = new InnerClassSub();
InnerClassSub.Outer<U> outer = innerClass.new Outer<>();
return outer.new Nested<>(outerField, innerField);
}

public static <U, V> void consumeNestedStatic(InnerClass.Outer<U>.Nested<V> nested) {
}

public static <U, V> void consumeNestedStatic2(Outer<U>.Nested<V> nested) {
}

}
16 changes: 16 additions & 0 deletions sbt-test/pipelining/Yjava-tasty-paths/a/RawTypes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package a;

public class RawTypes {

public class C<T> {
public class D<U> {
}
}

public static void mii_Raw_Raw(RawTypes.C.D d) {
}

public static void mii_Raw_Raw2(C.D d) {
}

}
2 changes: 2 additions & 0 deletions sbt-test/pipelining/Yjava-tasty-paths/a/package.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// THIS FILE EXISTS SO THAT `A.java` WILL BE COMPILED BY SCALAC
package a
Loading

0 comments on commit 51ea65a

Please sign in to comment.