diff --git a/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala b/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala index 98f0d80dd536..628491af03a3 100644 --- a/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala +++ b/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala @@ -81,8 +81,9 @@ class TreeUnpickler[Tasty <: TastyUniverse]( this.roots = Set(objectRoot, classRoot) val rdr = new TreeReader(reader).fork ownerTree = new OwnerTree(NoAddr, 0, rdr.fork, reader.endAddr) + def indexTopLevel(implicit ctx: Context): Unit = rdr.indexStats(reader.endAddr) if (rdr.isTopLevel) - rdr.indexStats(reader.endAddr) + inIndexingContext(indexTopLevel(_)) } class Completer(reader: TastyReader, originalFlagSet: TastyFlagSet)(implicit ctx: Context) extends TastyLazyType(originalFlagSet) { self => @@ -604,21 +605,19 @@ class TreeUnpickler[Tasty <: TastyUniverse]( * current address and `end`. */ def indexStats(end: Addr)(implicit ctx: Context): Unit = { - val statsCtx = ctx.addMode(IndexStats) while (currentAddr.index < end.index) { nextByte match { case tag @ (VALDEF | DEFDEF | TYPEDEF | TYPEPARAM | PARAM) => - symbolAtCurrent()(statsCtx) + symbolAtCurrent() skipTree() case IMPORT => skipTree() case PACKAGE => - processPackage(end => implicit ctx => indexStats(end))(statsCtx) + processPackage(end => implicit ctx => indexStats(end)) case _ => skipTree() } } - statsCtx.forceAnnotations() assert(currentAddr.index === end.index) } @@ -774,23 +773,25 @@ class TreeUnpickler[Tasty <: TastyUniverse]( assert(readByte() === TEMPLATE) val end = readEnd() - // ** PARAMETERS ** - ctx.log(s"$currentAddr Template: reading parameters of $cls:") - val tparams = readIndexedParams[NoCycle](TYPEPARAM) - if (tparams.nonEmpty) { - cls.info = defn.PolyType(tparams.map(symFromNoCycle), cls.info) + def completeTypeParameters()(implicit ctx: Context): List[Symbol] = { + ctx.log(s"$currentAddr Template: reading parameters of $cls:") + val tparams = readIndexedParams[NoCycle](TYPEPARAM).map(symFromNoCycle) + if (tparams.nonEmpty) { + cls.info = defn.PolyType(tparams, cls.info) + } + readIndexedParams[NoCycle](PARAM) // skip value parameters + tparams } - readIndexedParams[NoCycle](PARAM) // skip value parameters - // ** MEMBERS ** - ctx.log(s"$currentAddr Template: indexing members of $cls:") - val bodyIndexer = fork - while (bodyIndexer.reader.nextByte != DEFDEF) bodyIndexer.skipTree() // skip until primary ctor - bodyIndexer.indexStats(end) + def indexMembers()(implicit ctx: Context): Unit = { + ctx.log(s"$currentAddr Template: indexing members of $cls:") + val bodyIndexer = fork + while (bodyIndexer.reader.nextByte != DEFDEF) bodyIndexer.skipTree() // skip until primary ctor + bodyIndexer.indexStats(end) + } - // ** PARENTS ** - ctx.log(s"$currentAddr Template: adding parents of $cls:") - val parents = { + def traverseParents()(implicit ctx: Context): List[Type] = { + ctx.log(s"$currentAddr Template: adding parents of $cls:") val parentCtx = ctx.withOwner(localDummy).addMode(ReadParents) val parentWithOuter = parentCtx.addMode(OuterTerm) collectWhile(nextByte != SELFDEF && nextByte != DEFDEF) { @@ -801,7 +802,7 @@ class TreeUnpickler[Tasty <: TastyUniverse]( } } - if (nextByte === SELFDEF) { + def addSelfDef()(implicit ctx: Context): Unit = { ctx.log(s"$currentAddr Template: adding self-type of $cls:") readByte() // read SELFDEF tag readLongNat() // skip Name @@ -810,21 +811,36 @@ class TreeUnpickler[Tasty <: TastyUniverse]( cls.typeOfThis = selfTpe } - val parentTypes = ctx.adjustParents(cls, parents) - - ctx.setInfo(cls, { - val classInfo = defn.ClassInfoType(parentTypes, cls) - // TODO [tasty]: if support opaque types, refine the self type with any opaque members here - if (tparams.isEmpty) classInfo - else defn.PolyType(tparams.map(symFromNoCycle), classInfo) - }) + def setInfoWithParents(tparams: List[Symbol], parentTypes: List[Type])(implicit ctx: Context): Unit = { + def debugMsg = { + val addendum = + if (parentTypes.isEmpty) "" + else parentTypes.map(lzyShow).mkString(" extends ", " with ", "") // don't force types + s"$currentAddr Template: Updated info of $cls$addendum" + } + val info = { + val classInfo = defn.ClassInfoType(parentTypes, cls) + // TODO [tasty]: if support opaque types, refine the self type with any opaque members here + if (tparams.isEmpty) classInfo + else defn.PolyType(tparams, classInfo) + } + ctx.setInfo(cls, info) + ctx.log(debugMsg) + } - ctx.log { - val addendum = - if (parentTypes.isEmpty) "" - else parentTypes.map(lzyShow).mkString(" extends ", " with ", "") // don't force types - s"$currentAddr Template: Updated info of $cls$addendum" + def traverseTemplate()(implicit ctx: Context): Unit = { + val tparams = completeTypeParameters() + indexMembers() + val parents = traverseParents() + if (nextByte === SELFDEF) { + addSelfDef() + } + val parentTypes = ctx.adjustParents(cls, parents) + setInfoWithParents(tparams, parentTypes) } + + inIndexingContext(traverseTemplate()(_)) + } def isTopLevel: Boolean = nextByte === IMPORT || nextByte === PACKAGE @@ -845,7 +861,8 @@ class TreeUnpickler[Tasty <: TastyUniverse]( until(end)(readIndexedStatAsSym(exprOwner)) def readStatsAsSyms(exprOwner: Symbol, end: Addr)(implicit ctx: Context): List[NoCycle] = { - fork.indexStats(end) + def forkAndIndexStats(implicit ctx: Context): Unit = fork.indexStats(end) + inIndexingContext(forkAndIndexStats(_)) readIndexedStatsAsSyms(exprOwner, end) } diff --git a/src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala b/src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala index 11fc5fd6de37..48a5e0e43279 100644 --- a/src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala +++ b/src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala @@ -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 @@ -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" } @@ -74,64 +74,45 @@ trait ContextOps { self: TastyUniverse => else u.NoSymbol //throw new AssertionError(s"no module $name in ${location(owner)}") } - /**Maintains state through traversal of a TASTy file, such as the outer scope of the defintion being traversed, the - * traversal mode, and the root owners and source path for the TASTy file. - * 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 => + /**Perform an operation within a context that has the mode [[IndexStats]] will force any collected annotations + * afterwards*/ + def inIndexingContext(op: Context => Unit)(implicit ctx: Context): Unit = { + val statsCtx = ctx.addMode(IndexStats) + op(statsCtx) + statsCtx.initialContext.forceAnnotations() + } - private[this] implicit final def implyThisCtx: thisCtx.type = thisCtx - private[this] var mySymbolsToForceAnnots: mutable.LinkedHashSet[Symbol] = _ - private def stageSymbolToForceAnnots(sym: Symbol): Unit = { - if (sym.annotations.nonEmpty) { - if (mySymbolsToForceAnnots == null) { - mySymbolsToForceAnnots = mutable.LinkedHashSet.empty - } - mySymbolsToForceAnnots += sym + /**Forces lazy annotations, if one is [[scala.annotation.internal.Child]] then it will add the referenced type as a + * sealed child. + */ + private def analyseAnnotations(sym: Symbol)(implicit ctx: Context): Unit = { + for (annot <- sym.annotations) { + annot.completeInfo() + if (annot.tpe.typeSymbolDirect === defn.ChildAnnot) { + val child = annot.tpe.typeArgs.head.typeSymbolDirect + sym.addChild(child) + ctx.log(s"adding sealed child ${showSym(child)} to ${showSym(sym)}") } } + } - /** Force any lazy annotations collected from declaration statements directly in this scope. - * - * It is important to call this *after* indexing statements in a scope, otherwise calling - * [[ownertree.findOwner]] can fail, this is because [[ownertree.findOwner]] cannot traverse a definition tree at - * a given address before a symbol has been registered to that address. - */ - def forceAnnotations(): Unit = { - if (mySymbolsToForceAnnots != null) { - val toForce = mySymbolsToForceAnnots - mySymbolsToForceAnnots = null - for (sym <- toForce) { - log(s"!!! forcing annotations on ${showSym(sym)}") - analyseAnnotations(sym) - } - } - } + /**Maintains state through traversal of a TASTy file, such as the outer scope of the defintion being traversed, the + * traversal mode, and the root owners and source path for the TASTy file. + * 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 => - /**Forces lazy annotations, if one is [[scala.annotation.internal.Child]] then it will add the referenced type as a - * sealed child. - */ - private def analyseAnnotations(sym: Symbol): Unit = { - for (annot <- sym.annotations) { - annot.completeInfo() - if (annot.tpe.typeSymbolDirect === defn.ChildAnnot) { - val child = annot.tpe.typeArgs.head.typeSymbolDirect - sym.addChild(child) - log(s"adding sealed child ${showSym(child)} to ${showSym(sym)}") - } - } - } + protected implicit final def implyThisCtx: thisCtx.type = thisCtx /**Associates the annotations with the symbol, and will force their evaluation if not reading statements.*/ def adjustAnnotations(sym: Symbol, annots: List[DeferredAnnotation]): Unit = { if (annots.nonEmpty) { if (mode.is(IndexStats)) { log(s"lazily adding annotations to ${showSym(sym)}") - stageSymbolToForceAnnots(sym.setAnnotations(annots.map(_.lzy(sym)))) + initialContext.stageSymbolToForceAnnots(sym.setAnnotations(annots.map(_.lzy(sym)))) } else { log(s"eagerly adding annotations to ${showSym(sym)}") @@ -169,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 { @@ -198,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 @@ -428,36 +400,30 @@ trait ContextOps { self: TastyUniverse => } final def withOwner(owner: Symbol): Context = - if (owner `ne` this.owner) fresh(owner) else this + if (owner `ne` this.owner) freshSymbol(owner) else this final def withNewScope: Context = - fresh(newLocalDummy) + freshSymbol(newLocalDummy) final def selectionCtx(name: TastyName): Context = this // if (name.isConstructorName) this.addMode(Mode.InSuperCall) else this - final def fresh(owner: Symbol): FreshContext = new FreshContext(owner, this, this.mode) - - private def sibling(mode: TastyMode): FreshContext = new FreshContext(this.owner, outerOrThis, mode) - private def sibling: FreshContext = sibling(mode) - - private def outerOrThis: Context = this match { - case ctx: FreshContext => ctx.outer - case ctx => ctx - } + final def freshSymbol(owner: Symbol): FreshContext = new FreshContext(owner, this, this.mode) + final def freshMode(mode: TastyMode): FreshContext = new FreshContext(this.owner, this, mode) + final def fresh: FreshContext = new FreshContext(this.owner, this, this.mode) final def addMode(mode: TastyMode): Context = - if (!this.mode.is(mode)) sibling(this.mode | mode) + if (!this.mode.is(mode)) freshMode(this.mode | mode) else this final def retractMode(mode: TastyMode): Context = - if (this.mode.isOneOf(mode)) sibling(this.mode &~ mode) + if (this.mode.isOneOf(mode)) freshMode(this.mode &~ mode) else this final def withMode(mode: TastyMode): Context = - if (mode != this.mode) sibling(mode) + if (mode != this.mode) freshMode(mode) else this final def withSource(source: AbstractFile): Context = - if (source `ne` this.source) sibling.atSource(source) + if (source `ne` this.source) fresh.atSource(source) else this final def withPhaseNoLater[T](phase: String)(op: Context => T): T = @@ -468,6 +434,36 @@ trait ContextOps { self: TastyUniverse => final class InitialContext(val topLevelClass: Symbol, val source: AbstractFile) extends Context { def mode: TastyMode = EmptyTastyMode def owner: Symbol = topLevelClass.owner + + private[this] var mySymbolsToForceAnnots: mutable.LinkedHashSet[Symbol] = _ + + private[ContextOps] def stageSymbolToForceAnnots(sym: Symbol): Unit = { + if (sym.annotations.nonEmpty) { + if (mySymbolsToForceAnnots == null) { + mySymbolsToForceAnnots = mutable.LinkedHashSet.empty + } + mySymbolsToForceAnnots += sym + } + } + + /** Force any lazy annotations collected from declaration statements directly in this scope. + * + * It is important to call this *after* indexing statements in a scope, otherwise calling + * [[ownertree.findOwner]] can fail, this is because [[ownertree.findOwner]] cannot traverse a definition tree at + * a given address before a symbol has been registered to that address. + */ + private[ContextOps] def forceAnnotations(): Unit = { + if (mySymbolsToForceAnnots != null) { + val toForce = mySymbolsToForceAnnots + mySymbolsToForceAnnots = null + for (sym <- toForce) { + log(s"!!! forcing annotations on ${showSym(sym)}") + analyseAnnotations(sym) + } + assert(mySymbolsToForceAnnots == null, "more symbols added while forcing") + } + } + } final class FreshContext(val owner: Symbol, val outer: Context, val mode: TastyMode) extends Context { diff --git a/src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala b/src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala index 30832afb30b5..b8146aefd66f 100644 --- a/src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala +++ b/src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala @@ -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. @@ -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) } @@ -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 } @@ -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})" } diff --git a/src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala b/src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala index 5062b1f701d4..af1521a8e204 100644 --- a/src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala +++ b/src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala @@ -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 diff --git a/src/compiler/scala/tools/tasty/ErasedTypeRef.scala b/src/compiler/scala/tools/tasty/ErasedTypeRef.scala index cc2288751afd..b102183350a4 100644 --- a/src/compiler/scala/tools/tasty/ErasedTypeRef.scala +++ b/src/compiler/scala/tools/tasty/ErasedTypeRef.scala @@ -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 { diff --git a/src/compiler/scala/tools/tasty/TastyName.scala b/src/compiler/scala/tools/tasty/TastyName.scala index b5daa294530c..0d72e023a54f 100644 --- a/src/compiler/scala/tools/tasty/TastyName.scala +++ b/src/compiler/scala/tools/tasty/TastyName.scala @@ -80,14 +80,18 @@ 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) } } @@ -95,20 +99,22 @@ object TastyName { */ 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 ']' } @@ -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 diff --git a/test/tasty/neg-isolated/src-2/TestAnnotated.check b/test/tasty/neg-isolated/src-2/TestAnnotated.check index 2ee179c73404..6e0d8fc8525d 100644 --- a/test/tasty/neg-isolated/src-2/TestAnnotated.check +++ b/test/tasty/neg-isolated/src-2/TestAnnotated.check @@ -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 diff --git a/test/tasty/neg-isolated/src-2/TestChildren.check b/test/tasty/neg-isolated/src-2/TestChildren.check index f910d39bd745..d5d9f0d654b2 100644 --- a/test/tasty/neg-isolated/src-2/TestChildren.check +++ b/test/tasty/neg-isolated/src-2/TestChildren.check @@ -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 diff --git a/test/tasty/neg-isolated/src-2/TestMethodDeps.check b/test/tasty/neg-isolated/src-2/TestMethodDeps.check index a0f185879900..4e1f62d038bb 100644 --- a/test/tasty/neg-isolated/src-2/TestMethodDeps.check +++ b/test/tasty/neg-isolated/src-2/TestMethodDeps.check @@ -1,4 +1,4 @@ -TestMethodDeps_fail.scala:5: error: can't find type tastytest.Parent while unpickling method parent; perhaps it is missing from the classpath. +TestMethodDeps_fail.scala:5: error: can't find type required by method parent in object tastytest.MethodDeps: tastytest.Parent; perhaps it is missing from the classpath. def test = MethodDeps.parent ^ 1 error diff --git a/test/tasty/neg/src-2/TestCompiletimeQuoteType.check b/test/tasty/neg/src-2/TestCompiletimeQuoteType.check index d0b9f388e304..65c9dd9e1a38 100644 --- a/test/tasty/neg/src-2/TestCompiletimeQuoteType.check +++ b/test/tasty/neg/src-2/TestCompiletimeQuoteType.check @@ -1,4 +1,4 @@ -TestCompiletimeQuoteType_fail.scala:4: error: can't find type scala.AnyKind while unpickling type T; perhaps it is missing from the classpath. +TestCompiletimeQuoteType_fail.scala:4: error: can't find type required by type parameter T in class scala.quoted.Type: scala.AnyKind; perhaps it is missing from the classpath. def test = CompiletimeQuoteType.f[Int] ^ 1 error diff --git a/test/tasty/pos/src-2/tastytest/TestAnnotated.scala b/test/tasty/pos/src-2/tastytest/TestAnnotated.scala index bec803aa300a..1f1343fa3357 100644 --- a/test/tasty/pos/src-2/tastytest/TestAnnotated.scala +++ b/test/tasty/pos/src-2/tastytest/TestAnnotated.scala @@ -3,4 +3,13 @@ package tastytest object TestAnnotated { def test1 = new Annotated {} def test2 = new RootAnnotated {} + def test3 = { + val o = new OuterClassAnnotated {} + o.foo + } + def test4 = new ParameterizedAnnotated(23).foo + def test5 = { + val o = new OuterAnnotated {} + o.foo + } } diff --git a/test/tasty/pos/src-3/tastytest/Annotated.scala b/test/tasty/pos/src-3/tastytest/Annotated.scala index cc868a7cd707..772596caca88 100644 --- a/test/tasty/pos/src-3/tastytest/Annotated.scala +++ b/test/tasty/pos/src-3/tastytest/Annotated.scala @@ -5,3 +5,20 @@ trait Annotated @rootAnnot(1) trait RootAnnotated + +trait OuterClassAnnotated extends OuterClass { + @basicAnnot(xyz) + def foo = 1 +} + +class ParameterizedAnnotated(@basicAnnot(ParameterizedAnnotated.value) x: Int) { + def foo = x +} +object ParameterizedAnnotated { + final val value = 23 +} + +trait OuterAnnotated extends OuterTrait { + @innerAnnot(new Inner) + def foo = 1 +} diff --git a/test/tasty/pos/src-3/tastytest/OuterClass.scala b/test/tasty/pos/src-3/tastytest/OuterClass.scala new file mode 100644 index 000000000000..bb597dd4198e --- /dev/null +++ b/test/tasty/pos/src-3/tastytest/OuterClass.scala @@ -0,0 +1,7 @@ +package tastytest + +class OuterClass { + + final val xyz = "xyz" + +} diff --git a/test/tasty/pos/src-3/tastytest/OuterTrait.scala b/test/tasty/pos/src-3/tastytest/OuterTrait.scala new file mode 100644 index 000000000000..d0481102f67f --- /dev/null +++ b/test/tasty/pos/src-3/tastytest/OuterTrait.scala @@ -0,0 +1,9 @@ +package tastytest + +trait OuterTrait { + + final class Inner + + final class innerAnnot(inner: Inner) extends scala.annotation.StaticAnnotation + +} diff --git a/test/tasty/pos/src-3/tastytest/basicAnnot.scala b/test/tasty/pos/src-3/tastytest/basicAnnot.scala new file mode 100644 index 000000000000..e442be700a54 --- /dev/null +++ b/test/tasty/pos/src-3/tastytest/basicAnnot.scala @@ -0,0 +1,3 @@ +package tastytest + +final class basicAnnot[T](member: T) extends scala.annotation.StaticAnnotation diff --git a/test/tasty/run/pre/tastytest/package.scala b/test/tasty/run/pre/tastytest/package.scala index 296667df5642..69094d25ba22 100644 --- a/test/tasty/run/pre/tastytest/package.scala +++ b/test/tasty/run/pre/tastytest/package.scala @@ -10,7 +10,7 @@ package object tastytest { object Macros { - def hasStaticAnnotImpl[T, A](c: Context)(implicit T: c.WeakTypeTag[T], A: c.WeakTypeTag[A]) : c.Expr[Boolean] = { + def hasStaticAnnotImpl[T, A](c: Context)(implicit T: c.WeakTypeTag[T], A: c.WeakTypeTag[A]): c.Expr[Boolean] = { import c.universe._ if (weakTypeOf[T].members.filter(_.isMethod).exists(_.annotations.exists(_.tree.tpe =:= weakTypeOf[A]))) { c.Expr[Boolean](q"true")