Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Root of Java select must be class or rooted package #21800

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ trait MessageRendering {
}

val syntax =
if (ctx.settings.color.value != "never")
if (ctx.settings.color.value != "never" && !ctx.isJava)
SyntaxHighlighting.highlight(new String(pos.linesSlice)).toCharArray
else pos.linesSlice
val lines = linesFrom(syntax)
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ trait TypeAssigner {

/** The type of the selection `tree`, where `qual1` is the typed qualifier part. */
def selectionType(tree: untpd.RefTree, qual1: Tree)(using Context): Type =
val qualType0 = qual1.tpe.widenIfUnstable
val qualType =
val qualType0 = qual1.tpe.widenIfUnstable
if !qualType0.hasSimpleKind && tree.name != nme.CONSTRUCTOR then
// constructors are selected on type constructor, type arguments are passed afterwards
errorType(em"$qualType0 takes type parameters", qual1.srcPos)
Expand Down Expand Up @@ -199,7 +199,7 @@ trait TypeAssigner {

/** Type assignment method. Each method takes as parameters
* - an untpd.Tree to which it assigns a type,
* - typed child trees it needs to access to cpmpute that type,
* - typed child trees it needs to access to compute that type,
* - any further information it needs to access to compute that type.
*/
def assignType(tree: untpd.Ident, tp: Type)(using Context): Ident =
Expand Down
51 changes: 36 additions & 15 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -986,20 +986,44 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
record("typedSelect")

def typeSelectOnTerm(using Context): Tree =
val qual = typedExpr(tree.qualifier, shallowSelectionProto(tree.name, pt, this, tree.nameSpan))
if ctx.isJava then
javaSelection(qual)
// permitted selection depends on Java context (type or expression).
// we don't propagate (as a mode) whether a.b.m is a type name; OK since we only see type contexts.
// to allow correct selections, approximate by fallback for x.y: take x as class or (rooted) package.
def tryQualFallback(qual: untpd.Ident, name: Name)(using Context): Tree =
val qualTpe =
findRef(name.toTypeName, WildcardType, EmptyFlags, EmptyFlags, qual.srcPos) match
case tpe: NamedType if tpe.symbol.isClass => tpe
case _ =>
val maybePackage = defn.RootPackage.info.member(name)
if maybePackage.exists then maybePackage.info else NoType
if qualTpe.exists then
javaSelection(assignType(cpy.Ident(qual)(name), qualTpe))
else
errorTree(tree, em"no class or package to resolve `$name`") // just fail fallback
def tryQual(qual: untpd.Tree)(using Context): Tree =
javaSelection(typedExpr(qual, shallowSelectionProto(tree.name, pt, this, tree.nameSpan)))
tree.qualifier match
case qual @ Ident(name) => tryAlternatively(tryQual(qual))(tryQualFallback(qual, name))
case qual => tryQual(qual)
else
val qual = typedExpr(tree.qualifier, shallowSelectionProto(tree.name, pt, this, tree.nameSpan))
typedSelectWithAdapt(tree, pt, qual).withSpan(tree.span).computeNullable()

def javaSelection(qual: Tree)(using Context) =
val tree1 = assignType(cpy.Select(tree)(qual, tree.name), qual)
tree1.tpe match
case moduleRef: TypeRef if moduleRef.symbol.is(ModuleClass, butNot = JavaDefined) =>
// handle unmangling of module names (Foo$ -> Foo[ModuleClass])
cpy.Select(tree)(qual, tree.name.unmangleClassName).withType(moduleRef)
case _ =>
tree1
qual match
case id @ Ident(name) if id.symbol.is(Package) && !id.symbol.owner.isRoot =>
val rooted = defn.RootPackage.info.member(name)
val qual1 = if rooted.exists then assignType(cpy.Ident(id)(name), rooted.info) else qual
assignType(cpy.Select(tree)(qual1, tree.name), qual1)
case _ =>
val tree1 = assignType(cpy.Select(tree)(qual, tree.name), qual)
tree1.tpe match
case moduleRef: TypeRef if moduleRef.symbol.is(ModuleClass, butNot = JavaDefined) =>
// handle unmangling of module names (Foo$ -> Foo[ModuleClass])
cpy.Select(tree)(qual, tree.name.unmangleClassName).withType(moduleRef)
case _ =>
tree1

def tryJavaSelectOnType(using Context): Tree = tree.qualifier match {
case sel @ Select(qual, name) =>
Expand All @@ -1016,17 +1040,14 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
errorTree(tree, em"cannot convert to type selection") // will never be printed due to fallback
}

def selectWithFallback(fallBack: Context ?=> Tree) =
tryAlternatively(typeSelectOnTerm)(fallBack)

if (tree.qualifier.isType) {
val qual1 = typedType(tree.qualifier, shallowSelectionProto(tree.name, pt, this, tree.nameSpan))
assignType(cpy.Select(tree)(qual1, tree.name), qual1)
}
else if (ctx.isJava && tree.name.isTypeName)
// SI-3120 Java uses the same syntax, A.B, to express selection from the
// value A and from the type A. We have to try both.
selectWithFallback(tryJavaSelectOnType) // !!! possibly exponential bcs of qualifier retyping
// scala/bug#3120 Java uses the same syntax, A.B, to express selection from the
// value A and from the type A. We have to try both. (possibly exponential bc of qualifier retyping)
tryAlternatively(typeSelectOnTerm)(tryJavaSelectOnType)
else
typeSelectOnTerm
}
Expand Down
6 changes: 6 additions & 0 deletions tests/pos/t10350/Bar.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@

package bar

object Bar {
def xxx(s: String): foo.Foo = foo.Foo.create(s)
}
5 changes: 5 additions & 0 deletions tests/pos/t10350/Baz.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@

package foo.java;

interface Baz {
}
14 changes: 14 additions & 0 deletions tests/pos/t10350/Foo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

package foo;

public interface Foo {
static Foo create(java.lang.String v) {
return null;
}
}

/*
5 | static Foo create(java.lang.String v) {
| ^^^^^^^^^
| value lang is not a member of foo.java
*/
5 changes: 5 additions & 0 deletions tests/pos/t11788/Bar.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package p

object Bar extends App {
println(new Foo().test())
}
11 changes: 11 additions & 0 deletions tests/pos/t11788/Foo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package p;

public class Foo {
private String java;

// java is the rooted package, not the field
public java.lang.Integer test() {
//return Integer.valueOf(42);
throw null;
}
}
5 changes: 5 additions & 0 deletions tests/pos/t11788b/Bar.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package p

object Bar extends App {
println(new Foo().test())
}
10 changes: 10 additions & 0 deletions tests/pos/t11788b/Foo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package p;

public class Foo {
private String java;

public java.lang.Integer test() {
//return Integer.valueOf(42);
throw null;
}
}
8 changes: 8 additions & 0 deletions tests/pos/t11788b/java.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package p;

public class java {
public static class lang {
public static class Integer {
}
}
}
5 changes: 5 additions & 0 deletions tests/pos/t11788c/Bar.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package p

object Bar extends App {
println(new Foo().test())
}
10 changes: 10 additions & 0 deletions tests/pos/t11788c/Foo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package p;

public class Foo {
private String java;

// java is class in scope, not the term member or package
public java.lang.Integer.Inner test() {
throw null;
}
}
10 changes: 10 additions & 0 deletions tests/pos/t11788c/java.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package p;

public class java {
public static class lang {
public static class Integer {
public static class Inner {
}
}
}
}
Loading