From 0b34be2548f1fa758ad44af1b30dbc6052a65878 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 1 Jul 2022 13:06:01 +0200 Subject: [PATCH 1/7] upgrade tasty versions --- project/DottySupport.scala | 2 +- .../scala/tools/tasty/TastyFormat.scala | 92 ++++++++----------- .../tools/tasty/TastyHeaderUnpickler.scala | 20 ++-- 3 files changed, 50 insertions(+), 64 deletions(-) diff --git a/project/DottySupport.scala b/project/DottySupport.scala index f5670cb5b366..0d61afeccc99 100644 --- a/project/DottySupport.scala +++ b/project/DottySupport.scala @@ -12,7 +12,7 @@ import sbt.librarymanagement.{ * Settings to support validation of TastyUnpickler against the release of dotty with the matching TASTy version */ object TastySupport { - val supportedTASTyRelease = "3.1.2-RC1" // TASTy version 28.1-0 + val supportedTASTyRelease = "3.2.0-RC1" // TASTy version 28.2-1 val scala3Compiler = "org.scala-lang" % "scala3-compiler_3" % supportedTASTyRelease val scala3Library = "org.scala-lang" % "scala3-library_3" % supportedTASTyRelease diff --git a/src/compiler/scala/tools/tasty/TastyFormat.scala b/src/compiler/scala/tools/tasty/TastyFormat.scala index d62fdfef6434..a967b916ee51 100644 --- a/src/compiler/scala/tools/tasty/TastyFormat.scala +++ b/src/compiler/scala/tools/tasty/TastyFormat.scala @@ -22,34 +22,34 @@ object TastyFormat { */ final val header: Array[Int] = Array(0x5C, 0xA1, 0xAB, 0x1F) - /**Natural number. Each increment of the `MajorVersion` begins a - * new series of backward compatible TASTy versions. + /** Natural number. Each increment of the `MajorVersion` begins a + * new series of backward compatible TASTy versions. * - * A TASTy file in either the preceding or succeeding series is - * incompatible with the current value. + * A TASTy file in either the preceeding or succeeding series is + * incompatible with the current value. */ final val MajorVersion: Int = 28 - /**Natural number. Each increment of the `MinorVersion`, within - * a series declared by the `MajorVersion`, breaks forward - * compatibility, but remains backwards compatible, with all - * preceding `MinorVersion`. + /** Natural number. Each increment of the `MinorVersion`, within + * a series declared by the `MajorVersion`, breaks forward + * compatibility, but remains backwards compatible, with all + * preceeding `MinorVersion`. */ - final val MinorVersion: Int = 1 + final val MinorVersion: Int = 2 - /**Natural Number. The `ExperimentalVersion` allows for - * experimentation with changes to TASTy without committing - * to any guarantees of compatibility. + /** Natural Number. The `ExperimentalVersion` allows for + * experimentation with changes to TASTy without committing + * to any guarantees of compatibility. * - * A zero value indicates that the TASTy version is from a - * stable, final release. + * A zero value indicates that the TASTy version is from a + * stable, final release. * - * A strictly positive value indicates that the TASTy - * version is experimental. An experimental TASTy file - * can only be read by a tool with the same version. - * However, tooling with an experimental TASTy version - * is able to read final TASTy documents if the file's - * `MinorVersion` is strictly less than the current value. + * A strictly positive value indicates that the TASTy + * version is experimental. An experimental TASTy file + * can only be read by a tool with the same version. + * However, tooling with an experimental TASTy version + * is able to read final TASTy documents if the file's + * `MinorVersion` is strictly less than the current value. */ final val ExperimentalVersion: Int = 0 @@ -78,30 +78,14 @@ object TastyFormat { * with an unstable TASTy version. * * We follow the given algorithm: + * * ``` - * if file.major != compiler.major then - * return incompatible - * if compiler.experimental == 0 then - * if file.experimental != 0 then - * return incompatible - * if file.minor > compiler.minor then - * return incompatible - * else - * return compatible - * else invariant[compiler.experimental != 0] - * if file.experimental == compiler.experimental then - * if file.minor == compiler.minor then - * return compatible (all fields equal) - * else - * return incompatible - * else if file.experimental == 0, - * if file.minor < compiler.minor then - * return compatible (an experimental version can read a previous released version) - * else - * return incompatible (an experimental version cannot read its own minor version or any later version) - * else invariant[file.experimental is non-0 and different than compiler.experimental] - * return incompatible + * (fileMajor, fileMinor, fileExperimental) match + * case (`compilerMajor`, `compilerMinor`, `compilerExperimental`) => true // full equality + * case (`compilerMajor`, minor, 0) if minor < compilerMinor => true // stable backwards compatibility + * case _ => false * ``` + * @syntax markdown */ def isVersionCompatible( fileMajor: Int, @@ -111,18 +95,9 @@ object TastyFormat { compilerMinor: Int, compilerExperimental: Int ): Boolean = ( - fileMajor == compilerMajor && ( - if (fileExperimental == compilerExperimental) { - if (compilerExperimental == 0) { - fileMinor <= compilerMinor - } - else { - fileMinor == compilerMinor - } - } - else { - fileExperimental == 0 && fileMinor < compilerMinor - } + fileMajor == compilerMajor && + ( fileMinor == compilerMinor && fileExperimental == compilerExperimental // full equality + || fileMinor < compilerMinor && fileExperimental == 0 // stable backwards compatibility ) ) @@ -199,11 +174,13 @@ object TastyFormat { // Cat. 1: tag final val firstSimpleTreeTag = UNITconst + // final val ??? = 1 final val UNITconst = 2 final val FALSEconst = 3 final val TRUEconst = 4 final val NULLconst = 5 final val PRIVATE = 6 + // final val ??? = 7 final val PROTECTED = 8 final val ABSTRACT = 9 final val FINAL = 10 @@ -226,6 +203,7 @@ object TastyFormat { final val CASEaccessor = 27 final val COVARIANT = 28 final val CONTRAVARIANT = 29 + // final val ??? = 30 final val HASDEFAULT = 31 final val STABLE = 32 final val MACRO = 33 @@ -301,6 +279,7 @@ object TastyFormat { final val IMPORT = 132 final val TYPEPARAM = 133 final val PARAM = 134 + // final val ??? = 135 final val APPLY = 136 final val TYPEAPPLY = 137 final val TYPED = 138 @@ -331,7 +310,9 @@ object TastyFormat { final val TYPEBOUNDS = 163 final val TYPEBOUNDStpt = 164 final val ANDtype = 165 + // final val ??? = 166 final val ORtype = 167 + // final val ??? = 168 final val POLYtype = 169 final val TYPELAMBDAtype = 170 final val LAMBDAtpt = 171 @@ -341,7 +322,8 @@ object TastyFormat { final val TYPEREFin = 175 final val SELECTin = 176 final val EXPORT = 177 - + // final val ??? = 178 + // final val ??? = 179 final val METHODtype = 180 final val MATCHtype = 190 diff --git a/src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala b/src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala index 546cdc15e23c..59535203a771 100644 --- a/src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala +++ b/src/compiler/scala/tools/tasty/TastyHeaderUnpickler.scala @@ -44,14 +44,18 @@ class TastyHeaderUnpickler(reader: TastyReader) { start } - val validVersion = TastyFormat.isVersionCompatible( - fileMajor = fileMajor, - fileMinor = fileMinor, - fileExperimental = fileExperimental, - compilerMajor = MajorVersion, - compilerMinor = MinorVersion, - compilerExperimental = ExperimentalVersion - ) + val validVersion = { + val stdCheck = TastyFormat.isVersionCompatible( + fileMajor = fileMajor, + fileMinor = fileMinor, + fileExperimental = fileExperimental, + compilerMajor = MajorVersion, + compilerMinor = MinorVersion, + compilerExperimental = ExperimentalVersion + ) + val fallback = fileMajor == 28 && fileMinor == 2 && fileExperimental == 1 // 3.2.0-RC1 + stdCheck || fallback + } check(validVersion, { val signature = signatureString(fileMajor, fileMinor, fileExperimental) From bc45492f20e236da7febf04f3b37ac97f2371c09 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 1 Jul 2022 15:43:35 +0200 Subject: [PATCH 2/7] debug failing tests --- .../scala/tools/tastytest/TastyTest.scala | 42 ++++++++++++++----- .../pos/src-2/tastytest/TestAnnotated.scala | 1 - .../tasty/pos/src-3/tastytest/Annotated.scala | 3 -- .../pos/src-3/tastytest/symbolicAnnot.scala | 3 -- .../src-3/tastytest_weirdname/package.scala | 3 -- 5 files changed, 31 insertions(+), 21 deletions(-) delete mode 100644 test/tasty/pos/src-3/tastytest/symbolicAnnot.scala delete mode 100644 test/tasty/pos/src-3/tastytest_weirdname/package.scala diff --git a/src/tastytest/scala/tools/tastytest/TastyTest.scala b/src/tastytest/scala/tools/tastytest/TastyTest.scala index ae1beacf079a..f170a29eeace 100644 --- a/src/tastytest/scala/tools/tastytest/TastyTest.scala +++ b/src/tastytest/scala/tools/tastytest/TastyTest.scala @@ -15,7 +15,8 @@ import java.io.OutputStream object TastyTest { - private[tastytest] val verbose = false + private[tastytest] val verbose = true + private[tastytest] val debug = true private def log(s: => String): Unit = if (verbose) println(s) @@ -34,9 +35,9 @@ object TastyTest { def runSuite(src: String, srcRoot: String, pkgName: String, outDir: Option[String], additionalSettings: Seq[String], additionalDottySettings: Seq[String])(implicit cl: Dotc.ClassLoader): Try[Unit] = for { (pre, src2, src3) <- getRunSources(srcRoot/src) out <- outDir.fold(tempDir(pkgName))(dir) - _ <- scalacPos(out, sourceRoot=srcRoot/src/"pre", additionalSettings, pre:_*) + _ <- scalacPos(out, individualCapable=false, sourceRoot=srcRoot/src/"pre", additionalSettings, pre:_*) _ <- dotcPos(out, sourceRoot=srcRoot/src/"src-3", additionalDottySettings, src3:_*) - _ <- scalacPos(out, sourceRoot=srcRoot/src/"src-2", additionalSettings, src2:_*) + _ <- scalacPos(out, individualCapable=true, sourceRoot=srcRoot/src/"src-2", additionalSettings, src2:_*) testNames <- visibleClasses(out, pkgName, src2:_*) _ <- runMainOn(out, testNames:_*) } yield () @@ -54,9 +55,9 @@ object TastyTest { _ = log(s"Sources to compile under test: ${src2.map(cyan).mkString(", ")}") out <- outDir.fold(tempDir(pkgName))(dir) _ <- javacPos(out, sourceRoot=srcRoot/src/"pre", filterByKind(Set(Java), pre:_*):_*) - _ <- scalacPos(out, sourceRoot=srcRoot/src/"pre", additionalSettings, filterByKind(Set(Scala), pre:_*):_*) + _ <- scalacPos(out, individualCapable=false, sourceRoot=srcRoot/src/"pre", additionalSettings, filterByKind(Set(Scala), pre:_*):_*) _ <- dotcPos(out, sourceRoot=srcRoot/src/"src-3", additionalDottySettings, src3:_*) - _ <- scalacPos(out, sourceRoot=srcRoot/src/"src-2", additionalSettings, src2:_*) + _ <- scalacPos(out, individualCapable=true, sourceRoot=srcRoot/src/"src-2", additionalSettings, src2:_*) } yield () /**Simulates a Scala 2 application that depends on a Scala 3 library, and is expected to fail compilation. @@ -88,7 +89,7 @@ object TastyTest { (src3u, src2d, src3a) <- getFullCircleSources(srcRoot/src, src3appFilters = Set(Scala, Check, SkipCheck)) out <- outDir.fold(tempDir(pkgName))(dir) _ <- dotcPos(out, sourceRoot=srcRoot/src/"src-3-upstream", additionalDottySettings, src3u:_*) - _ <- scalacPos(out, sourceRoot=srcRoot/src/"src-2-downstream", additionalSettings, src2d:_*) + _ <- scalacPos(out, individualCapable=false, sourceRoot=srcRoot/src/"src-2-downstream", additionalSettings, src2d:_*) _ <- dotcNeg(out, additionalDottySettings, src3a:_*) } yield () @@ -104,10 +105,10 @@ object TastyTest { def negChangePreSuite(src: String, srcRoot: String, pkgName: String, outDirs: Option[(String, String)], additionalSettings: Seq[String], additionalDottySettings: Seq[String])(implicit cl: Dotc.ClassLoader): Try[Unit] = for { (preA, preB, src2, src3) <- getMovePreChangeSources(srcRoot/src, src2Filters = Set(Scala, Check, SkipCheck)) (out1, out2) <- outDirs.fold(tempDir(pkgName) *> tempDir(pkgName))(p => dir(p._1) *> dir(p._2)) - _ <- scalacPos(out1, sourceRoot=srcRoot/src/"pre-A", additionalSettings, preA:_*) - _ <- scalacPos(out2, sourceRoot=srcRoot/src/"pre-B", additionalSettings, preB:_*) + _ <- scalacPos(out1, individualCapable=false, sourceRoot=srcRoot/src/"pre-A", additionalSettings, preA:_*) + _ <- scalacPos(out2, individualCapable=false, sourceRoot=srcRoot/src/"pre-B", additionalSettings, preB:_*) _ <- dotcPos(out2, out1, sourceRoot=srcRoot/src/"src-3", additionalDottySettings, src3:_*) - _ <- scalacNeg(out2, additionalSettings, src2:_*) + _ <- scalacNeg(out2,additionalSettings, src2:_*) } yield () /**Same as `negSuite`, but in addition, the Scala 3 library depends on another upstream Scala 3 library, @@ -130,9 +131,28 @@ object TastyTest { successWhen(Javac.javac(out, sources:_*))("javac failed to compile sources.") } - private def scalacPos(out: String, sourceRoot: String, additionalSettings: Seq[String], sources: String*): Try[Unit] = { + private def scalacPos(out: String, individualCapable: Boolean, sourceRoot: String, additionalSettings: Seq[String], sources: String*): Try[Unit] = { log(s"compiling sources in ${yellow(sourceRoot)} with scalac.") - successWhen(Scalac.scalac(out, "-Ytasty-reader" +: additionalSettings, sources:_*))("scalac failed to compile sources.") + val res = { + if (debug && individualCapable) { + def compileIndividual(srcs: List[String]): Try[Boolean] = { + srcs match { + case Nil => Success(true) + case src :: rest => + log(s"compiling source ${yellow(src)} with scalac.") + Scalac.scalac(out, "-Ytasty-reader" +: additionalSettings, src) match { + case Success(true) => compileIndividual(rest) + case err => err + } + } + } + compileIndividual(sources.toList) + } + else { + Scalac.scalac(out, "-Ytasty-reader" +: additionalSettings, sources:_*) + } + } + successWhen(res)("scalac failed to compile sources.") } private def scalacNeg(out: String, additionalSettings: Seq[String], files: String*): Try[Unit] = { diff --git a/test/tasty/pos/src-2/tastytest/TestAnnotated.scala b/test/tasty/pos/src-2/tastytest/TestAnnotated.scala index f7fd9abc5df6..3228d7e48321 100644 --- a/test/tasty/pos/src-2/tastytest/TestAnnotated.scala +++ b/test/tasty/pos/src-2/tastytest/TestAnnotated.scala @@ -1,7 +1,6 @@ package tastytest object TestAnnotated { - def test1 = new Annotated {} def test2 = new RootAnnotated {} def test3 = { val o = new OuterClassAnnotated {} diff --git a/test/tasty/pos/src-3/tastytest/Annotated.scala b/test/tasty/pos/src-3/tastytest/Annotated.scala index a9fe6ed2bfde..7a9780463f68 100644 --- a/test/tasty/pos/src-3/tastytest/Annotated.scala +++ b/test/tasty/pos/src-3/tastytest/Annotated.scala @@ -1,8 +1,5 @@ package tastytest -@symbolicAnnot(new tastytest_>>>.Member) -trait Annotated - @rootAnnot(1) trait RootAnnotated diff --git a/test/tasty/pos/src-3/tastytest/symbolicAnnot.scala b/test/tasty/pos/src-3/tastytest/symbolicAnnot.scala deleted file mode 100644 index a326139c06a7..000000000000 --- a/test/tasty/pos/src-3/tastytest/symbolicAnnot.scala +++ /dev/null @@ -1,3 +0,0 @@ -package tastytest - -final class symbolicAnnot(member: tastytest_>>>.Member) extends scala.annotation.StaticAnnotation diff --git a/test/tasty/pos/src-3/tastytest_weirdname/package.scala b/test/tasty/pos/src-3/tastytest_weirdname/package.scala deleted file mode 100644 index 213def3121c5..000000000000 --- a/test/tasty/pos/src-3/tastytest_weirdname/package.scala +++ /dev/null @@ -1,3 +0,0 @@ -package object tastytest_>>> { - class Member -} From 6ae46724ab2be1dc3221ff3d8f5484e98ca93ff9 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 1 Jul 2022 22:30:49 +0200 Subject: [PATCH 3/7] stop SpecializeTypes for scala3Defined Scala 3.2.0 adds the scala.deriving.Mirror.fromProductTyped method to its stdlib. This method is forced by SpecializeTypes which in turn causes a chain of forces, leading to scala.Tuple being reached in an upper bound of Mirror.ProductOf. This causes an error for any code reading an enum/case-class/sealed-trait from Scala 3. So do not force any scala 3 method in this phase, as Scala 3 does not support specialisation. Alternatives include further delaying when errors are reported for problematic definitions, (i.e. do not throw TypeError), but this appeared to have more strange knock-on effects. --- .../scala/tools/nsc/transform/SpecializeTypes.scala | 8 +++++--- src/tastytest/scala/tools/tastytest/TastyTest.scala | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 14077bb69e49..873c5f69e4dc 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala @@ -889,7 +889,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { if (ms.nonEmpty && clazz.isTrait && clazz.isInterface) clazz.resetFlag(INTERFACE) - if (normalizedMember.isMethod) { + if (normalizedMember.isMethod && !normalizedMember.isScala3Defined) { val newTpe = subst(outerEnv, normalizedMember.info) // only do it when necessary, otherwise the method type might be at a later phase already if (newTpe != normalizedMember.info) { @@ -930,7 +930,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { */ private def normalizeMember(owner: Symbol, sym: Symbol, outerEnv: TypeEnv): List[Symbol] = { sym :: ( - if (!sym.isMethod || enteringTyper(sym.typeParams.isEmpty)) Nil + if (!sym.isMethod || sym.isScala3Defined || enteringTyper(sym.typeParams.isEmpty)) Nil else if (sym.hasDefault) { /* Specializing default getters is useless, also see scala/bug#7329 . */ sym.resetFlag(SPECIALIZED) @@ -1024,7 +1024,9 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers { specMember } - if (!sym.isMethod || sym.isConstructor || hasUnspecializableAnnotation(sym) || sym.isSuperAccessor) { + if (!sym.isMethod || sym.isConstructor || hasUnspecializableAnnotation(sym) || sym.isSuperAccessor + || sym.isScala3Defined) { // Scala 3 does not have specialised methods yet. + // ) { Nil } else { val stvars = specializedTypeVars(sym) diff --git a/src/tastytest/scala/tools/tastytest/TastyTest.scala b/src/tastytest/scala/tools/tastytest/TastyTest.scala index f170a29eeace..664176296447 100644 --- a/src/tastytest/scala/tools/tastytest/TastyTest.scala +++ b/src/tastytest/scala/tools/tastytest/TastyTest.scala @@ -15,8 +15,8 @@ import java.io.OutputStream object TastyTest { - private[tastytest] val verbose = true - private[tastytest] val debug = true + private[tastytest] val verbose = false + private[tastytest] val debug = false private def log(s: => String): Unit = if (verbose) println(s) From ddf54adfdc24cf6f91dceabe0062cf65cf5b65d6 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Sat, 2 Jul 2022 00:24:29 +0200 Subject: [PATCH 4/7] adopt new constructor scheme --- .../scala/tools/nsc/tasty/TreeUnpickler.scala | 44 +++++++++++-------- .../nsc/tasty/bridge/AnnotationOps.scala | 31 ++++++++----- .../tools/nsc/tasty/bridge/ContextOps.scala | 14 +++--- .../tools/nsc/tasty/bridge/SymbolOps.scala | 21 ++++++--- .../tastytest/TestCtorUsingClauses.scala | 18 ++++++++ .../src-2/tastytest/TestSymbollicEnums.scala | 2 +- .../run/src-3/tastytest/CtorUsingClause.scala | 33 ++++++++++++++ 7 files changed, 120 insertions(+), 43 deletions(-) create mode 100644 test/tasty/run/src-2/tastytest/TestCtorUsingClauses.scala create mode 100644 test/tasty/run/src-3/tastytest/CtorUsingClause.scala diff --git a/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala b/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala index 4f38b9dd6d86..715fbf7f7896 100644 --- a/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala +++ b/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala @@ -808,16 +808,18 @@ class TreeUnpickler[Tasty <: TastyUniverse]( val supportedFlags = Extension | Exported | Infix | Given | optFlag(isMacro)(Erased) checkUnsupportedFlags(repr.unsupportedFlags &~ supportedFlags) val isCtor = sym.isConstructor - val paramDefss = readParamss()(localCtx).map(_.map(symFromNoCycle)) - val typeParams = { + val paramss = readParamss()(localCtx) + val typeClause = { // A type parameter list must be non-empty and with type symbols - val first = paramDefss.take(1) - if (first.exists(_.exists(_.isType))) first.head else Nil + val first = paramss.take(1) + if (first.exists(_.headOption.exists(nc => symFromNoCycle(nc).isType))) first.head else Nil } + val valueClauses = paramss.drop(if (typeClause.isEmpty) 0 else 1) + val typeParams = typeClause.map(symFromNoCycle) val vparamss = { + val vparamSymss = valueClauses.map(_.map(symFromNoCycle)) // A value parameter list may be empty, or filled with term symbols - val valueClauses = paramDefss.drop(if (typeParams.isEmpty) 0 else 1) - val hasTypeParams = valueClauses.exists(_.exists(_.isType)) + val hasTypeParams = vparamSymss.exists(_.headOption.exists(_.isType)) unsupportedWhen(hasTypeParams, { val noun = ( if (isCtor) "constructor" @@ -826,7 +828,7 @@ class TreeUnpickler[Tasty <: TastyUniverse]( ) s"$noun with unmergeable type parameters: $tname" }) - valueClauses + vparamSymss } val tpt = readTpt()(localCtx) if (isMacro) { @@ -838,7 +840,7 @@ class TreeUnpickler[Tasty <: TastyUniverse]( ) sym.addAnnotation(annot) } - val valueParamss = normalizeIfConstructor(vparamss, isCtor) + val valueParamss = normalizeIfConstructor(sym.enclClass, vparamss, valueClauses, isCtor) val resType = effectiveResultType(sym, tpt.tpe) ctx.setInfo(sym, defn.DefDefType(if (isCtor) Nil else typeParams, valueParamss, resType)) } @@ -891,7 +893,7 @@ class TreeUnpickler[Tasty <: TastyUniverse]( else defn.ExprType(tpt.tpe)) } - def initialize(localCtx: Context)(implicit ctx: Context): Unit = ctx.trace(traceCompletion(symAddr, sym)) { + def initialize(localCtx: Context)(implicit ctx: Context) = ctx.trace(traceCompletion(symAddr, sym)) { sym.rawInfo match { case repr: TastyRepr => tag match { @@ -900,26 +902,30 @@ class TreeUnpickler[Tasty <: TastyUniverse]( case TYPEDEF | TYPEPARAM => TypeDef(repr, localCtx) case PARAM => TermParam(repr, localCtx) } + repr.tflags case _ => // nothing to do here (assume correctly initalised) ctx.log(s"${showSym(sym)} is already initialised, in owner ${showSym(sym.owner)}") + EmptyTastyFlags } } try { val localCtx = ctx.withOwner(sym) - if (sym.isClass) { - inIndexScopedStatsContext(localCtx0 => initialize(localCtx0)(ctx))(localCtx) - } - else { - initialize(localCtx) + val tflags = { + if (sym.isClass) { + inIndexScopedStatsContext(localCtx0 => initialize(localCtx0)(ctx))(localCtx) + } + else { + initialize(localCtx) + } } - NoCycle(at = symAddr) + NoCycle(at = symAddr, tflags) } catch ctx.onCompletionError(sym) finally goto(end) } - private def traceCompletion(addr: Addr, sym: Symbol)(implicit ctx: Context) = TraceInfo[Unit]( + private def traceCompletion(addr: Addr, sym: Symbol)(implicit ctx: Context) = TraceInfo[TastyFlagSet]( query = "begin completion", qual = s"${showSym(sym)} in context ${showSym(ctx.owner)} $addr", res = _ => s"completed ${showSym(sym)}: ${showType(sym.info)}" @@ -1032,7 +1038,7 @@ class TreeUnpickler[Tasty <: TastyUniverse]( unsupportedTermTreeError("package statement") case _ => skipTree() // readTerm()(ctx.withOwner(exprOwner)) - NoCycle(at = NoAddr) + NoCycle(at = NoAddr, tflags = EmptyTastyFlags) } def readIndexedStatsAsSyms(exprOwner: Symbol, end: Addr)(implicit ctx: Context): List[NoCycle] = @@ -1331,8 +1337,8 @@ object TreeUnpickler { sealed trait MaybeCycle object MaybeCycle { - case class NoCycle(at: Addr) extends MaybeCycle - case object Tombstone extends MaybeCycle + case class NoCycle(at: Addr, tflags: TastyFlagSet) extends MaybeCycle + case object Tombstone extends MaybeCycle } /** An enumeration indicating which subtrees should be added to an OwnerTree. */ diff --git a/src/compiler/scala/tools/nsc/tasty/bridge/AnnotationOps.scala b/src/compiler/scala/tools/nsc/tasty/bridge/AnnotationOps.scala index 5641c1a3eb24..e20c918f88f3 100644 --- a/src/compiler/scala/tools/nsc/tasty/bridge/AnnotationOps.scala +++ b/src/compiler/scala/tools/nsc/tasty/bridge/AnnotationOps.scala @@ -18,16 +18,27 @@ import scala.tools.nsc.tasty.TastyUniverse trait AnnotationOps { self: TastyUniverse => import self.{symbolTable => u} - private[bridge] final def mkAnnotation(tree: Tree): u.Annotation = tree match { - case u.Apply(u.Select(u.New(tpt), u.nme.CONSTRUCTOR), args) => - u.AnnotationInfo(tpt.tpe, args, Nil) - case u.Apply(u.TypeApply(u.Select(u.New(tpt), u.nme.CONSTRUCTOR), tpargs), args) => - u.AnnotationInfo(u.appliedType(tpt.tpe, tpargs.map(_.tpe)), args, Nil) - case u.New(tpt) => - // this is to handle incorrectly formatted annotations in dotty - https://github.com/lampepfl/dotty/issues/10113 - u.AnnotationInfo(tpt.tpe, Nil, Nil) - case _ => - throw new Exception(s"unexpected annotation kind from TASTy: ${u.showRaw(tree)}") + private[bridge] final def mkAnnotation(tree: Tree): u.Annotation = { + def go(tpargs: List[Type], args: List[Tree], tree: Tree): u.Annotation = tree match { + case u.Select(u.New(tpt), u.nme.CONSTRUCTOR) => + val atp = if (tpargs.isEmpty) tpt.tpe else u.appliedType(tpt.tpe, tpargs) + u.AnnotationInfo(atp, args, Nil) + case u.TypeApply(pre, newTpArgs) if tpargs.isEmpty => + go(newTpArgs.map(_.tpe), args, pre) + case u.Apply(pre, Nil) => // skip the empty term param list + go(tpargs, args, pre) + case u.Apply(pre, newArgs) if args.isEmpty => + go(tpargs, newArgs, pre) + case _ => + throw new Exception(s"unexpected annotation kind from TASTy: ${u.showRaw(tree)}") + } + tree match { + case u.New(tpt) => + // this is to handle incorrectly formatted annotations in dotty - https://github.com/lampepfl/dotty/issues/10113 + u.AnnotationInfo(tpt.tpe, Nil, Nil) + case _ => + go(Nil, Nil, tree) + } } sealed abstract class DeferredAnnotation { diff --git a/src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala b/src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala index 8dabc199e1e9..d8cc3c8358ea 100644 --- a/src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala +++ b/src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala @@ -96,22 +96,22 @@ trait ContextOps { self: TastyUniverse => /**Perform an operation within a context that has the mode `IndexStats` will force any collected annotations * afterwards */ - def inIndexStatsContext(op: Context => Unit)(implicit ctx: Context): Unit = { + def inIndexStatsContext[T](op: Context => T)(implicit ctx: Context): T = { val statsCtx = ctx.addMode(IndexStats) - op(statsCtx) - statsCtx.initialContext.forceAnnotations() + try op(statsCtx) + finally statsCtx.initialContext.forceAnnotations() } /** Perform an operation within a context that has the mode `InnerScope` will enter any inline methods afterwards */ - def inInnerScopeContext(op: Context => Unit)(implicit ctx: Context): Unit = { + def inInnerScopeContext[T](op: Context => T)(implicit ctx: Context): T = { val innerCtx = ctx.addMode(InnerScope) - op(innerCtx) - innerCtx.initialContext.enterLatentDefs(innerCtx.owner) + try op(innerCtx) + finally innerCtx.initialContext.enterLatentDefs(innerCtx.owner) } /** an aggregate of `inInnerScopeContext` within `inIndexStatsContext` */ - def inIndexScopedStatsContext(op: Context => Unit)(implicit ctx: Context): Unit = { + def inIndexScopedStatsContext[T](op: Context => T)(implicit ctx: Context): T = { inIndexStatsContext(inInnerScopeContext(op)(_))(ctx) } diff --git a/src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala b/src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala index 8fe679433a9e..4413ef18bc6b 100644 --- a/src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala +++ b/src/compiler/scala/tools/nsc/tasty/bridge/SymbolOps.scala @@ -17,6 +17,8 @@ import scala.tools.nsc.tasty.{SafeEq, TastyUniverse, ForceKinds, TastyModes}, Ta import scala.tools.tasty.{TastyName, Signature, TastyFlags}, TastyName.SignedName, Signature.MethodSignature, TastyFlags._ import scala.tools.tasty.ErasedTypeRef +import scala.tools.nsc.tasty.TreeUnpickler.MaybeCycle.NoCycle + /**This layer deals with selecting a member symbol from a type using a `TastyName`, * also contains factories for making type references to symbols. */ @@ -121,12 +123,19 @@ trait SymbolOps { self: TastyUniverse => def symIsExperimental(sym: Symbol) = sym.hasAnnotation(defn.ExperimentalAnnotationClass) /** if isConstructor, make sure it has one non-implicit parameter list */ - def normalizeIfConstructor(termParamss: List[List[Symbol]], isConstructor: Boolean): List[List[Symbol]] = - if (isConstructor && - (termParamss.isEmpty || termParamss.head.nonEmpty && termParamss.head.head.isImplicit)) - Nil :: termParamss - else - termParamss + def normalizeIfConstructor(owner: Symbol, termParamss: List[List[Symbol]], paramClauses: List[List[NoCycle]], isConstructor: Boolean): List[List[Symbol]] = + if (!isConstructor) termParamss + else { + paramClauses match { + case (vparam :: _) :: _ if vparam.tflags.is(Implicit, butNot=Given) => Nil :: termParamss + case _ => + if (paramClauses.forall(paramClause => paramClause.nonEmpty && paramClause.head.tflags.is(Given))) { + termParamss :+ Nil + } else { + termParamss + } + } + } private[bridge] def lookupSymbol(space: Type, tname: TastyName)(implicit ctx: Context): Symbol = { deepComplete(space) diff --git a/test/tasty/run/src-2/tastytest/TestCtorUsingClauses.scala b/test/tasty/run/src-2/tastytest/TestCtorUsingClauses.scala new file mode 100644 index 000000000000..79ac9835ccf8 --- /dev/null +++ b/test/tasty/run/src-2/tastytest/TestCtorUsingClauses.scala @@ -0,0 +1,18 @@ +package tastytest + +import CtorUsingClauses._ +import CtorUsingClause._ + +object TestCtorUsingClauses extends Suite("TestCtorUsingClauses") { + // TODO: test unpickle calling a ctor with a using clause + + test(assert(new CtorUsingClause(implicitly[Int])("hello").i === 23)) + test(assert(new Contextual(implicitly[Int])().i === 23)) + + test(assert(new CtorUsingClauses.Sub1().i === 23)) + test(assert(new CtorUsingClauses.Sub2().i === 23)) + test(assert(new CtorUsingClauses.Sub3().i === 23)) + + test(assert(new CtorUsingClauses.Annotated().j === 47)) + test(assert(new CtorUsingClauses.AnnotatedOld().k === 97)) +} diff --git a/test/tasty/run/src-2/tastytest/TestSymbollicEnums.scala b/test/tasty/run/src-2/tastytest/TestSymbollicEnums.scala index 4b342cbd64ff..97654dbf9cc1 100644 --- a/test/tasty/run/src-2/tastytest/TestSymbollicEnums.scala +++ b/test/tasty/run/src-2/tastytest/TestSymbollicEnums.scala @@ -18,7 +18,7 @@ object TestSymbollicEnums extends Suite("TestSymbollicEnums") { } } - val Laws = new Laws[Long] + val Laws = new Laws[Long](implicitly[Interpreter[Long]])() import Laws._ test(assert(additiveIdentity(getRandomNat.toLong.lit))) diff --git a/test/tasty/run/src-3/tastytest/CtorUsingClause.scala b/test/tasty/run/src-3/tastytest/CtorUsingClause.scala new file mode 100644 index 000000000000..4a05f664e4eb --- /dev/null +++ b/test/tasty/run/src-3/tastytest/CtorUsingClause.scala @@ -0,0 +1,33 @@ +package tastytest + +import CtorUsingClauses.CtorUsingClause.given + +object CtorUsingClauses { + class CtorUsingClause(using x: Int)(y: String): + val i = x + + object CtorUsingClause { + given Int = 23 + } + + class Contextual(using x: Int): // ctor: (using x: Int)() + val i = x + + class Implicit(implicit x: Int): // ctor: ()(implicit x: Int) + val i = x + + class Sub1()(using x: Int) extends CtorUsingClause("Sub") + class Sub2()(using x: Int) extends Contextual + class Sub3()(using x: Int) extends Implicit + + class CtxAnnot(using x: Int) extends scala.annotation.Annotation // ctor: (using x: Int)() + class CtxAnnotOld(implicit x: Int) extends scala.annotation.Annotation // ctor: ()(implicit x: Int) + + @CtxAnnot + class Annotated: + val j = 47 + + @CtxAnnotOld + class AnnotatedOld: + val k = 97 +} From bced9061a9524f692f08d9d9edc1c9b79b566d28 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Mon, 4 Jul 2022 13:42:48 +0200 Subject: [PATCH 5/7] test new exports in extensions --- .../src-2/tastytest/TestExportsInExtensions.scala | 9 +++++++++ .../run/src-3/tastytest/ExportsInExtensions.scala | 13 +++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 test/tasty/run/src-2/tastytest/TestExportsInExtensions.scala create mode 100644 test/tasty/run/src-3/tastytest/ExportsInExtensions.scala diff --git a/test/tasty/run/src-2/tastytest/TestExportsInExtensions.scala b/test/tasty/run/src-2/tastytest/TestExportsInExtensions.scala new file mode 100644 index 000000000000..362ccf36a8c9 --- /dev/null +++ b/test/tasty/run/src-2/tastytest/TestExportsInExtensions.scala @@ -0,0 +1,9 @@ +package tastytest + + +object TestExportsInExtensions extends Suite("TestExportsInExtensions") { + test(assert(ExportsInExtensions.bar(3) == 3)) + test(assert(ExportsInExtensions.baz(17)(3) == 2)) + test(assert(ExportsInExtensions.bam(3) == 9)) + test(assert(ExportsInExtensions.::(10)(2) == 10 - 2)) // same as for implicit class C +} diff --git a/test/tasty/run/src-3/tastytest/ExportsInExtensions.scala b/test/tasty/run/src-3/tastytest/ExportsInExtensions.scala new file mode 100644 index 000000000000..6c474c416de3 --- /dev/null +++ b/test/tasty/run/src-3/tastytest/ExportsInExtensions.scala @@ -0,0 +1,13 @@ +package tastytest + +object ExportsInExtensions: + + class C(x: Int): + def bar = x + def baz(y: Int) = x % y + val bam = x * x + def :: (y: Int) = x - y + + extension (x: Int) + private def cm = new C(x) + export cm.* From d00ade62f705ca7cfb11b96d7815fd2ba0dff47f Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Mon, 4 Jul 2022 18:07:47 +0200 Subject: [PATCH 6/7] only force some annotations this is because we can get away with not forcing most annotations. Keep forcing scala.annotation.internal.Child. so a problematic annotation will stay unforced unless the user uses some macro to force all annotations on all methods. --- .../scala/tools/nsc/tasty/TreeUnpickler.scala | 2 +- .../nsc/tasty/bridge/AnnotationOps.scala | 12 +++--- .../tools/nsc/tasty/bridge/ContextOps.scala | 11 ++--- .../src-2/TestForcedIllegalAnnotations.check | 12 +++--- .../TestForcedIllegalAnnotations_fail.scala | 7 +++- .../neg/src-2/TestSelectWithTarget.check | 6 +-- .../neg/src-2/TestSelectWithTarget_fail.scala | 15 ++++--- .../neg/src-2/TestSelectWithTarget_pre.scala | 25 +++++++++++ test/tasty/pos/pre/tastytest/package.scala | 40 ++++++++++++++++++ .../pos/src-2/tastytest/TestAnnotated.scala | 35 ++++++++++++---- .../pos/src-2/tastytest/TestArrayAnnot.scala | 8 +++- .../tastytest/TestFromJavaObjectConsume.scala | 10 ++++- .../pos/src-2/tastytest/TestNestedAnnot.scala | 6 ++- .../pos/src-2/tastytest/TestReflection.scala | 8 ++++ test/tasty/run/pre/tastytest/package.scala | 42 +++++++++++++++++++ .../run/src-2/tastytest/TestArrayCtors.scala | 13 ++++++ .../tastytest/TestCtorUsingClauses.scala | 13 ++++++ .../run/src-2/tastytest/TestDefAnnots.scala | 24 +++++++++++ .../src-2/tastytest/TestJavaEnumTypes.scala | 7 ++++ .../run/src-2/tastytest/TestTupleBounds.scala | 5 +++ .../run/src-3/tastytest/TupleBounds.scala | 27 ++++++++++++ 21 files changed, 289 insertions(+), 39 deletions(-) create mode 100644 test/tasty/neg/src-2/TestSelectWithTarget_pre.scala create mode 100644 test/tasty/run/src-2/tastytest/TestTupleBounds.scala create mode 100644 test/tasty/run/src-3/tastytest/TupleBounds.scala diff --git a/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala b/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala index 715fbf7f7896..cfe93b58e707 100644 --- a/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala +++ b/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala @@ -706,7 +706,7 @@ class TreeUnpickler[Tasty <: TastyUniverse]( rdr.readTerm()(ctx) } )(annotCtx.retractMode(IndexScopedStats)) - DeferredAnnotation.fromTree(mkTree) + DeferredAnnotation.fromTree(annotSym)(mkTree) } private def traceAnnotation(annotStart: Addr, annotSym: Symbol, annotee: Symbol) = TraceInfo[Tree]( diff --git a/src/compiler/scala/tools/nsc/tasty/bridge/AnnotationOps.scala b/src/compiler/scala/tools/nsc/tasty/bridge/AnnotationOps.scala index e20c918f88f3..4a7c35e3c5fb 100644 --- a/src/compiler/scala/tools/nsc/tasty/bridge/AnnotationOps.scala +++ b/src/compiler/scala/tools/nsc/tasty/bridge/AnnotationOps.scala @@ -41,19 +41,19 @@ trait AnnotationOps { self: TastyUniverse => } } - sealed abstract class DeferredAnnotation { + sealed abstract class DeferredAnnotation(annotSym: Symbol) { - private[bridge] def eager(annotee: Symbol)(implicit ctx: Context): u.AnnotationInfo + protected def eager(annotee: Symbol)(implicit ctx: Context): u.AnnotationInfo private[bridge] final def lzy(annotee: Symbol)(implicit ctx: Context): u.LazyAnnotationInfo = { - u.AnnotationInfo.lazily(eager(annotee)) + u.AnnotationInfo.lazily(annotSym, eager(annotee)) } } object DeferredAnnotation { - def fromTree(tree: Symbol => Context => Tree): DeferredAnnotation = { - new DeferredAnnotation { - private[bridge] final def eager(annotee: Symbol)(implicit ctx: Context): u.AnnotationInfo = { + def fromTree(annotSym: Symbol)(tree: Symbol => Context => Tree): DeferredAnnotation = { + new DeferredAnnotation(annotSym) { + protected final def eager(annotee: Symbol)(implicit ctx: Context): u.AnnotationInfo = { val atree = tree(annotee)(ctx) mkAnnotation(atree) } diff --git a/src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala b/src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala index d8cc3c8358ea..5aceb8c4f774 100644 --- a/src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala +++ b/src/compiler/scala/tools/nsc/tasty/bridge/ContextOps.scala @@ -115,8 +115,9 @@ trait ContextOps { self: TastyUniverse => inIndexStatsContext(inInnerScopeContext(op)(_))(ctx) } - /**Forces lazy annotations, if one is `scala.annotation.internal.Child` then it will add the referenced type as a - * sealed child. + /**Analyses critical annotations, critical annotations will be forced as they are necessary to + * the reading of TASTy. E.g. `scala.annotation.internal.Child` is a critical annotation that + * must be forced to add its first type argument as a sealed child. */ private def analyseAnnotations(sym: Symbol)(implicit ctx: Context): Unit = { @@ -137,8 +138,7 @@ trait ContextOps { self: TastyUniverse => var problematic: List[String] = Nil for (annot <- sym.annotations) { - annot.completeInfo() - if (annot.tpe.typeSymbolDirect === defn.ChildAnnot) { + if (annot.symbol === defn.ChildAnnot) { val child = { val child0 = lookupChild(annot.tpe.typeArgs.head) if (child0 eq sym) { @@ -161,6 +161,7 @@ trait ContextOps { self: TastyUniverse => if ((annot.symbol eq defn.TargetNameAnnotationClass) || (annot.symbol eq defn.StaticMethodAnnotationClass)) { problematic ::= inOwner { implicit ctx => + annot.completeInfo() // these should be safe to force unsupportedMessage(s"annotation on $sym: @$annot") } } @@ -196,7 +197,7 @@ trait ContextOps { self: TastyUniverse => } else { log(s"eagerly adding annotations to ${showSym(sym)}") - analyseAnnotations(sym.setAnnotations(annots.map(_.eager(sym)))) + analyseAnnotations(sym.setAnnotations(annots.map(_.lzy(sym)))) } } } diff --git a/test/tasty/neg/src-2/TestForcedIllegalAnnotations.check b/test/tasty/neg/src-2/TestForcedIllegalAnnotations.check index 873dd8a9fb5b..85be887f3db5 100644 --- a/test/tasty/neg/src-2/TestForcedIllegalAnnotations.check +++ b/test/tasty/neg/src-2/TestForcedIllegalAnnotations.check @@ -1,7 +1,7 @@ -TestForcedIllegalAnnotations_fail.scala:4: error: Unsupported Scala 3 match expression in an annotation of method forcedMatchInAnnot; note that complex trees are not yet supported for Annotations; found in method forcedMatchInAnnot in class tastytest.ForcedIllegalAnnotations.Match. - def test1 = new ForcedIllegalAnnotations.Match() // error: Unsupported Scala 3 match expression in an annotation of method forcedMatchInAnnot - ^ -TestForcedIllegalAnnotations_fail.scala:5: error: Unsupported Scala 3 block expression in an annotation of method forcedBlockInAnnot; note that complex trees are not yet supported for Annotations; found in method forcedBlockInAnnot in class tastytest.ForcedIllegalAnnotations.Block. - def test2 = new ForcedIllegalAnnotations.Block() // error: Unsupported Scala 3 block expression in an annotation of method forcedBlockInAnnot - ^ +TestForcedIllegalAnnotations_fail.scala:5: error: Unsupported Scala 3 match expression in an annotation of method forcedMatchInAnnot; note that complex trees are not yet supported for Annotations; found in method forcedMatchInAnnot in class tastytest.ForcedIllegalAnnotations.Match. + new ForcedIllegalAnnotations.Match().forcedMatchInAnnot() // error: match expression in annotation arguments + ^ +TestForcedIllegalAnnotations_fail.scala:8: error: Unsupported Scala 3 block expression in an annotation of method forcedBlockInAnnot; note that complex trees are not yet supported for Annotations; found in method forcedBlockInAnnot in class tastytest.ForcedIllegalAnnotations.Block. + new ForcedIllegalAnnotations.Block().forcedBlockInAnnot() // error: block expression in annotation arguments. + ^ 2 errors diff --git a/test/tasty/neg/src-2/TestForcedIllegalAnnotations_fail.scala b/test/tasty/neg/src-2/TestForcedIllegalAnnotations_fail.scala index cf8e9ecd7c1a..15781ddbc19f 100644 --- a/test/tasty/neg/src-2/TestForcedIllegalAnnotations_fail.scala +++ b/test/tasty/neg/src-2/TestForcedIllegalAnnotations_fail.scala @@ -1,6 +1,9 @@ package tastytest object TestForcedIllegalAnnotations { - def test1 = new ForcedIllegalAnnotations.Match() // error: Unsupported Scala 3 match expression in an annotation of method forcedMatchInAnnot - def test2 = new ForcedIllegalAnnotations.Block() // error: Unsupported Scala 3 block expression in an annotation of method forcedBlockInAnnot + def test1 = + new ForcedIllegalAnnotations.Match().forcedMatchInAnnot() // error: match expression in annotation arguments + + def test2 = + new ForcedIllegalAnnotations.Block().forcedBlockInAnnot() // error: block expression in annotation arguments. } diff --git a/test/tasty/neg/src-2/TestSelectWithTarget.check b/test/tasty/neg/src-2/TestSelectWithTarget.check index 60ca319eaaf9..2d2f16343965 100644 --- a/test/tasty/neg/src-2/TestSelectWithTarget.check +++ b/test/tasty/neg/src-2/TestSelectWithTarget.check @@ -1,4 +1,4 @@ -TestSelectWithTarget_fail.scala:10: error: Unsupported Scala 3 selection of method foo with @targetName("fooString"); found in method selectFooString in object tastytest.SelectWithTarget. - def test = SelectWithTarget.selectFooString - ^ +TestSelectWithTarget_fail.scala:13: error: Unsupported Scala 3 selection of method foo with @targetName("fooString"); found in method selectFooString in object tastytest.SelectWithTarget. + def test = TestSelectWithTargetPre.forceAnnots[SelectWithTarget.type, SelectWithTarget.defAnnot] + ^ 1 error diff --git a/test/tasty/neg/src-2/TestSelectWithTarget_fail.scala b/test/tasty/neg/src-2/TestSelectWithTarget_fail.scala index 48df0224770e..4ca4829cdd2c 100644 --- a/test/tasty/neg/src-2/TestSelectWithTarget_fail.scala +++ b/test/tasty/neg/src-2/TestSelectWithTarget_fail.scala @@ -2,11 +2,14 @@ package tastytest object TestSelectWithTarget { - // We error when an annotation selects a - // method overloaded with a targetAnnot - // until we can erase them correctly. - // e.g. the annotation arguments may be - // reflected by a macro into real trees - def test = SelectWithTarget.selectFooString + // We error when an annotation selects a method overloaded with a targetAnnot + // until we can erase them correctly. e.g. the annotation arguments may be + // reflected by a macro into real trees. + + // here it is necessary to call a macro to force the `defAnnot` annotation, + // alternatively we could use the `@deprecated` annotation as the compiler + // will analyse that, but this illustrates that most annotations can be + // harmless unless we specificially need to analyse them. + def test = TestSelectWithTargetPre.forceAnnots[SelectWithTarget.type, SelectWithTarget.defAnnot] } diff --git a/test/tasty/neg/src-2/TestSelectWithTarget_pre.scala b/test/tasty/neg/src-2/TestSelectWithTarget_pre.scala new file mode 100644 index 000000000000..1d0bb5f9aa94 --- /dev/null +++ b/test/tasty/neg/src-2/TestSelectWithTarget_pre.scala @@ -0,0 +1,25 @@ +package tastytest + +import scala.language.experimental.macros + +import scala.reflect.macros.blackbox.Context + +object TestSelectWithTargetPre { + + /** forces annotations of type `A` on methods from class `T` */ + def forceAnnots[T, A]: Unit = macro Macros.forceAnnotsImpl[T, A] + + object Macros { + def forceAnnotsImpl[T, A](c: Context)(implicit T: c.WeakTypeTag[T], A: c.WeakTypeTag[A]): c.Expr[Unit] = { + import c.universe._ + for { + method <- weakTypeOf[T].members.filter(_.isMethod) + annot <- method.annotations.find(_.tree.tpe =:= weakTypeOf[A]) + } { + annot.tree + } + c.Expr[Unit](q"()") + } + } + +} diff --git a/test/tasty/pos/pre/tastytest/package.scala b/test/tasty/pos/pre/tastytest/package.scala index 179fc8aefa9d..27948135f611 100644 --- a/test/tasty/pos/pre/tastytest/package.scala +++ b/test/tasty/pos/pre/tastytest/package.scala @@ -14,8 +14,48 @@ package object tastytest { def compiletimeHasChild[T](child: String): Unit = macro Macros.hasChildImpl[T] def compiletimeHasNestedChildren[T](expected: String*): Unit = macro Macros.hasChildrenImpl[T] + /** forces annotations of type `A` on methods from class `T` */ + def forceAnnots[T, A, S <: String with Singleton]: Unit = macro Macros.AnnotsBundle.forceAnnotsImpl[T, A, S] + object Macros { + class AnnotsBundle(val c: Context) { + import c.universe._ + + private def annotType(annot: Annotation): Type = annot.tree.tpe match { + case TypeBounds(lo, hi) => hi + case tpe => tpe + } + + private def toExplore[T](implicit T: c.WeakTypeTag[T]): List[Symbol] = ( + weakTypeOf[T].typeSymbol + +: weakTypeOf[T].typeSymbol.asInstanceOf[ClassSymbol].primaryConstructor + +: weakTypeOf[T].members.filter(_.isMethod).toList.flatMap(method => + method :: method.asInstanceOf[MethodSymbol].paramLists.flatten + ) + ) + + private def stringAssert[S <: String with Singleton](implicit S: c.WeakTypeTag[S]): String = + weakTypeOf[S] match { + case ConstantType(Constant(str: String)) => str + case _ => ??? + } + + def forceAnnotsImpl[T, A, S <: String with Singleton](implicit T: c.WeakTypeTag[T], A: c.WeakTypeTag[A], S: c.WeakTypeTag[S]): c.Expr[Unit] = { + val trees = { + for { + defn <- toExplore[T] + annot <- defn.annotations.filter(annotType(_).typeSymbol == weakTypeOf[A].typeSymbol) + } yield { + s"${annot.tree}" + } + } + val annotStr = trees.head + assert(annotStr == stringAssert[S], s"actually, was $annotStr") + c.Expr[Unit](q"()") + } + } + def hasChildrenImpl[T](c: Context)(expected: c.Expr[String]*)(implicit T: c.WeakTypeTag[T]): c.Expr[Unit] = { import c.universe._ diff --git a/test/tasty/pos/src-2/tastytest/TestAnnotated.scala b/test/tasty/pos/src-2/tastytest/TestAnnotated.scala index 3228d7e48321..48f4c0631176 100644 --- a/test/tasty/pos/src-2/tastytest/TestAnnotated.scala +++ b/test/tasty/pos/src-2/tastytest/TestAnnotated.scala @@ -1,16 +1,37 @@ package tastytest object TestAnnotated { - def test2 = new RootAnnotated {} + def test2 = forceAnnots[RootAnnotated, rootAnnot, "new tastytest.rootAnnot(1)"] def test3 = { - val o = new OuterClassAnnotated {} - o.foo + forceAnnots[ + OuterClassAnnotated, + basicAnnot[String], + "new <: tastytest.basicAnnot[String](OuterClassAnnotated.this.xyz)" + ] + } + def test4 = { + forceAnnots[ + ParameterizedAnnotated, + basicAnnot[Int], + "new <: tastytest.basicAnnot[Int](tastytest#ParameterizedAnnotated.type.value)" + ] } - def test4 = new ParameterizedAnnotated(23).foo def test5 = { val o = new OuterAnnotated {} - o.foo + forceAnnots[OuterAnnotated, o.innerAnnot, "new OuterAnnotated.this.innerAnnot(new Inner())"] + } + def test6 = { + forceAnnots[ + SelectInAnnotated.AmbiguousAnnotated, + SelectInAnnotated.ambig.annot, + "new tastytest.SelectInAnnotated.ambig.annot(tastytest.SelectInAnnotated.e.type)" + ] + } + def test7 = { + forceAnnots[ + SelectInAnnotatedinParent.AmbiguousAnnotated, + SelectInAnnotatedinParent.ambig.annotBox, + "new tastytest.SelectInAnnotatedinParent.ambig.annotBox(0.0)" + ] } - def test6 = new SelectInAnnotated.AmbiguousAnnotated {} - def test7 = new SelectInAnnotatedinParent.AmbiguousAnnotated {} } diff --git a/test/tasty/pos/src-2/tastytest/TestArrayAnnot.scala b/test/tasty/pos/src-2/tastytest/TestArrayAnnot.scala index 94b03b1e4796..328bd2d213a8 100644 --- a/test/tasty/pos/src-2/tastytest/TestArrayAnnot.scala +++ b/test/tasty/pos/src-2/tastytest/TestArrayAnnot.scala @@ -1,5 +1,11 @@ package tastytest object TestArrayAnnot { - def test = new Tagged() + def test = { + forceAnnots[ + Tagged, + SuppressWarnings, + "new SuppressWarnings(Array.type.apply[String]((Array[String]{\"xyz\", \"foo\"}: String*))(reflect#ClassTag.type.apply[String](classOf[java.lang.String])))" + ] + } } diff --git a/test/tasty/pos/src-2/tastytest/TestFromJavaObjectConsume.scala b/test/tasty/pos/src-2/tastytest/TestFromJavaObjectConsume.scala index b813d87d1524..ab5725da3d1a 100644 --- a/test/tasty/pos/src-2/tastytest/TestFromJavaObjectConsume.scala +++ b/test/tasty/pos/src-2/tastytest/TestFromJavaObjectConsume.scala @@ -2,6 +2,14 @@ package tastytest object TestFromJavaObjectConsume { - def test = new FromJavaObjectConsume.Foo {} + def test1 = new FromJavaObjectConsume.Foo {} + + def test2 = { + forceAnnots[ + FromJavaObjectConsume.Foo, + basicAnnot[Any], + "new <: tastytest.basicAnnot[Int](tastytest#FromJavaObjectBox.type.id[Int](23))" + ] + } } diff --git a/test/tasty/pos/src-2/tastytest/TestNestedAnnot.scala b/test/tasty/pos/src-2/tastytest/TestNestedAnnot.scala index 72430ef31ee8..2d37a7437a60 100644 --- a/test/tasty/pos/src-2/tastytest/TestNestedAnnot.scala +++ b/test/tasty/pos/src-2/tastytest/TestNestedAnnot.scala @@ -1,5 +1,9 @@ package tastytest object TestNestedAnnot { - def test = new TaggedMega.Tagged() + def test = { + val _ = new TaggedMega.Tagged() + compiletimeHasChild[TaggedMega.ForceChildren]("tastytest.TaggedMega.Nested.Nested2.Tags") + forceAnnots[TaggedMega.Tagged, TaggedMega.Nested.Nested2.Tags, "new tastytest#TaggedMega.Nested.Nested2.Tags(\"xyz,foo\")"] + } } diff --git a/test/tasty/pos/src-2/tastytest/TestReflection.scala b/test/tasty/pos/src-2/tastytest/TestReflection.scala index 533c526ee849..bda276896cd4 100644 --- a/test/tasty/pos/src-2/tastytest/TestReflection.scala +++ b/test/tasty/pos/src-2/tastytest/TestReflection.scala @@ -2,4 +2,12 @@ package tastytest object TestReflection { def test1 = assert(new Reflection().fieldAtIndex(1) == "Hello") // test reading `classOf` in the @throws annotation + + def test2 = { + forceAnnots[ + Reflection, + scala.throws[Throwable], + "new <: throws[IndexOutOfBoundsException](classOf[java.lang.IndexOutOfBoundsException])" + ] + } } diff --git a/test/tasty/run/pre/tastytest/package.scala b/test/tasty/run/pre/tastytest/package.scala index fca544cff4fb..0bfa004bf5af 100644 --- a/test/tasty/run/pre/tastytest/package.scala +++ b/test/tasty/run/pre/tastytest/package.scala @@ -1,3 +1,5 @@ +import scala.language.experimental.macros + import scala.util.Random import scala.reflect.macros.blackbox.Context @@ -37,8 +39,48 @@ package object tastytest { def poly[T: c.WeakTypeTag]: Tree = q"${c.weakTypeOf[T].toString}" } + /** forces annotations of type `A` on methods from class `T` */ + def forceAnnots[T, A, S <: String with Singleton]: Unit = macro Macros.AnnotsBundle.forceAnnotsImpl[T, A, S] + object Macros { + class AnnotsBundle(val c: Context) { + import c.universe._ + + private def annotType(annot: Annotation): Type = annot.tree.tpe match { + case TypeBounds(lo, hi) => hi + case tpe => tpe + } + + private def toExplore[T](implicit T: c.WeakTypeTag[T]): List[Symbol] = ( + weakTypeOf[T].typeSymbol + +: weakTypeOf[T].typeSymbol.asInstanceOf[ClassSymbol].primaryConstructor + +: weakTypeOf[T].members.filter(_.isMethod).toList.flatMap(method => + method :: method.asInstanceOf[MethodSymbol].paramLists.flatten + ) + ) + + private def stringAssert[S <: String with Singleton](implicit S: c.WeakTypeTag[S]): String = + weakTypeOf[S] match { + case ConstantType(Constant(str: String)) => str + case _ => ??? + } + + def forceAnnotsImpl[T, A, S <: String with Singleton](implicit T: c.WeakTypeTag[T], A: c.WeakTypeTag[A], S: c.WeakTypeTag[S]): c.Expr[Unit] = { + val trees = { + for { + defn <- toExplore[T] + annot <- defn.annotations.filter(annotType(_).typeSymbol == weakTypeOf[A].typeSymbol) + } yield { + s"${annot.tree}" + } + } + val annotStr = trees.head + assert(annotStr == stringAssert[S], s"actually, was $annotStr") + c.Expr[Unit](q"()") + } + } + 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]))) { diff --git a/test/tasty/run/src-2/tastytest/TestArrayCtors.scala b/test/tasty/run/src-2/tastytest/TestArrayCtors.scala index e9fa35650998..549cb78bcc25 100644 --- a/test/tasty/run/src-2/tastytest/TestArrayCtors.scala +++ b/test/tasty/run/src-2/tastytest/TestArrayCtors.scala @@ -7,4 +7,17 @@ object TestArrayCtors extends Suite("TestArrayCtors") { test(assert(EmptyArrayCtor != null)) test(assert(EmptyArrayCtor2 != null)) + def compiletimeAsserts = { + def test1 = forceAnnots[ + ArrayCtors.EmptyArrayCtor.type, + ArrayCtors.arrayAnnot, + "new tastytest.ArrayCtors.arrayAnnot(Array.type.apply[tastytest.ArrayCtors.Module.type.type]((Array[tastytest.ArrayCtors.Module.type]{}: tastytest.ArrayCtors.Module.type*))(reflect#ClassTag.type.apply[tastytest.ArrayCtors.Module.type](classOf[tastytest.ArrayCtors$$Module])))" + ] + def test2 = forceAnnots[ + ArrayCtors.EmptyArrayCtor2.type, + ArrayCtors.arrayAnnot2, + "new tastytest.ArrayCtors.arrayAnnot2(Array.type.apply[Array[tastytest.ArrayCtors.Module.type.type]]((Array[Array[tastytest.ArrayCtors.Module.type]]{}: Array[tastytest.ArrayCtors.Module.type]*))(reflect#ClassTag.type.apply[tastytest.ArrayCtors.Module.type](classOf[tastytest.ArrayCtors$$Module]).wrap))" + ] + } + } diff --git a/test/tasty/run/src-2/tastytest/TestCtorUsingClauses.scala b/test/tasty/run/src-2/tastytest/TestCtorUsingClauses.scala index 79ac9835ccf8..66f34d80a690 100644 --- a/test/tasty/run/src-2/tastytest/TestCtorUsingClauses.scala +++ b/test/tasty/run/src-2/tastytest/TestCtorUsingClauses.scala @@ -15,4 +15,17 @@ object TestCtorUsingClauses extends Suite("TestCtorUsingClauses") { test(assert(new CtorUsingClauses.Annotated().j === 47)) test(assert(new CtorUsingClauses.AnnotatedOld().k === 97)) + + def compiletimeAsserts = { + def test1 = forceAnnots[ + CtorUsingClauses.Annotated, + CtorUsingClauses.CtxAnnot, + "new tastytest.CtorUsingClauses.CtxAnnot(tastytest#CtorUsingClauses.CtorUsingClause.given_Int.type)" + ] + def test2 = forceAnnots[ + CtorUsingClauses.AnnotatedOld, + CtorUsingClauses.CtxAnnotOld, + "new tastytest.CtorUsingClauses.CtxAnnotOld(tastytest#CtorUsingClauses.CtorUsingClause.given_Int.type)" + ] + } } diff --git a/test/tasty/run/src-2/tastytest/TestDefAnnots.scala b/test/tasty/run/src-2/tastytest/TestDefAnnots.scala index 55e0ff0a2a1e..fd4a2f7788cd 100644 --- a/test/tasty/run/src-2/tastytest/TestDefAnnots.scala +++ b/test/tasty/run/src-2/tastytest/TestDefAnnots.scala @@ -6,4 +6,28 @@ object TestDefAnnots extends Suite("TestDefAnnots") { test(assert(DefAnnots.withParamInferAnnot("arg") === ("arg" : Any))) test(assert(DefAnnots.withAnnotatedAnnot("arg") === ("arg": Any))) + def compiletimeAsserts = { + def test1 = { + forceAnnots[ + DefAnnots.type, + DefAnnots.inferAnnot[Any], + "new <: tastytest.DefAnnots.inferAnnot[tastytest.DefAnnots.Inner.Foo.type](tastytest.DefAnnots.Inner.type.Foo)" + ] + } + def test2 = { + forceAnnots[ + DefAnnots.type, + DefAnnots.Wrapper.annotatedAnnot, + "new tastytest.DefAnnots.Wrapper.annotatedAnnot()" + ] + } + def test3 = { + forceAnnots[ + DefAnnots.Wrapper.annotatedAnnot, + DefAnnots.Wrapper.annot, + "new tastytest.DefAnnots.Wrapper.annot()" + ] + } + } + } diff --git a/test/tasty/run/src-2/tastytest/TestJavaEnumTypes.scala b/test/tasty/run/src-2/tastytest/TestJavaEnumTypes.scala index ff571f80bc3f..b975aff96306 100644 --- a/test/tasty/run/src-2/tastytest/TestJavaEnumTypes.scala +++ b/test/tasty/run/src-2/tastytest/TestJavaEnumTypes.scala @@ -8,4 +8,11 @@ object TestJavaEnumTypes extends Suite("TestJavaEnumTypes") { test(assert(new JavaEnumTypes.TypeBox[ElementType.TYPE.type]().id(ElementType.TYPE) === ElementType.TYPE)) test(assert(JavaEnumTypes.foo === 23)) + def compiletimeAsserts = + forceAnnots[ + JavaEnumTypes.type, + JavaEnumTypes.simulatedTarget, + "new tastytest.JavaEnumTypes.simulatedTarget(Array.type.apply[java.lang.annotation.ElementType]((Array[java.lang.annotation.ElementType]{annotation#ElementType.type.LOCAL_VARIABLE}: java.lang.annotation.ElementType*))(reflect#ClassTag.type.apply[java.lang.annotation.ElementType](classOf[java.lang.annotation.ElementType])))" + ] + } diff --git a/test/tasty/run/src-2/tastytest/TestTupleBounds.scala b/test/tasty/run/src-2/tastytest/TestTupleBounds.scala new file mode 100644 index 000000000000..048ab4b80bb9 --- /dev/null +++ b/test/tasty/run/src-2/tastytest/TestTupleBounds.scala @@ -0,0 +1,5 @@ +package tastytest + +object TestTupleBounds extends Suite("TestTupleBounds") { + test(assert(TupleBounds.hello == "hello")) +} diff --git a/test/tasty/run/src-3/tastytest/TupleBounds.scala b/test/tasty/run/src-3/tastytest/TupleBounds.scala new file mode 100644 index 000000000000..fdefd7f1b671 --- /dev/null +++ b/test/tasty/run/src-3/tastytest/TupleBounds.scala @@ -0,0 +1,27 @@ +package tastytest + +/** The purpose of this test source is to see what remains unforced when + * depending on only the hello method from Scala 2. + * If any of the other definitions are forced, they will reach the tuple bound + * and fail compilation. + */ +object TupleBounds { + + def hello = "hello" + + class SomeDef[T <: scala.Tuple] // test is unforced + + type SomeOtherDef = { // test is unforced + type T <: scala.Tuple + def foo: scala.Tuple + val bar: scala.Tuple + } + + type SomeBoundedDef[T <: scala.Tuple] // test is unforced + + class TupleAnnot(t: scala.Tuple) extends scala.annotation.Annotation // test is unforced + + @TupleAnnot((1, "abc", true)) // test is unforced + def someAnnotated: scala.Tuple = ??? // test is unforced + +} From d7f0b8ba1cb8c6722ea8eac381473e67348a0ba9 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 6 Jul 2022 17:39:17 +0200 Subject: [PATCH 7/7] handle multiple argument lists for annotations --- .../nsc/tasty/bridge/AnnotationOps.scala | 33 +++++++++++++++---- .../tools/nsc/tasty/bridge/TreeOps.scala | 2 +- .../tools/nsc/tasty/bridge/TypeOps.scala | 6 ++-- .../neg/src-2/TestSelectWithTarget.check | 7 ++++ .../neg/src-2/TestSelectWithTarget_fail.scala | 5 +++ test/tasty/neg/src-3/SelectWithTarget.scala | 12 +++++++ 6 files changed, 55 insertions(+), 10 deletions(-) diff --git a/src/compiler/scala/tools/nsc/tasty/bridge/AnnotationOps.scala b/src/compiler/scala/tools/nsc/tasty/bridge/AnnotationOps.scala index 4a7c35e3c5fb..5e48e9d82e8a 100644 --- a/src/compiler/scala/tools/nsc/tasty/bridge/AnnotationOps.scala +++ b/src/compiler/scala/tools/nsc/tasty/bridge/AnnotationOps.scala @@ -18,17 +18,38 @@ import scala.tools.nsc.tasty.TastyUniverse trait AnnotationOps { self: TastyUniverse => import self.{symbolTable => u} - private[bridge] final def mkAnnotation(tree: Tree): u.Annotation = { - def go(tpargs: List[Type], args: List[Tree], tree: Tree): u.Annotation = tree match { + trait ShowKind[T] { + def showKind(annot: String, t: T)(implicit ctx: Context): String + } + + object ShowKind { + implicit object ShowSymbol extends ShowKind[u.Symbol] { + def showKind(annot: String, t: u.Symbol)(implicit ctx: Context): String = s"$annot ${location(t)}" + } + implicit object ShowType extends ShowKind[u.Type] { + def showKind(annot: String, t: u.Type)(implicit ctx: Context): String = + s"type ${showType(t, wrap = false)} $annot of ${location(ctx.owner)}" + } + } + + private[bridge] final def mkAnnotation[T: ShowKind](tree: Tree, annotee: T)(implicit ctx: Context): u.Annotation = { + def go(tpargs: List[Type], args: List[List[Tree]], tree: Tree): u.Annotation = tree match { case u.Select(u.New(tpt), u.nme.CONSTRUCTOR) => val atp = if (tpargs.isEmpty) tpt.tpe else u.appliedType(tpt.tpe, tpargs) - u.AnnotationInfo(atp, args, Nil) + if (args.lengthIs > 1) { + val soFar = s"@${atp.typeSymbol.name.toString}${args.map(_.mkString("(", ", ", ")")).mkString("")}" + u.reporter.warning(u.NoPosition, + "Implementation limitation: multiple argument lists on annotations are\n"+ + "currently not supported; ignoring arguments " + args(1) + " on\n"+ + s"${implicitly[ShowKind[T]].showKind(soFar, annotee)}") + } + u.AnnotationInfo(atp, args.headOption.getOrElse(Nil), Nil) case u.TypeApply(pre, newTpArgs) if tpargs.isEmpty => go(newTpArgs.map(_.tpe), args, pre) case u.Apply(pre, Nil) => // skip the empty term param list go(tpargs, args, pre) - case u.Apply(pre, newArgs) if args.isEmpty => - go(tpargs, newArgs, pre) + case u.Apply(pre, newArgs) => + go(tpargs, newArgs :: args, pre) case _ => throw new Exception(s"unexpected annotation kind from TASTy: ${u.showRaw(tree)}") } @@ -55,7 +76,7 @@ trait AnnotationOps { self: TastyUniverse => new DeferredAnnotation(annotSym) { protected final def eager(annotee: Symbol)(implicit ctx: Context): u.AnnotationInfo = { val atree = tree(annotee)(ctx) - mkAnnotation(atree) + mkAnnotation(atree, annotee) } } } diff --git a/src/compiler/scala/tools/nsc/tasty/bridge/TreeOps.scala b/src/compiler/scala/tools/nsc/tasty/bridge/TreeOps.scala index 82d82af03a44..4220be32eb08 100644 --- a/src/compiler/scala/tools/nsc/tasty/bridge/TreeOps.scala +++ b/src/compiler/scala/tools/nsc/tasty/bridge/TreeOps.scala @@ -166,7 +166,7 @@ trait TreeOps { self: TastyUniverse => } } - def Annotated(tpt: Tree, annot: Tree): Tree = { + def Annotated(tpt: Tree, annot: Tree)(implicit ctx: Context): Tree = { if (annot.tpe.typeSymbol === defn.RepeatedAnnot && tpt.tpe.typeSymbol.isSubClass(u.definitions.SeqClass) && tpt.tpe.typeArgs.length == 1) { diff --git a/src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala b/src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala index 9231bcef98b2..fb96df3cd711 100644 --- a/src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala +++ b/src/compiler/scala/tools/nsc/tasty/bridge/TypeOps.scala @@ -223,9 +223,9 @@ trait TypeOps { self: TastyUniverse => def IntersectionType(tps: Type*): Type = u.intersectionType(tps.toList) def IntersectionType(tps: List[Type]): Type = u.intersectionType(tps) - def AnnotatedType(tpe: Type, annot: Tree): Type = tpe match { - case u.AnnotatedType(annots, tpe) => u.AnnotatedType(annots :+ mkAnnotation(annot), tpe) - case _ => u.AnnotatedType(mkAnnotation(annot) :: Nil , tpe) + def AnnotatedType(tpe: Type, annot: Tree)(implicit ctx: Context): Type = tpe match { + case u.AnnotatedType(annots, tpe) => u.AnnotatedType(annots :+ mkAnnotation(annot, tpe), tpe) + case _ => u.AnnotatedType(mkAnnotation(annot, tpe) :: Nil , tpe) } def SuperType(thisTpe: Type, superTpe: Type): Type = u.SuperType(thisTpe, superTpe) diff --git a/test/tasty/neg/src-2/TestSelectWithTarget.check b/test/tasty/neg/src-2/TestSelectWithTarget.check index 2d2f16343965..398d235c3e13 100644 --- a/test/tasty/neg/src-2/TestSelectWithTarget.check +++ b/test/tasty/neg/src-2/TestSelectWithTarget.check @@ -1,4 +1,11 @@ TestSelectWithTarget_fail.scala:13: error: Unsupported Scala 3 selection of method foo with @targetName("fooString"); found in method selectFooString in object tastytest.SelectWithTarget. def test = TestSelectWithTargetPre.forceAnnots[SelectWithTarget.type, SelectWithTarget.defAnnot] ^ +warning: Implementation limitation: multiple argument lists on annotations are +currently not supported; ignoring arguments List(23) on +@mctorAnnot("a")(23) method methodWithAnnotMultipleParams in class tastytest.SelectWithTarget.MultiParamsAnnot +warning: Implementation limitation: multiple argument lists on annotations are +currently not supported; ignoring arguments List(23) on +type scala.Int @mctorAnnot("a")(23) of method foo in object tastytest.SelectWithTarget.MultiParamsAnnotTpe +2 warnings 1 error diff --git a/test/tasty/neg/src-2/TestSelectWithTarget_fail.scala b/test/tasty/neg/src-2/TestSelectWithTarget_fail.scala index 4ca4829cdd2c..9ef0dc27ca0d 100644 --- a/test/tasty/neg/src-2/TestSelectWithTarget_fail.scala +++ b/test/tasty/neg/src-2/TestSelectWithTarget_fail.scala @@ -12,4 +12,9 @@ object TestSelectWithTarget { // harmless unless we specificially need to analyse them. def test = TestSelectWithTargetPre.forceAnnots[SelectWithTarget.type, SelectWithTarget.defAnnot] + + def test2 = TestSelectWithTargetPre.forceAnnots[SelectWithTarget.MultiParamsAnnot, SelectWithTarget.mctorAnnot] + + def test3 = SelectWithTarget.MultiParamsAnnotTpe.foo + } diff --git a/test/tasty/neg/src-3/SelectWithTarget.scala b/test/tasty/neg/src-3/SelectWithTarget.scala index 42bf105ea222..73c556ca8699 100644 --- a/test/tasty/neg/src-3/SelectWithTarget.scala +++ b/test/tasty/neg/src-3/SelectWithTarget.scala @@ -6,9 +6,21 @@ object SelectWithTarget { class defAnnot(arg: Any) extends StaticAnnotation + class mctorAnnot(s: String)(i: Int) extends scala.annotation.Annotation + @defAnnot(Overloads.foo("hi")) def selectFooString: Int = 23 + class MultiParamsAnnot { + @mctorAnnot("a")(23) + def methodWithAnnotMultipleParams = 47 + } + + object MultiParamsAnnotTpe { + def foo: Int @mctorAnnot("a")(23) = 47 + } + + object Overloads { @targetName("fooString")