Skip to content

Commit b0627d0

Browse files
authored
Merge pull request #100 from scalacenter/tasty/fix-ErasedTypeRef
translate type to ErasedTypeRef instead
2 parents 8839eac + a75b60e commit b0627d0

File tree

17 files changed

+251
-89
lines changed

17 files changed

+251
-89
lines changed

src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ package scala.tools.nsc.tasty.bridge
1515
import scala.annotation.tailrec
1616
import scala.reflect.io.AbstractFile
1717

18-
import scala.tools.tasty.{TastyName, TastyFlags}, TastyFlags._
18+
import scala.tools.tasty.{TastyName, TastyFlags}, TastyFlags._, TastyName.ObjectName
1919
import scala.tools.nsc.tasty.{TastyUniverse, TastyModes, SafeEq}, TastyModes._
2020
import scala.reflect.internal.MissingRequirementError
2121

@@ -24,7 +24,7 @@ trait ContextOps { self: TastyUniverse =>
2424

2525
private def describeOwner(owner: Symbol): String = {
2626
val kind =
27-
if (owner.is(Param)) {
27+
if (owner.isOneOf(Param | ParamSetter)) {
2828
if (owner.isType) "type parameter"
2929
else "parameter"
3030
}
@@ -60,7 +60,7 @@ trait ContextOps { self: TastyUniverse =>
6060
@inline final def assert(assertion: Boolean): Unit =
6161
if (!assertion) assertError("")
6262

63-
sealed abstract class Context {
63+
sealed abstract class Context { thisCtx =>
6464

6565
final def globallyVisibleOwner: Symbol = owner.logicallyEnclosingMember
6666

@@ -100,15 +100,9 @@ trait ContextOps { self: TastyUniverse =>
100100
symOrDependencyError(false, true, fullname)(loadingMirror.getPackage(encodeTermName(fullname).toString))
101101
}
102102

103-
final def requiredClass(fullname: TastyName.TypeName): Symbol =
104-
symOrDependencyError(false, false, fullname)(loadingMirror.getRequiredClass(encodeTypeName(fullname).toString))
105-
106103
final def optionalClass(fullname: TastyName.TypeName): Option[Symbol] =
107104
loadingMirror.getClassIfDefined(encodeTypeName(fullname).toString).toOption
108105

109-
final def requiredObject(fullname: TastyName.ObjectName): Symbol =
110-
symOrDependencyError(true, false, fullname)(loadingMirror.getRequiredModule(encodeTermName(fullname).toString))
111-
112106
private def symOrDependencyError(isObject: Boolean, isPackage: Boolean, fullname: TastyName)(sym: => Symbol): Symbol = {
113107
try sym
114108
catch {

src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import scala.tools.nsc.tasty.SafeEq
1616

1717
import scala.tools.nsc.tasty.{TastyUniverse, TastyModes}, TastyModes._
1818
import scala.tools.tasty.{TastyName, Signature, TastyFlags}, TastyName.SignedName, Signature.MethodSignature, TastyFlags.TastyFlagSet
19+
import scala.tools.tasty.ErasedTypeRef
1920

2021
trait SymbolOps { self: TastyUniverse =>
2122
import self.{symbolTable => u}
@@ -67,7 +68,7 @@ trait SymbolOps { self: TastyUniverse =>
6768
termParamss
6869

6970
def namedMemberOfType(space: Type, tname: TastyName)(implicit ctx: Context): Symbol = tname match {
70-
case SignedName(qual, sig) => signedMemberOfSpace(space, qual, sig.map(resolveErasedTypeRef))
71+
case SignedName(qual, sig) => signedMemberOfSpace(space, qual, sig.map(_.encode))
7172
case _ => memberOfSpace(space, tname)
7273
}
7374

@@ -88,40 +89,57 @@ trait SymbolOps { self: TastyUniverse =>
8889
}
8990
else space.member(encodeTermName(tname))
9091
}
91-
if (isSymbol(member)) member
92-
else {
93-
val kind = if (tname.isTypeName) "type" else "term"
94-
def addendum(name: String) =
95-
if (ctx.mode.is(ReadParents)) s"$kind in parents of ${if (ctx.owner.isLocalDummy) ctx.owner.owner else ctx.owner}: $name"
96-
else if (ctx.owner.isClass) s"$kind required by a member of ${ctx.owner}: $name"
97-
else s"$kind $name while unpickling ${ctx.owner}"
98-
val msg =
99-
if (tname.isTypeName && space.typeSymbol.hasPackageFlag)
100-
s"can't find ${addendum(s"${space.typeSymbol.fullNameString}.$tname")}; perhaps it is missing from the classpath."
101-
else
102-
s"can't find ${addendum("" + tname)}, in $space"
103-
typeError(msg)
92+
if (isSymbol(member) && hasType(member)) member
93+
else errorMissing(space, tname)
94+
}
95+
96+
private def hasType(member: Symbol)(implicit ctx: Context) = {
97+
ctx.mode.is(ReadAnnotation) || (member.rawInfo `ne` u.NoType)
98+
}
99+
100+
private def errorMissing[T](space: Type, tname: TastyName)(implicit ctx: Context) = {
101+
val kind = if (tname.isTypeName) "type" else "term"
102+
def typeToString(tpe: Type) = {
103+
def inner(sb: StringBuilder, tpe: Type): StringBuilder = tpe match {
104+
case u.SingleType(pre, sym) => inner(sb, pre) append '.' append (
105+
if (sym.isPackageObjectOrClass) s"`${sym.name}`"
106+
else String valueOf sym.name
107+
)
108+
case u.TypeRef(pre, sym, _) if sym.isTerm =>
109+
if ((pre eq u.NoPrefix) || (pre eq u.NoType)) sb append sym.name
110+
else inner(sb, pre) append '.' append sym.name
111+
case tpe => sb append tpe
112+
}
113+
inner(new StringBuilder(), tpe).toString
114+
}
115+
def addendum(name: String) = {
116+
if (ctx.mode.is(ReadParents)) s"$kind in parents of ${location(if (ctx.owner.isLocalDummy) ctx.owner.owner else ctx.owner)}: $name"
117+
else s"$kind required by ${location(ctx.owner)}: $name"
104118
}
119+
val missing = addendum(s"${typeToString(space)}.$tname")
120+
typeError(s"can't find $missing; perhaps it is missing from the classpath.")
105121
}
106122

107-
private def signedMemberOfSpace(space: Type, qual: TastyName, sig: MethodSignature[Type])(implicit ctx: Context): Symbol = {
108-
ctx.log(s"""<<< looking for overload member[$space] @@ $qual: ${sig.show}""")
123+
private def signedMemberOfSpace(space: Type, qual: TastyName, sig: MethodSignature[ErasedTypeRef])(implicit ctx: Context): Symbol = {
124+
ctx.log(s"""<<< looking for overload member[$space] @@ $qual: ${showSig(sig)}""")
109125
val member = space.member(encodeTermName(qual))
110-
val (tyParamCount, argTpes) = {
126+
if (!(isSymbol(member) && hasType(member))) errorMissing(space, qual)
127+
val (tyParamCount, argTpeRefs) = {
111128
val (tyParamCounts, params) = sig.params.partitionMap(identity)
112129
if (tyParamCounts.length > 1) {
113-
unsupportedError(s"multiple type parameter lists on erased method signature ${sig.show}")
130+
unsupportedError(s"multiple type parameter lists on erased method signature ${showSig(sig)}")
114131
}
115132
(tyParamCounts.headOption.getOrElse(0), params)
116133
}
117134
def compareSym(sym: Symbol): Boolean = sym match {
118135
case sym: u.MethodSymbol =>
119136
val params = sym.paramss.flatten
120-
sym.returnType.erasure =:= sig.result &&
121-
params.length === argTpes.length &&
137+
val isJava = sym.isJavaDefined
138+
NameErasure.sigName(sym.returnType, isJava) === sig.result &&
139+
params.length === argTpeRefs.length &&
122140
(qual === TastyName.Constructor && tyParamCount === member.owner.typeParams.length
123141
|| tyParamCount === sym.typeParams.length) &&
124-
params.zip(argTpes).forall { case (param, tpe) => param.tpe.erasure =:= tpe } && {
142+
params.zip(argTpeRefs).forall { case (param, tpe) => NameErasure.sigName(param.tpe, isJava) === tpe } && {
125143
ctx.log(s">>> selected ${showSym(sym)}: ${sym.tpe}")
126144
true
127145
}
@@ -130,8 +148,9 @@ trait SymbolOps { self: TastyUniverse =>
130148
false
131149
}
132150
member.asTerm.alternatives.find(compareSym).getOrElse(
133-
typeError(s"No matching overload of $space.$qual with signature ${sig.show}"))
151+
typeError(s"No matching overload of $space.$qual with signature ${showSig(sig)}"))
134152
}
135153

154+
def showSig(sig: MethodSignature[ErasedTypeRef]): String = sig.map(_.signature).show
136155
def showSym(sym: Symbol): String = s"Symbol($sym, #${sym.id})"
137156
}

src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala

Lines changed: 84 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ package scala.tools.nsc.tasty.bridge
1414

1515
import scala.tools.nsc.tasty.{TastyUniverse, SafeEq}
1616

17-
import scala.tools.tasty.{TastyName, ErasedTypeRef, TastyFlags}, TastyFlags._
17+
import scala.tools.tasty.{TastyName, ErasedTypeRef, TastyFlags}, TastyFlags._, TastyName.QualifiedName
1818

1919
import scala.reflect.internal.Variance
2020
import scala.util.chaining._
@@ -150,15 +150,92 @@ trait TypeOps { self: TastyUniverse =>
150150
bounds
151151
}
152152

153-
private[bridge] def resolveErasedTypeRef(ref: ErasedTypeRef)(implicit ctx: Context): Type = {
154-
import TastyName._
153+
/** This is a port from Dotty of transforming a Method type to an ErasedTypeRef
154+
*/
155+
private[bridge] object NameErasure {
156+
157+
def isRepeatedParam(self: Type): Boolean =
158+
self.typeSymbol eq u.definitions.RepeatedParamClass
159+
160+
/** 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.
161+
* `from` and `to` must be static classes, both with one type parameter, and the same variance.
162+
* Do the same for by name types => From[T] and => To[T]
163+
*/
164+
def translateParameterized(self: Type)(from: u.ClassSymbol, to: u.ClassSymbol, wildcardArg: Boolean = false)(implicit ctx: Context): Type = self match {
165+
case self @ u.NullaryMethodType(tp) =>
166+
ui.nullaryMethodType(translateParameterized(tp)(from, to))
167+
case _ =>
168+
if (self.typeSymbol.isSubClass(from)) {
169+
def elemType(tp: Type): Type = tp.dealiasWiden match {
170+
// case tp: AndOrType => tp.derivedAndOrType(elemType(tp.tp1), elemType(tp.tp2))
171+
case tp: u.RefinedType => ui.intersectionType(tp.parents.map(elemType))
172+
case _ => tp.baseType(from).typeArgs.head
173+
}
174+
val arg = elemType(self)
175+
val arg1 = if (wildcardArg) u.TypeBounds.upper(arg) else arg
176+
to.ref(arg1 :: Nil)
177+
}
178+
else self
179+
}
180+
181+
def translateFromRepeated(self: Type)(toArray: Boolean, translateWildcard: Boolean = false)(implicit ctx: Context): Type = {
182+
val seqClass = if (toArray) u.definitions.ArrayClass else u.definitions.SeqClass
183+
if (translateWildcard && self === u.WildcardType)
184+
seqClass.ref(u.WildcardType :: Nil)
185+
else if (isRepeatedParam(self))
186+
// We want `Array[? <: T]` because arrays aren't covariant until after
187+
// erasure. See `tests/pos/i5140`.
188+
translateParameterized(self)(u.definitions.RepeatedParamClass, seqClass, wildcardArg = toArray)
189+
else self
190+
}
155191

156-
val sym = ref.qualifiedName match {
157-
case TypeName(obj: ObjectName) => ctx.requiredObject(obj)
158-
case clazz => ctx.requiredClass(clazz)
192+
def sigName(tp: Type, isJava: Boolean)(implicit ctx: Context): ErasedTypeRef = {
193+
val normTp = translateFromRepeated(tp)(toArray = isJava)
194+
erasedSigName(normTp.erasure)
195+
}
196+
197+
private def erasedSigName(erased: Type)(implicit ctx: Context): ErasedTypeRef = erased match {
198+
case erased: u.ExistentialType => erasedSigName(erased.underlying)
199+
case erased: u.TypeRef =>
200+
import TastyName._
201+
if (!isSymbol(erased.sym))
202+
typeError(s"missing: ${erased.prefix}, ${erased.sym.name}")
203+
var dims = 0
204+
var clazzRef: Type = erased
205+
while (clazzRef.typeArgs.nonEmpty && clazzRef.typeSymbol.isSubClass(u.definitions.ArrayClass)) {
206+
dims += 1
207+
clazzRef = clazzRef.typeArgs.head
208+
}
209+
def unpeelName(acc: List[TastyName], tpe: Type): List[TastyName] = {
210+
def mkRef(sym: Symbol) = {
211+
val name = SimpleName(sym.name.toString)
212+
if (sym.isModuleClass && !sym.isPackageClass) ObjectName(name)
213+
else name
214+
}
215+
def rec(pre: Type) =
216+
(pre ne u.NoPrefix) && (pre ne u.NoType) && (pre.typeSymbol != u.rootMirror.RootClass)
217+
tpe match {
218+
case u.TypeRef(pre, sym, _) =>
219+
val ref = mkRef(sym)
220+
if (rec(pre)) unpeelName(ref :: acc, pre)
221+
else ref :: acc
222+
case tpe @ u.ThisType(sym) =>
223+
val ref = mkRef(sym)
224+
val pre = tpe.prefix
225+
if (rec(pre)) unpeelName(ref :: acc, pre)
226+
else ref :: acc
227+
}
228+
}
229+
val name = (unpeelName(Nil, clazzRef): @unchecked) match {
230+
case single :: Nil => single
231+
case base :: rest => rest.foldLeft(base)((acc, n) => n match {
232+
case ObjectName(base) => ObjectName(QualifiedName(acc, PathSep, base.asSimpleName))
233+
case name => QualifiedName(acc, PathSep, name.asSimpleName)
234+
})
235+
}
236+
ErasedTypeRef(name.toTypeName, dims)
159237
}
160238

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

164241
/** A type which accepts two type arguments, representing an intersection type

src/compiler/scala/tools/tasty/ErasedTypeRef.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ case class ErasedTypeRef(qualifiedName: TypeName, arrayDims: Int) {
2525
val qualified = qualifiedName.source
2626
"[" * arrayDims + (if (qualifiedName.toTermName.isObjectName) s"object $qualified" else qualified)
2727
}
28+
def encode: ErasedTypeRef = ErasedTypeRef(TastyName.deepEncode(qualifiedName).toTypeName, arrayDims)
2829
}
2930

3031
object ErasedTypeRef {

src/compiler/scala/tools/tasty/TastyName.scala

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@
1313
package scala.tools.tasty
1414

1515
import scala.reflect.NameTransformer
16+
import scala.tools.tasty.TastyName.SimpleName
17+
import scala.tools.tasty.TastyName.ObjectName
18+
import scala.tools.tasty.TastyName.SignedName
19+
import scala.tools.tasty.TastyName.DefaultName
20+
import scala.tools.tasty.TastyName.TypeName
1621

1722
object TastyName {
1823

@@ -159,6 +164,18 @@ object TastyName {
159164

160165
}
161166

167+
def deepEncode(name: TastyName): TastyName = name match {
168+
case SimpleName(raw) => SimpleName(NameTransformer.encode(raw))
169+
case QualifiedName(qual, sep, selector) => QualifiedName(deepEncode(qual), sep, deepEncode(selector).asSimpleName)
170+
case ObjectName(base) => ObjectName(deepEncode(base))
171+
case UniqueName(qual, sep, num) => UniqueName(deepEncode(qual), sep, num)
172+
case DefaultName(qual, num) => DefaultName(deepEncode(qual), num)
173+
case PrefixName(prefix, qual) => PrefixName(prefix, deepEncode(qual))
174+
case SuffixName(qual, suffix) => SuffixName(deepEncode(qual), suffix)
175+
case TypeName(base) => TypeName(deepEncode(base))
176+
case SignedName(qual, sig) => SignedName(deepEncode(qual), sig.map(_.encode))
177+
}
178+
162179
}
163180

164181
/** class to represent Names as defined in TASTy, with methods to extract scala identifiers

src/reflect/scala/reflect/internal/StdNames.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,7 @@ trait StdNames {
280280
final val TypeName: NameType = nameType("TypeName")
281281
final val TypeDef: NameType = nameType("TypeDef")
282282
final val Quasiquote: NameType = nameType("Quasiquote")
283+
final val TastyMacroImpl: NameType = nameType("TastyMacroImpl")
283284

284285
// quasiquote-specific names
285286
final val QUASIQUOTE_FUNCTION: NameType = nameType("$quasiquote$function$")

src/tastytest/scala/tools/tastytest/Dotc.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ object Dotc {
2222
}
2323
}
2424

25-
def dotc(out: String, classpath: String, sources: String*): Try[Boolean] = {
25+
def dotc(out: String, classpath: String, additionalSettings: Seq[String], sources: String*): Try[Boolean] = {
2626
if (sources.isEmpty) {
2727
Success(true)
2828
}
@@ -34,14 +34,14 @@ object Dotc {
3434
"-Yerased-terms",
3535
"-Xfatal-warnings",
3636
"-usejavacp"
37-
) ++ sources
37+
) ++ additionalSettings ++ sources
3838
dotcProcess(args)
3939
}
4040
}
4141

4242
def main(args: Array[String]): Unit = {
4343
val Array(out, src) = args
44-
val success = dotc(out, out, src).get
44+
val success = dotc(out, out, Nil, src).get
4545
sys.exit(if (success) 0 else 1)
4646
}
4747
}

0 commit comments

Comments
 (0)