From 76e941c74b16d42fb07c99b22a482b55f4b7fa02 Mon Sep 17 00:00:00 2001 From: changvvb Date: Mon, 21 Jun 2021 00:47:16 +0800 Subject: [PATCH 1/5] Migrate scala2 PR-7671 --- .../dotty/tools/dotc/core/ContextOps.scala | 43 +++++++++++++++++-- .../tools/dotc/parsing/JavaParsers.scala | 11 +---- .../dotty/tools/dotc/typer/TypeAssigner.scala | 9 +++- .../src/dotty/tools/dotc/typer/Typer.scala | 4 +- .../test/dotc/pos-test-pickling.blacklist | 1 + .../test/dotc/run-test-pickling.blacklist | 2 + tests/pos/java-inherited-type/Client.scala | 19 ++++++++ tests/pos/java-inherited-type/Test.java | 30 +++++++++++++ tests/pos/java-inherited-type1/J.java | 9 ++++ tests/pos/java-inherited-type1/S.scala | 9 ++++ tests/pos/java-inherited-type1/Test.scala | 13 ++++++ .../pos/java-inherited-type1/TestMessage.java | 17 ++++++++ tests/run/t6138-2.check | 1 + tests/run/t6138-2/JavaClass.java | 4 ++ tests/run/t6138-2/ScalaClass.scala | 18 ++++++++ tests/run/t6138/JavaClass.java | 4 ++ tests/run/t6138/ScalaClass.scala | 13 ++++++ tests/run/t6238.check | 1 + 18 files changed, 192 insertions(+), 16 deletions(-) create mode 100644 tests/pos/java-inherited-type/Client.scala create mode 100644 tests/pos/java-inherited-type/Test.java create mode 100644 tests/pos/java-inherited-type1/J.java create mode 100644 tests/pos/java-inherited-type1/S.scala create mode 100644 tests/pos/java-inherited-type1/Test.scala create mode 100644 tests/pos/java-inherited-type1/TestMessage.java create mode 100644 tests/run/t6138-2.check create mode 100644 tests/run/t6138-2/JavaClass.java create mode 100644 tests/run/t6138-2/ScalaClass.scala create mode 100644 tests/run/t6138/JavaClass.java create mode 100644 tests/run/t6138/ScalaClass.scala create mode 100644 tests/run/t6238.check diff --git a/compiler/src/dotty/tools/dotc/core/ContextOps.scala b/compiler/src/dotty/tools/dotc/core/ContextOps.scala index 34956d9294c9..140ebfedff7a 100644 --- a/compiler/src/dotty/tools/dotc/core/ContextOps.scala +++ b/compiler/src/dotty/tools/dotc/core/ContextOps.scala @@ -2,8 +2,8 @@ package dotty.tools.dotc package core import Contexts._, Symbols._, Types._, Flags._, Scopes._, Decorators._, NameOps._ -import Denotations._ -import SymDenotations.LazyType, Names.Name, StdNames.nme +import Denotations._, SymDenotations._ +import Names.Name, StdNames.nme import ast.untpd /** Extension methods for contexts where we want to keep the ctx. syntax */ @@ -34,7 +34,8 @@ object ContextOps: if (elem.name == name) return elem.sym.denot // return self } val pre = ctx.owner.thisType - pre.findMember(name, pre, required, excluded) + if ctx.isJava then javaFindMember(name, pre, 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. ctx.owner.findMember(name, ctx.owner.thisType, required, excluded) @@ -42,6 +43,42 @@ 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 = + assert(ctx.isJava) + inContext(ctx) { + + val preSym = pre.typeSymbol + + // 1. Try to search in current type and parents + 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 + preSym.companionClass.thisType.findMember(name, pre, required, excluded) + else NoDenotation + + // 3. Try to search in companion objects of super classes. + // In Java code, static inner classes, which we model as members of the companion object, + // can be referenced from an ident in a subclass or by a selection prefixed by the subclass. + def searchSuperCompanionObjects = + val toSearch = if preSym.is(Flags.Module) then + if preSym.companionClass.exists then + preSym.companionClass.asClass.baseClasses + else Nil + else + preSym.asClass.baseClasses + + toSearch.iterator.map { bc => + val pre1 = bc.thisType.typeSymbol.companionClass.thisType + pre1.findMember(name, pre1, required, excluded) + }.find(_.exists).getOrElse(NoDenotation) + + if preSym.isClass then + directSearch orElse searchCompanionClass orElse searchSuperCompanionObjects + else + directSearch + } + /** A fresh local context with given tree and owner. * Owner might not exist (can happen for self valdefs), in which case * no owner is set in result context diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 44236c168b66..5a62d7882ddf 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -901,16 +901,7 @@ object JavaParsers { members) ++= decls } } - def forwarders(sdef: Tree): List[Tree] = sdef match { - case TypeDef(name, _) if (parentToken == INTERFACE) => - var rhs: Tree = Select(Ident(parentName.toTermName), name) - List(TypeDef(name, rhs).withMods(Modifiers(Flags.Protected))) - case _ => - List() - } - val sdefs = statics.toList - val idefs = members.toList ::: (sdefs flatMap forwarders) - (sdefs, idefs) + (statics.toList, members.toList) } def annotationParents: List[Select] = List( scalaAnnotationDot(tpnme.Annotation), diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index ea702e47e673..5692919c54fb 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -4,7 +4,7 @@ package typer import core._ import ast._ -import Contexts._, Constants._, Types._, Symbols._, Names._, Flags._, Decorators._ +import Contexts._, ContextOps._, Constants._, Types._, Symbols._, Names._, Flags._, Decorators._ import ErrorReporting._, Annotations._, Denotations._, SymDenotations._, StdNames._ import util.Spans._ import util.SrcPos @@ -145,7 +145,12 @@ trait TypeAssigner { // this is exactly what Erasure will do. case _ => val pre = maybeSkolemizePrefix(qualType, name) - val mbr = qualType.findMember(name, pre) + val mbr = + if ctx.isJava && pre.typeSymbol.isClass then + ctx.javaFindMember(name, pre) + else + qualType.findMember(name, pre) + if reallyExists(mbr) then qualType.select(name, mbr) else if qualType.isErroneous || name.toTermName == nme.ERROR then UnspecifiedErrorType else NoType diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 902f8506c870..4dc4ef37178c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -367,7 +367,9 @@ class Typer extends Namer if (qualifies(defDenot)) { val found = if (isSelfDenot(defDenot)) curOwner.enclosingClass.thisType - else { + else if(ctx.isJava && defDenot.symbol.isStatic) { + defDenot.symbol.namedType + } else { val effectiveOwner = if (curOwner.isTerm && defDenot.symbol.maybeOwner.isType) // Don't mix NoPrefix and thisType prefixes, since type comparer diff --git a/compiler/test/dotc/pos-test-pickling.blacklist b/compiler/test/dotc/pos-test-pickling.blacklist index 0306d29f60d3..e1c454f8972b 100644 --- a/compiler/test/dotc/pos-test-pickling.blacklist +++ b/compiler/test/dotc/pos-test-pickling.blacklist @@ -66,3 +66,4 @@ i2797a # GADT cast applied to singleton type difference i4176-gadt.scala +java-inherited-type1 \ No newline at end of file diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index 41821a5d4d70..873c34a19c42 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -35,3 +35,5 @@ varargs-abstract zero-arity-case-class.scala i12194.scala i12753 +t6138 +t6138-2 \ No newline at end of file diff --git a/tests/pos/java-inherited-type/Client.scala b/tests/pos/java-inherited-type/Client.scala new file mode 100644 index 000000000000..a644363cdd4c --- /dev/null +++ b/tests/pos/java-inherited-type/Client.scala @@ -0,0 +1,19 @@ +object Client { + def test= { + Test.Outer.Nested.sig + Test.Outer.Nested.sig1 + Test.Outer.Nested.sig2 + val o = new Test.Outer + new o.Nested1().sig + new o.Nested1().sig1 + new o.Nested1().sig2 + } + + def test1 = { + val t = new Test + val o = new t.Outer1 + new o.Nested1().sig + new o.Nested1().sig1 + new o.Nested1().sig2 + } +} diff --git a/tests/pos/java-inherited-type/Test.java b/tests/pos/java-inherited-type/Test.java new file mode 100644 index 000000000000..ae89a6559a67 --- /dev/null +++ b/tests/pos/java-inherited-type/Test.java @@ -0,0 +1,30 @@ +public class Test { + static class OuterBase implements OuterBaseInterface { + static class StaticInner {} + class Inner {} + } + interface OuterBaseInterface { + interface InnerFromInterface {} + } + public static class Outer extends OuterBase { + public static class Nested { + public static P sig; // was: "type StaticInner", "not found: type Inner", "not found: type InnerFromInterface" + public static P sig1; // was: "type StaticInner is not a member of Test.Outer" + public static P sig2; + + } + public class Nested1 { + public P sig; // was: "not found: type StaticInner" + public P sig1; // was: "type StaticInner is not a member of Test.Outer" + public P sig2; + } + } + public class Outer1 extends OuterBase { + public class Nested1 { + public P sig; // was: "not found: type StaticInner" + public P sig1; // was: "type StaticInner is not a member of Test.Outer" + public P sig2; + } + } + public static class P{} +} diff --git a/tests/pos/java-inherited-type1/J.java b/tests/pos/java-inherited-type1/J.java new file mode 100644 index 000000000000..ba9963104699 --- /dev/null +++ b/tests/pos/java-inherited-type1/J.java @@ -0,0 +1,9 @@ +class J extends S { + // These references all work in Javac because `object O { class I }` erases to `O$I` + + void select1(S1.Inner1 i) { new S1.Inner1(); } + void ident(Inner i) {} + + void ident1(Inner1 i) {} + void select(S.Inner i) { new S.Inner(); } +} diff --git a/tests/pos/java-inherited-type1/S.scala b/tests/pos/java-inherited-type1/S.scala new file mode 100644 index 000000000000..155efc0e06ba --- /dev/null +++ b/tests/pos/java-inherited-type1/S.scala @@ -0,0 +1,9 @@ +class S extends S1 +object S { + class Inner +} + +class S1 +object S1 { + class Inner1 +} diff --git a/tests/pos/java-inherited-type1/Test.scala b/tests/pos/java-inherited-type1/Test.scala new file mode 100644 index 000000000000..082167342a02 --- /dev/null +++ b/tests/pos/java-inherited-type1/Test.scala @@ -0,0 +1,13 @@ +object Test { + val j = new J + // force completion of these signatures + j.ident(null); + j.ident1(null); + j.select(null); + j.select1(null); + + val message:TestMessage = null + val builder:TestMessage.Builder = message.toBuilder + builder.setName("name") + +} diff --git a/tests/pos/java-inherited-type1/TestMessage.java b/tests/pos/java-inherited-type1/TestMessage.java new file mode 100644 index 000000000000..fac373e302f7 --- /dev/null +++ b/tests/pos/java-inherited-type1/TestMessage.java @@ -0,0 +1,17 @@ +abstract class AbstractMessage { + public static abstract class Builder> { + } +} + +class TestMessage extends AbstractMessage { + + public Builder toBuilder() { + return null; + } + + public static class Builder extends AbstractMessage.Builder { + public Builder setName(String name) { + return this; + } + } +} diff --git a/tests/run/t6138-2.check b/tests/run/t6138-2.check new file mode 100644 index 000000000000..473ecde25dba --- /dev/null +++ b/tests/run/t6138-2.check @@ -0,0 +1 @@ +Foo$Bar was instantiated! diff --git a/tests/run/t6138-2/JavaClass.java b/tests/run/t6138-2/JavaClass.java new file mode 100644 index 000000000000..9774c05a0d91 --- /dev/null +++ b/tests/run/t6138-2/JavaClass.java @@ -0,0 +1,4 @@ +public class JavaClass { + // This is defined in ScalaClass + public static final Foo.Bar bar = new Foo.Bar(); +} \ No newline at end of file diff --git a/tests/run/t6138-2/ScalaClass.scala b/tests/run/t6138-2/ScalaClass.scala new file mode 100644 index 000000000000..0528133cbf2c --- /dev/null +++ b/tests/run/t6138-2/ScalaClass.scala @@ -0,0 +1,18 @@ +/* Similar to t10490 -- but defines `Foo` in the object. + * Placing this test within t10490 makes it work without a fix, that's why it's independent. + * Note that this was already working, we add it to make sure we don't regress + */ + +class Foo +object Foo { + class Bar { + override def toString: String = "Foo$Bar was instantiated!" + } +} + +object Test { + def main(args: Array[String]): Unit = { + // JavaClass is the user of the Scala defined classes + println(JavaClass.bar) + } +} \ No newline at end of file diff --git a/tests/run/t6138/JavaClass.java b/tests/run/t6138/JavaClass.java new file mode 100644 index 000000000000..08b9e0bd55d4 --- /dev/null +++ b/tests/run/t6138/JavaClass.java @@ -0,0 +1,4 @@ +public class JavaClass { + // This is defined in ScalaClass + public static final Foo.Bar bar = (new Foo()).new Bar(); +} \ No newline at end of file diff --git a/tests/run/t6138/ScalaClass.scala b/tests/run/t6138/ScalaClass.scala new file mode 100644 index 000000000000..da3c682b5033 --- /dev/null +++ b/tests/run/t6138/ScalaClass.scala @@ -0,0 +1,13 @@ +class Foo { + class Bar { + override def toString: String = "Foo$Bar was instantiated!" + } +} + +object Test { + def main(args: Array[String]): Unit = { + // JavaClass is the user of the Scala defined classes + println(JavaClass.bar) + //println(JavaClass.baz) + } +} \ No newline at end of file diff --git a/tests/run/t6238.check b/tests/run/t6238.check new file mode 100644 index 000000000000..473ecde25dba --- /dev/null +++ b/tests/run/t6238.check @@ -0,0 +1 @@ +Foo$Bar was instantiated! From e75e9edc769e60ff28abef770371d7d6084d1950 Mon Sep 17 00:00:00 2001 From: changvvb Date: Thu, 24 Jun 2021 13:22:40 +0800 Subject: [PATCH 2/5] Add space after if --- compiler/src/dotty/tools/dotc/typer/Typer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 4dc4ef37178c..989f65215377 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -367,7 +367,7 @@ class Typer extends Namer if (qualifies(defDenot)) { val found = if (isSelfDenot(defDenot)) curOwner.enclosingClass.thisType - else if(ctx.isJava && defDenot.symbol.isStatic) { + else if (ctx.isJava && defDenot.symbol.isStatic) { defDenot.symbol.namedType } else { val effectiveOwner = From 0f895a149226c93a033efc462f8fcaa2c631086a Mon Sep 17 00:00:00 2001 From: changvvb Date: Thu, 24 Jun 2021 21:29:35 +0800 Subject: [PATCH 3/5] Use companionModule where search modules --- compiler/src/dotty/tools/dotc/core/ContextOps.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/ContextOps.scala b/compiler/src/dotty/tools/dotc/core/ContextOps.scala index 140ebfedff7a..594f103b8481 100644 --- a/compiler/src/dotty/tools/dotc/core/ContextOps.scala +++ b/compiler/src/dotty/tools/dotc/core/ContextOps.scala @@ -49,10 +49,10 @@ object ContextOps: val preSym = pre.typeSymbol - // 1. Try to search in current type and parents + // 1. Try to search in current type and parents. val directSearch = pre.findMember(name, pre, required, excluded) - // 2. Try to search in companion class if current is an object + // 2. Try to search in companion class if current is an object. def searchCompanionClass = if preSym.is(Flags.Module) then preSym.companionClass.thisType.findMember(name, pre, required, excluded) else NoDenotation @@ -69,7 +69,7 @@ object ContextOps: preSym.asClass.baseClasses toSearch.iterator.map { bc => - val pre1 = bc.thisType.typeSymbol.companionClass.thisType + val pre1 = bc.companionModule.namedType pre1.findMember(name, pre1, required, excluded) }.find(_.exists).getOrElse(NoDenotation) From dcccaaa352229109d1a691818977ac1d0e7c9f4d Mon Sep 17 00:00:00 2001 From: changvvb Date: Thu, 24 Jun 2021 22:08:18 +0800 Subject: [PATCH 4/5] Remove import companion module in java class --- .../tools/dotc/parsing/JavaParsers.scala | 31 ++----------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index 5a62d7882ddf..8322c75e5e2d 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -751,35 +751,8 @@ object JavaParsers { makeTemplate(List(), statics, List(), false)).withMods((cdef.mods & Flags.RetainedModuleClassFlags).toTermFlags) } - def importCompanionObject(cdef: TypeDef): Tree = - Import( - Ident(cdef.name.toTermName).withSpan(NoSpan), - ImportSelector(Ident(nme.WILDCARD)) :: Nil) - - // Importing the companion object members cannot be done uncritically: see - // ticket #2377 wherein a class contains two static inner classes, each of which - // has a static inner class called "Builder" - this results in an ambiguity error - // when each performs the import in the enclosing class's scope. - // - // To address this I moved the import Companion._ inside the class, as the first - // statement. This should work without compromising the enclosing scope, but may (?) - // end up suffering from the same issues it does in scala - specifically that this - // leaves auxiliary constructors unable to access members of the companion object - // as unqualified identifiers. - def addCompanionObject(statics: List[Tree], cdef: TypeDef): List[Tree] = { - // if there are no statics we can use the original cdef, but we always - // create the companion so import A._ is not an error (see ticket #1700) - val cdefNew = - if (statics.isEmpty) cdef - else { - val template = cdef.rhs.asInstanceOf[Template] - cpy.TypeDef(cdef)(cdef.name, - cpy.Template(template)(body = importCompanionObject(cdef) :: template.body)) - .withMods(cdef.mods) - } - - List(makeCompanionObject(cdefNew, statics), cdefNew) - } + def addCompanionObject(statics: List[Tree], cdef: TypeDef): List[Tree] = + List(makeCompanionObject(cdef, statics), cdef) def importDecl(): List[Tree] = { val start = in.offset From 7ff48ef4e660e4f52fd32f5411883f89aad45dba Mon Sep 17 00:00:00 2001 From: changvvb Date: Thu, 24 Jun 2021 22:36:04 +0800 Subject: [PATCH 5/5] Simplify selectionType --- compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala index 5692919c54fb..dfba38d4efa5 100644 --- a/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -146,7 +146,7 @@ trait TypeAssigner { case _ => val pre = maybeSkolemizePrefix(qualType, name) val mbr = - if ctx.isJava && pre.typeSymbol.isClass then + if ctx.isJava then ctx.javaFindMember(name, pre) else qualType.findMember(name, pre)