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

Rework unification of Object and Any in Java/Scala interop #7966

Merged
merged 1 commit into from
May 8, 2019
Merged
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
23 changes: 12 additions & 11 deletions src/compiler/scala/tools/nsc/symtab/classfile/ClassfileParser.scala
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
case variance @ ('+' | '-' | '*') =>
index += 1
val bounds = variance match {
case '+' => TypeBounds.upper(objToAny(sig2type(tparams, skiptvs)))
case '+' => TypeBounds.upper(sig2type(tparams, skiptvs))
case '-' =>
val tp = sig2type(tparams, skiptvs)
// sig2type seems to return AnyClass regardless of the situation:
Expand Down Expand Up @@ -705,7 +705,8 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {

val classSym = classNameToSymbol(subName(c => c == ';' || c == '<'))
assert(!classSym.isOverloaded, classSym.alternatives)
var tpe = processClassType(processInner(classSym.tpe_*))
val classTpe = if (classSym eq ObjectClass) ObjectTpeJava else classSym.tpe_*
var tpe = processClassType(processInner(classTpe))
while (sig.charAt(index) == '.') {
accept('.')
val name = newTypeName(subName(c => c == ';' || c == '<' || c == '.'))
Expand All @@ -722,10 +723,8 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
// make unbounded Array[T] where T is a type variable into Array[T with Object]
// (this is necessary because such arrays have a representation which is incompatible
// with arrays of primitive types.
// NOTE that the comparison to Object only works for abstract types bounded by classes that are strict subclasses of Object
// if the bound is exactly Object, it will have been converted to Any, and the comparison will fail
// see also RestrictJavaArraysMap (when compiling java sources directly)
if (elemtp.typeSymbol.isAbstractType && !(elemtp <:< ObjectTpe)) {
if (elemtp.typeSymbol.isAbstractType && elemtp.upperBound =:= ObjectTpe) {
elemtp = intersectionType(List(elemtp, ObjectTpe))
}

Expand All @@ -735,15 +734,15 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
assert(sym ne null, sig)
val paramtypes = new ListBuffer[Type]()
while (sig.charAt(index) != ')') {
paramtypes += objToAny(sig2type(tparams, skiptvs))
paramtypes += sig2type(tparams, skiptvs)
}
index += 1
val restype = if (sym != null && sym.isClassConstructor) {
accept('V')
clazz.tpe_*
} else
sig2type(tparams, skiptvs)
JavaMethodType(sym.newSyntheticValueParams(paramtypes.toList), restype)
MethodType(sym.newSyntheticValueParams(paramtypes.toList), restype)
case 'T' =>
val n = newTypeName(subName(';'.==))
index += 1
Expand All @@ -757,7 +756,7 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
while (sig.charAt(index) == ':') {
index += 1
if (sig.charAt(index) != ':') // guard against empty class bound
ts += objToAny(sig2type(tparams, skiptvs))
ts += sig2type(tparams, skiptvs)
}
TypeBounds.upper(intersectionType(ts.toList, sym))
}
Expand Down Expand Up @@ -793,7 +792,8 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
classTParams = tparams
val parents = new ListBuffer[Type]()
while (index < end) {
parents += sig2type(tparams, skiptvs = false) // here the variance doesn't matter
val parent = sig2type(tparams, skiptvs = false)
parents += (if (parent == ObjectTpeJava) ObjectTpe else parent) // here the variance doesn't matter
}
ClassInfoType(parents.toList, instanceScope, sym)
}
Expand Down Expand Up @@ -1271,8 +1271,9 @@ abstract class ClassfileParser(reader: ReusableInstance[ReusableDataReader]) {
override def complete(sym: symbolTable.Symbol): Unit = {
val info = if (sig != null) sigToType(sym, sig) else {
val superTpe = if (parent == null) definitions.AnyClass.tpe_* else getClassSymbol(parent.value).tpe_*
var ifacesTypes = ifaces.filterNot(_ eq null).map(x => getClassSymbol(x.value).tpe_*)
ClassInfoType(superTpe :: ifacesTypes, instanceScope, clazz)
val superTpe1 = if (superTpe == ObjectTpeJava) ObjectTpe else superTpe
val ifacesTypes = ifaces.filterNot(_ eq null).map(x => getClassSymbol(x.value).tpe_*)
ClassInfoType(superTpe1 :: ifacesTypes, instanceScope, clazz)
}
sym.setInfo(info)
}
Expand Down
6 changes: 4 additions & 2 deletions src/compiler/scala/tools/nsc/typechecker/Infer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ trait Infer extends Checkable {
* Note: pre is not refchecked -- moreover, refchecking the resulting tree may not refcheck pre,
* since pre may not occur in its type (callers should wrap the result in a TypeTreeWithDeferredRefCheck)
*/
def checkAccessible(tree: Tree, sym: Symbol, pre: Type, site: Tree): Tree = {
def checkAccessible(tree: Tree, sym: Symbol, pre: Type, site: Tree, isJava: Boolean): Tree = {
def malformed(ex: MalformedType, instance: Type): Type = {
val what = if (ex.msg contains "malformed type") "is malformed" else s"contains a ${ex.msg}"
val message = s"\n because its instance type $instance $what"
Expand Down Expand Up @@ -302,7 +302,9 @@ trait Infer extends Checkable {
// OPT: avoid lambda allocation and Type.map for super constructor calls
case _: SuperType if !sym.isConstructor && !owntype.isInstanceOf[OverloadedType] =>
owntype map ((tp: Type) => if (tp eq pre) site.symbol.thisType else tp)
case _ => owntype
case _ =>
if ((owntype eq ObjectTpe) && isJava) ObjectTpeJava
else owntype
}
)
}
Expand Down
10 changes: 5 additions & 5 deletions src/compiler/scala/tools/nsc/typechecker/Namers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1273,12 +1273,12 @@ trait Namers extends MethodSynthesis {

// make a java method type if meth.isJavaDefined
private def methodTypeFor(meth: Symbol, vparamSymss: List[List[Symbol]], restpe: Type) = {
def makeJavaMethodType(vparams: List[Symbol], restpe: Type) = {
vparams foreach (p => p setInfo objToAny(p.tpe))
JavaMethodType(vparams, restpe)
def makeMethodType(vparams: List[Symbol], restpe: Type) = {
vparams foreach (p => p setInfo p.tpe)
MethodType(vparams, restpe)
}
if (vparamSymss.isEmpty) NullaryMethodType(restpe)
else if (meth.isJavaDefined) vparamSymss.foldRight(restpe)(makeJavaMethodType)
else if (meth.isJavaDefined) vparamSymss.foldRight(restpe)(makeMethodType)
else vparamSymss.foldRight(restpe)(MethodType(_, _))
}

Expand Down Expand Up @@ -1755,7 +1755,7 @@ trait Namers extends MethodSynthesis {
case TypeBounds(lt, rt) if (lt.isError || rt.isError) =>
TypeBounds.empty
case tp @ TypeBounds(lt, rt) if (tdef.symbol hasFlag JAVA) =>
TypeBounds(lt, objToAny(rt))
TypeBounds(lt, rt)
case tp =>
tp
}
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/scala/tools/nsc/typechecker/Typers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -588,9 +588,9 @@ trait Typers extends Adaptations with Tags with TypersTracking with PatternTyper
case SelectFromTypeTree(_, name) => SelectFromTypeTree(qual, name)
}
}
(checkAccessible(tree1, sym, qual.tpe, qual), qual.tpe)
(checkAccessible(tree1, sym, qual.tpe, qual, unit.isJava), qual.tpe)
} else {
(checkAccessible(tree, sym, pre, site), pre)
(checkAccessible(tree, sym, pre, site, unit.isJava), pre)
}

/** Post-process an identifier or selection node, performing the following:
Expand Down
5 changes: 3 additions & 2 deletions src/reflect/scala/reflect/internal/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ trait Definitions extends api.StandardDefinitions {
private def newMethod(owner: Symbol, name: TermName, formals: List[Type], restpe: Type, flags: Long): MethodSymbol = {
val msym = owner.newMethod(name.encode, NoPosition, flags)
val params = msym.newSyntheticValueParams(formals)
val info = if (owner.isJavaDefined) JavaMethodType(params, restpe) else MethodType(params, restpe)
val info = MethodType(params, restpe)
msym.setInfo(info).markAllCompleted
}
private def enterNewMethod(owner: Symbol, name: TermName, formals: List[Type], restpe: Type, flags: Long = 0L): MethodSymbol =
Expand Down Expand Up @@ -293,6 +293,7 @@ trait Definitions extends api.StandardDefinitions {
lazy val NothingTpe = NothingClass.tpe
lazy val NullTpe = NullClass.tpe
lazy val ObjectTpe = ObjectClass.tpe
lazy val ObjectTpeJava = mkObjectTpeJava
lazy val SerializableTpe = SerializableClass.tpe
lazy val StringTpe = StringClass.tpe
lazy val ThrowableTpe = ThrowableClass.tpe
Expand Down Expand Up @@ -447,7 +448,7 @@ trait Definitions extends api.StandardDefinitions {
// We don't need to deal with JavaRepeatedParamClass here, as `repeatedToSeq` is only called in the patmat translation for Scala sources.
def repeatedToSeq(tp: Type): Type = elementTransform(RepeatedParamClass, tp)(seqType) orElse tp
def seqToRepeated(tp: Type): Type = elementTransform(SeqClass, tp)(scalaRepeatedType) orElse tp
def isReferenceArray(tp: Type) = elementTest(ArrayClass, tp)(_ <:< AnyRefTpe)
def isReferenceArray(tp: Type) = elementTest(ArrayClass, tp)(elemtp => elemtp <:< AnyRefTpe || (elemtp eq ObjectTpeJava))
def isArrayOfSymbol(tp: Type, elem: Symbol) = elementTest(ArrayClass, tp)(_.typeSymbol == elem)
def elementType(container: Symbol, tp: Type): Type = elementExtract(container, tp)

Expand Down
34 changes: 8 additions & 26 deletions src/reflect/scala/reflect/internal/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2709,6 +2709,7 @@ trait Types
private final class ClassNoArgsTypeRef(pre: Type, sym: Symbol) extends NoArgsTypeRef(pre, sym){
override def contains(sym0: Symbol): Boolean = (sym eq sym0) || pre.contains(sym0)
}
private[scala] def mkObjectTpeJava: Type = new ClassNoArgsTypeRef(definitions.ObjectTpe.prefix, definitions.ObjectClass)

object TypeRef extends TypeRefExtractor {
def apply(pre: Type, sym: Symbol, args: List[Type]): Type = unique({
Expand Down Expand Up @@ -2827,7 +2828,6 @@ trait Types
}

def isImplicit = (params ne Nil) && params.head.isImplicit
def isJava = false // can we do something like for implicits? I.e. do Java methods without parameters need to be recognized?

override def paramSectionCount: Int = resultType.paramSectionCount + 1

Expand Down Expand Up @@ -2885,10 +2885,6 @@ trait Types

object MethodType extends MethodTypeExtractor

class JavaMethodType(ps: List[Symbol], rt: Type) extends MethodType(ps, rt) {
override def isJava = true
}

// TODO: rename so it's more appropriate for the type that is for a method without argument lists
// ("nullary" erroneously implies it has an argument list with zero arguments, it actually has zero argument lists)
case class NullaryMethodType(override val resultType: Type) extends Type with NullaryMethodTypeApi {
Expand Down Expand Up @@ -4039,15 +4035,8 @@ trait Types
typeRef(pre, sym, args)
}

/** The canonical creator for implicit method types */
def JavaMethodType(params: List[Symbol], resultType: Type): JavaMethodType =
new JavaMethodType(params, resultType) // don't unique this!

/** Create a new MethodType of the same class as tp, i.e. keep JavaMethodType */
def copyMethodType(tp: Type, params: List[Symbol], restpe: Type): Type = tp match {
case _: JavaMethodType => JavaMethodType(params, restpe)
case _ => MethodType(params, restpe)
}
/** Create a new MethodType */
def copyMethodType(tp: Type, params: List[Symbol], restpe: Type): Type = MethodType(params, restpe)

/** A creator for intersection type where intersections of a single type are
* replaced by the type itself, and repeated parent classes are merged.
Expand Down Expand Up @@ -4752,7 +4741,7 @@ trait Types
case mt2 @ MethodType(params2, res2) =>
// sameLength(params1, params2) was used directly as pre-screening optimization (now done by matchesQuantified -- is that ok, performance-wise?)
mt1.isImplicit == mt2.isImplicit &&
matchingParams(params1, params2, mt1.isJava, mt2.isJava) &&
matchingParams(params1, params2) &&
matchesQuantified(params1, params2, res1, res2)
case NullaryMethodType(res2) =>
if (params1.isEmpty) matchesType(res1, res2, alwaysMatchSimple)
Expand Down Expand Up @@ -4847,7 +4836,7 @@ trait Types
*/

/** Are `syms1` and `syms2` parameter lists with pairwise equivalent types? */
protected[internal] def matchingParams(syms1: List[Symbol], syms2: List[Symbol], syms1isJava: Boolean, syms2isJava: Boolean): Boolean = syms1 match {
protected[internal] def matchingParams(syms1: List[Symbol], syms2: List[Symbol]): Boolean = syms1 match {
case Nil =>
syms2.isEmpty
case sym1 :: rest1 =>
Expand All @@ -4857,10 +4846,7 @@ trait Types
case sym2 :: rest2 =>
val tp1 = sym1.tpe
val tp2 = sym2.tpe
(tp1 =:= tp2 ||
syms1isJava && tp2.typeSymbol == ObjectClass && tp1.typeSymbol == AnyClass ||
syms2isJava && tp1.typeSymbol == ObjectClass && tp2.typeSymbol == AnyClass) &&
matchingParams(rest1, rest2, syms1isJava, syms2isJava)
tp1 =:= tp2 && matchingParams(rest1, rest2)
}
}

Expand Down Expand Up @@ -5130,11 +5116,11 @@ trait Types
}

def isUnboundedGeneric(tp: Type) = tp match {
case t @ TypeRef(_, sym, _) => sym.isAbstractType && !(t <:< AnyRefTpe)
case t @ TypeRef(_, sym, _) => sym.isAbstractType && (!(t <:< AnyRefTpe) || (t.upperBound eq ObjectTpeJava))
case _ => false
}
def isBoundedGeneric(tp: Type) = tp match {
case TypeRef(_, sym, _) if sym.isAbstractType => (tp <:< AnyRefTpe)
case TypeRef(_, sym, _) if sym.isAbstractType => tp <:< AnyRefTpe && !(tp.upperBound eq ObjectTpeJava)
case TypeRef(_, sym, _) => !isPrimitiveValueClass(sym)
case _ => false
}
Expand All @@ -5159,10 +5145,6 @@ trait Types
*/
def importableMembers(pre: Type): Scope = pre.members filter isImportable

def objToAny(tp: Type): Type =
if (!phase.erasedTypes && tp.typeSymbol == ObjectClass) AnyTpe
else tp

def invalidateTreeTpeCaches(tree: Tree, updatedSyms: List[Symbol]) = if (!updatedSyms.isEmpty)
for (t <- tree if t.tpe != null)
for (tp <- t.tpe) {
Expand Down
9 changes: 5 additions & 4 deletions src/reflect/scala/reflect/internal/tpe/TypeComparers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,9 @@ trait TypeComparers {
&& (tp1.normalize =:= tp2.normalize)
)
private def isSameTypeRef(tr1: TypeRef, tr2: TypeRef) = (
equalSymsAndPrefixes(tr1.sym, tr1.pre, tr2.sym, tr2.pre)
&& (isSameHKTypes(tr1, tr2) || isSameTypes(tr1.args, tr2.args))
if ((((tr1 eq ObjectTpeJava) && (tr2.sym eq AnyClass)) || (tr2 eq ObjectTpeJava) && (tr1.sym eq AnyClass)))
true
else equalSymsAndPrefixes(tr1.sym, tr1.pre, tr2.sym, tr2.pre) && (isSameHKTypes(tr1, tr2) || isSameTypes(tr1.args, tr2.args))
)

private def isSameSingletonType(tp1: SingletonType, tp2: SingletonType): Boolean = {
Expand Down Expand Up @@ -459,7 +460,7 @@ trait TypeComparers {
// These typerefs are pattern matched up and down far more
// than is necessary.
val sym1 = tr1.sym
val sym2 = tr2.sym
val sym2 = if (!phase.erasedTypes && (tr2 eq ObjectTpeJava)) AnyClass else tr2.sym
val pre1 = tr1.pre
val pre2 = tr2.pre
(((if (sym1 eq sym2) phase.erasedTypes || sym1.owner.hasPackageFlag || isSubType(pre1, pre2, depth)
Expand Down Expand Up @@ -557,7 +558,7 @@ trait TypeComparers {
val res2 = mt2.resultType
(sameLength(params1, params2) &&
mt1.isImplicit == mt2.isImplicit &&
matchingParams(params1, params2, mt1.isJava, mt2.isJava) &&
matchingParams(params1, params2) &&
isSubType(res1.substSym(params1, params2), res2, depth))
// TODO: if mt1.params.isEmpty, consider NullaryMethodType?
case _ =>
Expand Down
2 changes: 1 addition & 1 deletion src/reflect/scala/reflect/internal/transform/Erasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ trait Erasure {
* e.g. with "tagged types" like Array[Int] with T.
*/
def unboundedGenericArrayLevel(tp: Type): Int = tp match {
case GenericArray(level, core) if !(core <:< AnyRefTpe) => level
case GenericArray(level, core) if !(core <:< AnyRefTpe || core.upperBound == ObjectTpeJava) => level
case RefinedType(ps, _) if ps.nonEmpty => logResult(s"Unbounded generic level for $tp is")(unboundedGenericArrayLevel(intersectionDominator(ps)))
case _ => 0
}
Expand Down
2 changes: 1 addition & 1 deletion src/reflect/scala/reflect/internal/transform/UnCurry.scala
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ trait UnCurry {
case TypeRef(pre, RepeatedParamClass, arg :: Nil) =>
Some(seqType(arg))
case TypeRef(pre, JavaRepeatedParamClass, arg :: Nil) =>
Some(arrayType(if (isUnboundedGeneric(arg)) ObjectTpe else arg))
Some(arrayType(if (isUnboundedGeneric(arg)) ObjectTpeJava else arg))
case _ =>
None
}
Expand Down
17 changes: 12 additions & 5 deletions src/reflect/scala/reflect/runtime/JavaMirrors.scala
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive
private class TypeParamCompleter(jtvar: jTypeVariable[_ <: GenericDeclaration]) extends LazyType with FlagAgnosticCompleter {
override def load(sym: Symbol) = complete(sym)
override def complete(sym: Symbol) = {
sym setInfo TypeBounds.upper(glb(jtvar.getBounds.toList map typeToScala map objToAny))
sym setInfo TypeBounds.upper(glb(jtvar.getBounds.toList map typeToScala))
markAllCompleted(sym)
}
}
Expand Down Expand Up @@ -1084,7 +1084,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive
val tparam = owner.newExistential(newTypeName("T$" + tparams.length))
.setInfo(TypeBounds(
lub(jwild.getLowerBounds.toList map typeToScala),
glb(jwild.getUpperBounds.toList map typeToScala map objToAny)))
glb(jwild.getUpperBounds.toList map typeToScala)))
tparams += tparam
typeRef(NoPrefix, tparam, List())
case _ =>
Expand All @@ -1102,7 +1102,10 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive
arrayType(typeToScala(jclazz.getComponentType))
else {
val clazz = classToScala(jclazz)
rawToExistential(typeRef(clazz.owner.thisType, clazz, List()))
rawToExistential(typeRef(clazz.owner.thisType, clazz, List())) match {
case ObjectTpe => ObjectTpeJava
case tp => tp
}
}
case japplied: ParameterizedType =>
// http://stackoverflow.com/questions/5767122/parameterizedtype-getrawtype-returns-j-l-r-type-not-class
Expand All @@ -1112,7 +1115,11 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive
val (args, bounds) = targsToScala(pre.typeSymbol, args0.toList)
newExistentialType(bounds, typeRef(pre, sym, args))
case jarr: GenericArrayType =>
arrayType(typeToScala(jarr.getGenericComponentType))
var elemtp = typeToScala(jarr.getGenericComponentType)
if (elemtp.typeSymbol.isAbstractType && elemtp.upperBound =:= ObjectTpe) {
elemtp = intersectionType(List(elemtp, ObjectTpe))
}
arrayType(elemtp)
case jtvar: jTypeVariable[_] =>
val tparam = typeParamToScala(jtvar)
typeRef(NoPrefix, tparam, List())
Expand Down Expand Up @@ -1219,7 +1226,7 @@ private[scala] trait JavaMirrors extends internal.SymbolTable with api.JavaUnive
if (param.isNamePresent) TermName(param.getName)
else nme.syntheticParamName(ix + 1)
meth.owner.newValueParameter(name, meth.pos)
.setInfo(objToAny(typeToScala(param.getParameterizedType)))
.setInfo(typeToScala(param.getParameterizedType))
.setFlag(if (param.isNamePresent) 0 else SYNTHETIC)
}
}
Expand Down
1 change: 1 addition & 0 deletions src/reflect/scala/reflect/runtime/JavaUniverseForce.scala
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ trait JavaUniverseForce { self: runtime.JavaUniverse =>
definitions.NothingTpe
definitions.NullTpe
definitions.ObjectTpe
definitions.ObjectTpeJava
definitions.SerializableTpe
definitions.StringTpe
definitions.ThrowableTpe
Expand Down
Loading