Skip to content

Commit

Permalink
Find overloads by converting types to ErasedTypeRef.
Browse files Browse the repository at this point in the history
Inner classes can not be found with Mirror getRequiredClass, so
we should have another way to compare types to signatures, this
proposes to instead convert method signatures to
Signature[ErasedTypeRef]
  • Loading branch information
bishabosha committed Jul 7, 2020
1 parent 94f693f commit f8c2212
Show file tree
Hide file tree
Showing 13 changed files with 190 additions and 68 deletions.
18 changes: 4 additions & 14 deletions src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import scala.reflect.io.AbstractFile

import collection.mutable

import scala.tools.tasty.{TastyName, TastyFlags}, TastyFlags._
import scala.tools.tasty.{TastyName, TastyFlags}, TastyFlags._, TastyName.ObjectName
import scala.tools.nsc.tasty.{TastyUniverse, TastyModes, SafeEq}, TastyModes._
import scala.reflect.internal.MissingRequirementError

Expand All @@ -29,7 +29,7 @@ trait ContextOps { self: TastyUniverse =>

private def describeOwner(owner: Symbol): String = {
val kind =
if (owner.is(Param)) {
if (owner.isOneOf(Param | ParamSetter)) {
if (owner.isType) "type parameter"
else "parameter"
}
Expand Down Expand Up @@ -103,8 +103,7 @@ trait ContextOps { self: TastyUniverse =>
* It also provides all operations for manipulation of the symbol table, such as creating/updating symbols and
* updating their types.
*/
sealed abstract class Context {
thisCtx =>
sealed abstract class Context { thisCtx =>

protected implicit final def implyThisCtx: thisCtx.type = thisCtx

Expand Down Expand Up @@ -151,15 +150,6 @@ trait ContextOps { self: TastyUniverse =>
symOrDependencyError(false, true, fullname)(loadingMirror.getPackage(encodeTermName(fullname).toString))
}

final def requiredClass(fullname: TastyName.TypeName): Symbol =
symOrDependencyError(false, false, fullname)(loadingMirror.getRequiredClass(encodeTypeName(fullname).toString))

final def optionalClass(fullname: TastyName.TypeName): Option[Symbol] =
loadingMirror.getClassIfDefined(encodeTypeName(fullname).toString).toOption

final def requiredObject(fullname: TastyName.ObjectName): Symbol =
symOrDependencyError(true, false, fullname)(loadingMirror.getRequiredModule(encodeTermName(fullname).toString))

private def symOrDependencyError(isObject: Boolean, isPackage: Boolean, fullname: TastyName)(sym: => Symbol): Symbol = {
try sym
catch {
Expand All @@ -180,7 +170,7 @@ trait ContextOps { self: TastyUniverse =>
owner.newTypeParameter(u.nme.WILDCARD.toTypeName, u.NoPosition, u.NoFlags).setInfo(info)

final def findRootSymbol(roots: Set[Symbol], name: TastyName): Option[Symbol] = {
import TastyName._
import TastyName.TypeName

def isSameRoot(root: Symbol, selector: u.Name): Boolean =
(root.owner `eq` this.owner) && selector === root.name
Expand Down
63 changes: 41 additions & 22 deletions src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import scala.tools.nsc.tasty.SafeEq

import scala.tools.nsc.tasty.{TastyUniverse, TastyModes}, TastyModes._
import scala.tools.tasty.{TastyName, Signature, TastyFlags}, TastyName.SignedName, Signature.MethodSignature, TastyFlags.TastyFlagSet
import scala.tools.tasty.ErasedTypeRef

/**This layer deals with selecting a member symbol from a type using a [[TastyName]],
* also contains factories for making type references to symbols.
Expand Down Expand Up @@ -73,7 +74,7 @@ trait SymbolOps { self: TastyUniverse =>
termParamss

def namedMemberOfType(space: Type, tname: TastyName)(implicit ctx: Context): Symbol = tname match {
case SignedName(qual, sig) => signedMemberOfSpace(space, qual, sig.map(resolveErasedTypeRef))
case SignedName(qual, sig) => signedMemberOfSpace(space, qual, sig.map(_.encode))
case _ => memberOfSpace(space, tname)
}

Expand All @@ -94,40 +95,57 @@ trait SymbolOps { self: TastyUniverse =>
}
else space.member(encodeTermName(tname))
}
if (isSymbol(member)) member
else {
val kind = if (tname.isTypeName) "type" else "term"
def addendum(name: String) =
if (ctx.mode.is(ReadParents)) s"$kind in parents of ${if (ctx.owner.isLocalDummy) ctx.owner.owner else ctx.owner}: $name"
else if (ctx.owner.isClass) s"$kind required by a member of ${ctx.owner}: $name"
else s"$kind $name while unpickling ${ctx.owner}"
val msg =
if (tname.isTypeName && space.typeSymbol.hasPackageFlag)
s"can't find ${addendum(s"${space.typeSymbol.fullNameString}.$tname")}; perhaps it is missing from the classpath."
else
s"can't find ${addendum("" + tname)}, in $space"
typeError(msg)
if (isSymbol(member) && hasType(member)) member
else errorMissing(space, tname)
}

private def hasType(member: Symbol)(implicit ctx: Context) = {
ctx.mode.is(ReadAnnotation) || (member.rawInfo `ne` u.NoType)
}

private def errorMissing[T](space: Type, tname: TastyName)(implicit ctx: Context) = {
val kind = if (tname.isTypeName) "type" else "term"
def typeToString(tpe: Type) = {
def inner(sb: StringBuilder, tpe: Type): StringBuilder = tpe match {
case u.SingleType(pre, sym) => inner(sb, pre) append '.' append (
if (sym.isPackageObjectOrClass) s"`${sym.name}`"
else String valueOf sym.name
)
case u.TypeRef(pre, sym, _) if sym.isTerm =>
if ((pre eq u.NoPrefix) || (pre eq u.NoType)) sb append sym.name
else inner(sb, pre) append '.' append sym.name
case tpe => sb append tpe
}
inner(new StringBuilder(), tpe).toString
}
def addendum(name: String) = {
if (ctx.mode.is(ReadParents)) s"$kind in parents of ${location(if (ctx.owner.isLocalDummy) ctx.owner.owner else ctx.owner)}: $name"
else s"$kind required by ${location(ctx.owner)}: $name"
}
val missing = addendum(s"${typeToString(space)}.$tname")
typeError(s"can't find $missing; perhaps it is missing from the classpath.")
}

private def signedMemberOfSpace(space: Type, qual: TastyName, sig: MethodSignature[Type])(implicit ctx: Context): Symbol = {
ctx.log(s"""<<< looking for overload member[$space] @@ $qual: ${sig.show}""")
private def signedMemberOfSpace(space: Type, qual: TastyName, sig: MethodSignature[ErasedTypeRef])(implicit ctx: Context): Symbol = {
ctx.log(s"""<<< looking for overload member[$space] @@ $qual: ${showSig(sig)}""")
val member = space.member(encodeTermName(qual))
val (tyParamCount, argTpes) = {
if (!(isSymbol(member) && hasType(member))) errorMissing(space, qual)
val (tyParamCount, argTpeRefs) = {
val (tyParamCounts, params) = sig.params.partitionMap(identity)
if (tyParamCounts.length > 1) {
unsupportedError(s"multiple type parameter lists on erased method signature ${sig.show}")
unsupportedError(s"multiple type parameter lists on erased method signature ${showSig(sig)}")
}
(tyParamCounts.headOption.getOrElse(0), params)
}
def compareSym(sym: Symbol): Boolean = sym match {
case sym: u.MethodSymbol =>
val params = sym.paramss.flatten
sym.returnType.erasure =:= sig.result &&
params.length === argTpes.length &&
val isJava = sym.isJavaDefined
NameErasure.sigName(sym.returnType, isJava) === sig.result &&
params.length === argTpeRefs.length &&
(qual === TastyName.Constructor && tyParamCount === member.owner.typeParams.length
|| tyParamCount === sym.typeParams.length) &&
params.zip(argTpes).forall { case (param, tpe) => param.tpe.erasure =:= tpe } && {
params.zip(argTpeRefs).forall { case (param, tpe) => NameErasure.sigName(param.tpe, isJava) === tpe } && {
ctx.log(s">>> selected ${showSym(sym)}: ${sym.tpe}")
true
}
Expand All @@ -136,8 +154,9 @@ trait SymbolOps { self: TastyUniverse =>
false
}
member.asTerm.alternatives.find(compareSym).getOrElse(
typeError(s"No matching overload of $space.$qual with signature ${sig.show}"))
typeError(s"No matching overload of $space.$qual with signature ${showSig(sig)}"))
}

def showSig(sig: MethodSignature[ErasedTypeRef]): String = sig.map(_.signature).show
def showSym(sym: Symbol): String = s"Symbol($sym, #${sym.id})"
}
89 changes: 83 additions & 6 deletions src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -175,15 +175,92 @@ trait TypeOps { self: TastyUniverse =>
bounds
}

private[bridge] def resolveErasedTypeRef(ref: ErasedTypeRef)(implicit ctx: Context): Type = {
import TastyName._
/** This is a port from Dotty of transforming a Method type to an ErasedTypeRef
*/
private[bridge] object NameErasure {

def isRepeatedParam(self: Type): Boolean =
self.typeSymbol eq u.definitions.RepeatedParamClass

/** Translate a type of the form From[T] to either To[T] or To[? <: T] (if `wildcardArg` is set). Keep other types as they are.
* `from` and `to` must be static classes, both with one type parameter, and the same variance.
* Do the same for by name types => From[T] and => To[T]
*/
def translateParameterized(self: Type)(from: u.ClassSymbol, to: u.ClassSymbol, wildcardArg: Boolean = false)(implicit ctx: Context): Type = self match {
case self @ u.NullaryMethodType(tp) =>
u.NullaryMethodType(translateParameterized(tp)(from, to, wildcardArg=false))
case _ =>
if (self.typeSymbol.isSubClass(from)) {
def elemType(tp: Type): Type = tp.dealiasWiden match {
// case tp: AndOrType => tp.derivedAndOrType(elemType(tp.tp1), elemType(tp.tp2))
case tp: u.RefinedType => u.intersectionType(tp.parents.map(elemType))
case _ => tp.baseType(from).typeArgs.head
}
val arg = elemType(self)
val arg1 = if (wildcardArg) u.TypeBounds.upper(arg) else arg
to.ref(arg1 :: Nil)
}
else self
}

def translateFromRepeated(self: Type)(toArray: Boolean, translateWildcard: Boolean = false)(implicit ctx: Context): Type = {
val seqClass = if (toArray) u.definitions.ArrayClass else u.definitions.SeqClass
if (translateWildcard && self === u.WildcardType)
seqClass.ref(u.WildcardType :: Nil)
else if (isRepeatedParam(self))
// We want `Array[? <: T]` because arrays aren't covariant until after
// erasure. See `tests/pos/i5140`.
translateParameterized(self)(u.definitions.RepeatedParamClass, seqClass, wildcardArg = toArray)
else self
}

val sym = ref.qualifiedName match {
case TypeName(obj: ObjectName) => ctx.requiredObject(obj)
case clazz => ctx.requiredClass(clazz)
def sigName(tp: Type, isJava: Boolean)(implicit ctx: Context): ErasedTypeRef = {
val normTp = translateFromRepeated(tp)(toArray = isJava)
erasedSigName(normTp.erasure)
}

private def erasedSigName(erased: Type)(implicit ctx: Context): ErasedTypeRef = erased match {
case erased: u.ExistentialType => erasedSigName(erased.underlying)
case erased: u.TypeRef =>
import TastyName._
if (!isSymbol(erased.sym))
typeError(s"missing: ${erased.prefix}, ${erased.sym.name}")
var dims = 0
var clazzRef: Type = erased
while (clazzRef.typeArgs.nonEmpty && clazzRef.typeSymbol.isSubClass(u.definitions.ArrayClass)) {
dims += 1
clazzRef = clazzRef.typeArgs.head
}
def unpeelName(acc: List[TastyName], tpe: Type): List[TastyName] = {
def mkRef(sym: Symbol) = {
val name = SimpleName(sym.name.toString)
if (sym.isModuleClass && !sym.isPackageClass) ObjectName(name)
else name
}
def rec(pre: Type) =
(pre ne u.NoPrefix) && (pre ne u.NoType) && (pre.typeSymbol != u.rootMirror.RootClass)
tpe match {
case u.TypeRef(pre, sym, _) =>
val ref = mkRef(sym)
if (rec(pre)) unpeelName(ref :: acc, pre)
else ref :: acc
case tpe @ u.ThisType(sym) =>
val ref = mkRef(sym)
val pre = tpe.prefix
if (rec(pre)) unpeelName(ref :: acc, pre)
else ref :: acc
}
}
val name = (unpeelName(Nil, clazzRef): @unchecked) match {
case single :: Nil => single
case base :: rest => rest.foldLeft(base)((acc, n) => n match {
case ObjectName(base) => ObjectName(QualifiedName(acc, PathSep, base.asSimpleName))
case name => QualifiedName(acc, PathSep, name.asSimpleName)
})
}
ErasedTypeRef(name.toTypeName, dims)
}

(0 until ref.arrayDims).foldLeft(sym.tpe.erasure)((acc, _) => u.definitions.arrayType(acc))
}

/** A type which accepts two type arguments, representing an intersection type
Expand Down
1 change: 1 addition & 0 deletions src/compiler/scala/tools/tasty/ErasedTypeRef.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ case class ErasedTypeRef(qualifiedName: TypeName, arrayDims: Int) {
val qualified = qualifiedName.source
"[" * arrayDims + (if (qualifiedName.toTermName.isObjectName) s"object $qualified" else qualified)
}
def encode: ErasedTypeRef = ErasedTypeRef(TastyName.deepEncode(qualifiedName).toTypeName, arrayDims)
}

object ErasedTypeRef {
Expand Down
53 changes: 35 additions & 18 deletions src/compiler/scala/tools/tasty/TastyName.scala
Original file line number Diff line number Diff line change
Expand Up @@ -80,35 +80,41 @@ object TastyName {
*/
object SourceEncoder extends StringBuilderEncoder {
def traverse(sb: StringBuilder, name: TastyName): StringBuilder = name match {
case name: SimpleName => sb.append(name.raw)
case name: SimpleName => sb append (name.raw)
case name: ObjectName => traverse(sb, name.base)
case name: TypeName => traverse(sb, name.base)
case name: SignedName => traverse(sb, name.qual)
case name: UniqueName => traverse(traverse(sb, name.qual), name.sep).append(name.num)
case name: DefaultName => traverse(sb, name.qual).append(DefaultGetterStr).append(name.num + 1)
case name: QualifiedName => traverse(traverse(traverse(sb, name.qual), name.sep), name.selector)
case name: PrefixName => traverse(traverse(sb, name.prefix), name.qual)
case name: UniqueName => traverse(sb, name.qual) append (name.sep.raw) append (name.num)
case name: QualifiedName => traverse(traverse(sb, name.qual) append (name.sep.raw), name.selector)
case name: PrefixName => traverse(sb append (name.prefix.raw), name.qual)

case name: DefaultName if name.qual == Constructor =>
sb append DefaultGetterInitStr append (name.num + 1)

case name: DefaultName => traverse(sb, name.qual) append DefaultGetterStr append (name.num + 1)
}
}

/** Displays formatted information about the structure of the name
*/
object DebugEncoder extends StringBuilderEncoder {

def merge[T](sb: StringBuilder, sig: Signature[T]) = sig.mergeShow(sb)

def traverse(sb: StringBuilder, name: TastyName): StringBuilder = name match {

case SimpleName(raw) => sb.append(raw)
case DefaultName(qual, num) => traverse(sb, qual).append("[Default ").append(num + 1).append(']')
case PrefixName(prefix, qual) => traverse(traverse(sb, qual).append("[Prefix "), prefix).append(']')
case ObjectName(name) => traverse(sb, name).append("[ModuleClass]")
case TypeName(name) => traverse(sb, name).append("[Type]")
case SignedName(name,sig) => sig.map(_.signature).mergeShow(traverse(sb, name).append("[Signed ")).append(']')
case SimpleName(raw) => sb append raw
case DefaultName(qual, num) => traverse(sb, qual) append "[Default " append (num + 1) append ']'
case PrefixName(prefix, qual) => traverse(sb, qual) append "[Prefix " append (prefix.raw) append ']'
case ObjectName(name) => traverse(sb, name) append "[ModuleClass]"
case TypeName(name) => traverse(sb, name) append "[Type]"
case SignedName(name,sig) => merge(traverse(sb, name) append "[Signed ", sig.map(_.signature)) append ']'

case QualifiedName(qual, sep, name) =>
traverse(traverse(traverse(sb, qual).append("[Qualified "), sep).append(' '), name).append(']')
traverse(sb, qual) append "[Qualified " append (sep.raw) append ' ' append (name.raw) append ']'

case UniqueName(qual, sep, num) =>
traverse(traverse(sb, qual).append("[Unique "), sep).append(' ').append(num).append(']')
traverse(sb, qual) append "[Unique " append (sep.raw) append ' ' append num append ']'

}

Expand All @@ -130,17 +136,28 @@ object TastyName {
case name: ObjectName => traverse(sb, name.base)
case name: TypeName => traverse(sb, name.base)
case name: SignedName => traverse(sb, name.qual)
case name: UniqueName => traverse(sb, name.qual).append(name.sep.raw).append(name.num)
case name: QualifiedName => traverse(traverse(sb, name.qual).append(name.sep.raw), name.selector)
case name: PrefixName => traverse(sb.append(name.prefix), name.qual)
case name: UniqueName => traverse(sb, name.qual) append (name.sep.raw) append (name.num)
case name: QualifiedName => traverse(traverse(sb, name.qual) append (name.sep.raw), name.selector)
case name: PrefixName => traverse(sb append (name.prefix.raw), name.qual)

case name: DefaultName if name.qual == Constructor => sb.append(DefaultGetterInitStr).append(name.num + 1)
case name: DefaultName if name.qual == Constructor => sb append DefaultGetterInitStr append (name.num + 1)

case name: DefaultName => traverse(sb, name.qual).append(DefaultGetterStr).append(name.num + 1)
case name: DefaultName => traverse(sb, name.qual) append DefaultGetterStr append (name.num + 1)
}

}

def deepEncode(name: TastyName): TastyName = name match {
case SimpleName(raw) => SimpleName(NameTransformer.encode(raw))
case QualifiedName(qual, sep, selector) => QualifiedName(deepEncode(qual), sep, deepEncode(selector).asSimpleName)
case ObjectName(base) => ObjectName(deepEncode(base))
case UniqueName(qual, sep, num) => UniqueName(deepEncode(qual), sep, num)
case DefaultName(qual, num) => DefaultName(deepEncode(qual), num)
case PrefixName(prefix, qual) => PrefixName(prefix, deepEncode(qual))
case TypeName(base) => TypeName(deepEncode(base))
case SignedName(qual, sig) => SignedName(deepEncode(qual), sig.map(_.encode))
}

}

/**This is a data structure representing semantic names. [[TastyName]] is the interface that TASTy uses to select
Expand Down
6 changes: 3 additions & 3 deletions test/tasty/neg-isolated/src-2/TestAnnotated.check
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
TestAnnotated_fail.scala:5: error: can't find type required by a member of package tastytest: tastytest.annot; perhaps it is missing from the classpath.
TestAnnotated_fail.scala:5: error: can't find type required by package tastytest: tastytest.annot; perhaps it is missing from the classpath.
def test1 = new Annotated {} // error: can't find type required by a member of package tastytest: tastytest.annot
^
TestAnnotated_fail.scala:6: error: could not find class tastytest.Parent whilst reading annotation of trait PublicAnnotated; perhaps it is missing from the classpath.
TestAnnotated_fail.scala:6: error: can't find type required by parameter parent in class tastytest.publicAnnot: tastytest.Parent; perhaps it is missing from the classpath.
def test2 = new PublicAnnotated {} // error: could not find class tastytest.Parent
^
TestAnnotated_fail.scala:7: error: could not find class tastytest.<<< whilst reading annotation of trait SymbollicAnnotated; perhaps it is missing from the classpath.
TestAnnotated_fail.scala:7: error: can't find type required by parameter parent in class tastytest.symbollicAnnot: tastytest.<<<; perhaps it is missing from the classpath.
def test3 = new SymbollicAnnotated {} // error: could not find class tastytest.<<<
^
3 errors
4 changes: 2 additions & 2 deletions test/tasty/neg-isolated/src-2/TestChildren.check
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
TestChildren_fail.scala:5: error: can't find type in parents of class Child: tastytest.Parent; perhaps it is missing from the classpath.
TestChildren_fail.scala:5: error: can't find type in parents of class tastytest.Child: tastytest.Parent; perhaps it is missing from the classpath.
def test1 = new Child
^
TestChildren_fail.scala:6: error: can't find type in parents of object Module: tastytest.Parent; perhaps it is missing from the classpath.
TestChildren_fail.scala:6: error: can't find type in parents of object tastytest.Module: tastytest.Parent; perhaps it is missing from the classpath.
def test2 = Module
^
2 errors

0 comments on commit f8c2212

Please sign in to comment.