From 165c0aba171223f6fffa2e58f2d1633e38d422f0 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Mon, 30 Sep 2024 12:48:38 +0200 Subject: [PATCH 01/35] SIP 61 - copy phase and annotation from com-lihaoyi/unroll Co-authored-by: Jamie Thompson Co-authored-by: Li Haoyi --- compiler/src/dotty/tools/dotc/Compiler.scala | 1 + .../dotty/tools/dotc/core/Definitions.scala | 1 + .../tools/dotc/transform/UnrollDefs.scala | 278 ++++++++++++++++++ library/src/scala/annotation/unroll.scala | 4 + 4 files changed, 284 insertions(+) create mode 100644 compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala create mode 100644 library/src/scala/annotation/unroll.scala diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index d8ba1ab5dc2e..24a492b4fe77 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -61,6 +61,7 @@ class Compiler { List(new InstrumentCoverage) :: // Perform instrumentation for code coverage (if -coverage-out is set) List(new CrossVersionChecks, // Check issues related to deprecated and experimental new FirstTransform, // Some transformations to put trees into a canonical form + new UnrollDefs, // Unroll annotated methods new CheckReentrant, // Internal use only: Check that compiled program has no data races involving global vars new ElimPackagePrefixes, // Eliminate references to package prefixes in Select nodes new CookComments, // Cook the comments: expand variables, doc, etc. diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 2890bdf306be..4a136b4cea95 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1036,6 +1036,7 @@ class Definitions { @tu lazy val MigrationAnnot: ClassSymbol = requiredClass("scala.annotation.migration") @tu lazy val NowarnAnnot: ClassSymbol = requiredClass("scala.annotation.nowarn") @tu lazy val UnusedAnnot: ClassSymbol = requiredClass("scala.annotation.unused") + @tu lazy val UnrollAnnot: ClassSymbol = requiredClass("scala.annotation.unroll") @tu lazy val TransparentTraitAnnot: ClassSymbol = requiredClass("scala.annotation.transparentTrait") @tu lazy val NativeAnnot: ClassSymbol = requiredClass("scala.native") @tu lazy val RepeatedAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Repeated") diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala new file mode 100644 index 000000000000..f2e2800ee127 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala @@ -0,0 +1,278 @@ +package dotty.tools.dotc.transform + +import dotty.tools.dotc.* +import core.* +import MegaPhase.MiniPhase +import Contexts.* +import Symbols.* +import Flags.* +import SymDenotations.* +import Decorators.* +import ast.Trees.* +import ast.tpd +import StdNames.nme +import Names.* +import Constants.Constant +import dotty.tools.dotc.core.NameKinds.DefaultGetterName +import dotty.tools.dotc.core.Types.{MethodType, NamedType, PolyType, Type} +import dotty.tools.dotc.core.Symbols + +import scala.language.implicitConversions + +class UnrollDefs extends MiniPhase { + import tpd._ + + val phaseName = "unroll" + + override val runsAfter = Set(FirstTransform.name) + + def copyParam(p: ValDef, parent: Symbol)(using Context) = { + implicitly[Context].typeAssigner.assignType( + cpy.ValDef(p)(p.name, p.tpt, p.rhs), + Symbols.newSymbol(parent, p.name, p.symbol.flags, p.symbol.info) + ) + } + + def copyParam2(p: TypeDef, parent: Symbol)(using Context) = { + implicitly[Context].typeAssigner.assignType( + cpy.TypeDef(p)(p.name, p.rhs), + Symbols.newSymbol(parent, p.name, p.symbol.flags, p.symbol.info) + ) + } + + def findUnrollAnnotations(params: List[Symbol])(using Context): List[Int] = { + params + .zipWithIndex + .collect { + case (v, i) if v.annotations.exists(_.symbol.fullName.toString == "scala.annotation.unroll") => + i + } + } + def isTypeClause(p: ParamClause) = p.headOption.exists(_.isInstanceOf[TypeDef]) + def generateSingleForwarder(defdef: DefDef, + prevMethodType: Type, + paramIndex: Int, + nextParamIndex: Int, + nextSymbol: Symbol, + annotatedParamListIndex: Int, + paramLists: List[ParamClause], + isCaseApply: Boolean) + (using Context) = { + + def truncateMethodType0(tpe: Type, n: Int): Type = { + tpe match{ + case pt: PolyType => PolyType(pt.paramNames, pt.paramInfos, truncateMethodType0(pt.resType, n + 1)) + case mt: MethodType => + if (n == annotatedParamListIndex) MethodType(mt.paramInfos.take(paramIndex), mt.resType) + else MethodType(mt.paramInfos, truncateMethodType0(mt.resType, n + 1)) + } + } + + val truncatedMethodType = truncateMethodType0(prevMethodType, 0) + val forwarderDefSymbol = Symbols.newSymbol( + defdef.symbol.owner, + defdef.name, + defdef.symbol.flags &~ + HasDefaultParams &~ + (if (nextParamIndex == -1) Flags.EmptyFlags else Deferred) | + Invisible, + truncatedMethodType + ) + + val newParamLists: List[ParamClause] = paramLists.zipWithIndex.map{ case (ps, i) => + if (i == annotatedParamListIndex) ps.take(paramIndex).map(p => copyParam(p.asInstanceOf[ValDef], forwarderDefSymbol)) + else { + if (isTypeClause(ps)) ps.map(p => copyParam2(p.asInstanceOf[TypeDef], forwarderDefSymbol)) + else ps.map(p => copyParam(p.asInstanceOf[ValDef], forwarderDefSymbol)) + } + } + + val defaultOffset = paramLists + .iterator + .take(annotatedParamListIndex) + .filter(!isTypeClause(_)) + .map(_.size) + .sum + + val defaultCalls = Range(paramIndex, nextParamIndex).map(n => + val inner = if (defdef.symbol.isConstructor) { + ref(defdef.symbol.owner.companionModule) + .select(DefaultGetterName(defdef.name, n + defaultOffset)) + } else if (isCaseApply) { + ref(defdef.symbol.owner.companionModule) + .select(DefaultGetterName(termName(""), n + defaultOffset)) + } else { + This(defdef.symbol.owner.asClass) + .select(DefaultGetterName(defdef.name, n + defaultOffset)) + } + + newParamLists + .take(annotatedParamListIndex) + .map(_.map(p => ref(p.symbol))) + .foldLeft[Tree](inner){ + case (lhs: Tree, newParams) => + if (newParams.headOption.exists(_.isInstanceOf[TypeTree])) TypeApply(lhs, newParams) + else Apply(lhs, newParams) + } + ) + + val forwarderInner: Tree = This(defdef.symbol.owner.asClass).select(nextSymbol) + + val forwarderCallArgs = + newParamLists.zipWithIndex.map{case (ps, i) => + if (i == annotatedParamListIndex) ps.map(p => ref(p.symbol)).take(nextParamIndex) ++ defaultCalls + else ps.map(p => ref(p.symbol)) + } + + lazy val forwarderCall0 = forwarderCallArgs.foldLeft[Tree](forwarderInner){ + case (lhs: Tree, newParams) => + if (newParams.headOption.exists(_.isInstanceOf[TypeTree])) TypeApply(lhs, newParams) + else Apply(lhs, newParams) + } + + lazy val forwarderCall = + if (!defdef.symbol.isConstructor) forwarderCall0 + else Block(List(forwarderCall0), Literal(Constant(()))) + + val forwarderDef = implicitly[Context].typeAssigner.assignType( + cpy.DefDef(defdef)( + name = forwarderDefSymbol.name, + paramss = newParamLists, + tpt = defdef.tpt, + rhs = if (nextParamIndex == -1) EmptyTree else forwarderCall + ), + forwarderDefSymbol + ) + + forwarderDef + } + + def generateFromProduct(startParamIndices: List[Int], paramCount: Int, defdef: DefDef)(using Context) = { + cpy.DefDef(defdef)( + name = defdef.name, + paramss = defdef.paramss, + tpt = defdef.tpt, + rhs = Match( + ref(defdef.paramss.head.head.asInstanceOf[ValDef].symbol).select(termName("productArity")), + startParamIndices.map { paramIndex => + val Apply(select, args) = defdef.rhs: @unchecked + CaseDef( + Literal(Constant(paramIndex)), + EmptyTree, + Apply( + select, + args.take(paramIndex) ++ + Range(paramIndex, paramCount).map(n => + ref(defdef.symbol.owner.companionModule) + .select(DefaultGetterName(defdef.symbol.owner.primaryConstructor.name.toTermName, n)) + ) + ) + ) + } ++ Seq( + CaseDef( + EmptyTree, + EmptyTree, + defdef.rhs + ) + ) + ) + ).setDefTree + } + + def generateSyntheticDefs(tree: Tree)(using Context): (Option[Symbol], Seq[Tree]) = tree match{ + case defdef: DefDef if defdef.paramss.nonEmpty => + import dotty.tools.dotc.core.NameOps.isConstructorName + + val isCaseCopy = + defdef.name.toString == "copy" && defdef.symbol.owner.is(CaseClass) + + val isCaseApply = + defdef.name.toString == "apply" && defdef.symbol.owner.companionClass.is(CaseClass) + + val isCaseFromProduct = defdef.name.toString == "fromProduct" && defdef.symbol.owner.companionClass.is(CaseClass) + + val annotated = + if (isCaseCopy) defdef.symbol.owner.primaryConstructor + else if (isCaseApply) defdef.symbol.owner.companionClass.primaryConstructor + else if (isCaseFromProduct) defdef.symbol.owner.companionClass.primaryConstructor + else defdef.symbol + + + annotated + .paramSymss + .zipWithIndex + .flatMap{case (paramClause, paramClauseIndex) => + val annotationIndices = findUnrollAnnotations(paramClause) + if (annotationIndices.isEmpty) None + else Some((paramClauseIndex, annotationIndices)) + } match{ + case Nil => (None, Nil) + case Seq((paramClauseIndex, annotationIndices)) => + val paramCount = annotated.paramSymss(paramClauseIndex).size + if (isCaseFromProduct) { + (Some(defdef.symbol), Seq(generateFromProduct(annotationIndices, paramCount, defdef))) + } else { + if (defdef.symbol.is(Deferred)){ + ( + Some(defdef.symbol), + (-1 +: annotationIndices :+ paramCount).sliding(2).toList.foldLeft((Seq.empty[DefDef], defdef.symbol))((m, v) => ((m, v): @unchecked) match { + case ((defdefs, nextSymbol), Seq(paramIndex, nextParamIndex)) => + val forwarder = generateSingleForwarder( + defdef, + defdef.symbol.info, + nextParamIndex, + paramIndex, + nextSymbol, + paramClauseIndex, + defdef.paramss, + isCaseApply + ) + (forwarder +: defdefs, forwarder.symbol) + })._1 + ) + + }else{ + + ( + None, + (annotationIndices :+ paramCount).sliding(2).toList.reverse.foldLeft((Seq.empty[DefDef], defdef.symbol))((m, v) => ((m, v): @unchecked) match { + case ((defdefs, nextSymbol), Seq(paramIndex, nextParamIndex)) => + val forwarder = generateSingleForwarder( + defdef, + defdef.symbol.info, + paramIndex, + nextParamIndex, + nextSymbol, + paramClauseIndex, + defdef.paramss, + isCaseApply + ) + (forwarder +: defdefs, forwarder.symbol) + })._1 + ) + } + } + + case multiple => sys.error("Cannot have multiple parameter lists containing `@unroll` annotation") + } + + case _ => (None, Nil) + } + + override def transformTemplate(tmpl: tpd.Template)(using Context): tpd.Tree = { + + val (removed0, generatedDefs) = tmpl.body.map(generateSyntheticDefs).unzip + val (_, generatedConstr) = generateSyntheticDefs(tmpl.constr) + val removed = removed0.flatten + + super.transformTemplate( + cpy.Template(tmpl)( + tmpl.constr, + tmpl.parents, + tmpl.derived, + tmpl.self, + tmpl.body.filter(t => !removed.contains(t.symbol)) ++ generatedDefs.flatten ++ generatedConstr + ) + ) + } +} diff --git a/library/src/scala/annotation/unroll.scala b/library/src/scala/annotation/unroll.scala new file mode 100644 index 000000000000..93327f7dcb86 --- /dev/null +++ b/library/src/scala/annotation/unroll.scala @@ -0,0 +1,4 @@ +package scala.annotation + +@experimental("under review as part of SIP-61") +final class unroll extends scala.annotation.StaticAnnotation From 63119f792bda315f168b4fd731b97f1d6299ce56 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Mon, 30 Sep 2024 12:48:56 +0200 Subject: [PATCH 02/35] SIP 61 - add compilation test, fix missing symbols --- .../tools/dotc/transform/UnrollDefs.scala | 1 + tests/run/unroll-caseclass.check | 37 ++++++++ tests/run/unroll-caseclass/Test_4.scala | 11 +++ tests/run/unroll-caseclass/unrolledV1_1.scala | 61 ++++++++++++ tests/run/unroll-caseclass/unrolledV2_2.scala | 55 +++++++++++ tests/run/unroll-caseclass/unrolledV3_3.scala | 95 +++++++++++++++++++ 6 files changed, 260 insertions(+) create mode 100644 tests/run/unroll-caseclass.check create mode 100644 tests/run/unroll-caseclass/Test_4.scala create mode 100644 tests/run/unroll-caseclass/unrolledV1_1.scala create mode 100644 tests/run/unroll-caseclass/unrolledV2_2.scala create mode 100644 tests/run/unroll-caseclass/unrolledV3_3.scala diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala index f2e2800ee127..5fcf6f0ba9ab 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala @@ -86,6 +86,7 @@ class UnrollDefs extends MiniPhase { else ps.map(p => copyParam(p.asInstanceOf[ValDef], forwarderDefSymbol)) } } + forwarderDefSymbol.setParamssFromDefs(newParamLists) val defaultOffset = paramLists .iterator diff --git a/tests/run/unroll-caseclass.check b/tests/run/unroll-caseclass.check new file mode 100644 index 000000000000..3d0ebd182b9f --- /dev/null +++ b/tests/run/unroll-caseclass.check @@ -0,0 +1,37 @@ +=== Unrolled Test V1 === +Assertion passed: found "cow1" + "true0" +Assertion passed: found "cow2" + "true0" +Assertion passed: found "cow1" + "true0" +Assertion passed: found "cow2" + "true0" +Assertion passed: found "cow1" + "true0" +Assertion passed: found "cow2" + "true0" +Assertion passed: found "hello31337" + "true0" +=== Unrolled Test V2 === +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" +Assertion passed: found "hello31337false" + "0" +=== Unrolled Test V3 === +Assertion passed: found "hello31337false12345" +as expected, no constructor for Unrolled(s: String) +public example.Unrolled(java.lang.String,int,boolean,long) +public example.Unrolled(java.lang.String,int) +public example.Unrolled(java.lang.String,int,boolean) +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false9" +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false9" +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false9" diff --git a/tests/run/unroll-caseclass/Test_4.scala b/tests/run/unroll-caseclass/Test_4.scala new file mode 100644 index 000000000000..3040e9133652 --- /dev/null +++ b/tests/run/unroll-caseclass/Test_4.scala @@ -0,0 +1,11 @@ +//> using options -experimental +import example.* +// !! IMPORTANT: If you remove this test, also remove unroll-caseclass.check +@main def Test(): Unit = { + println("=== Unrolled Test V1 ===") + UnrollTestMainV1.main(Array.empty) + println("=== Unrolled Test V2 ===") + UnrollTestMainV2.main(Array.empty) + println("=== Unrolled Test V3 ===") + UnrollTestMainV3.main(Array.empty) +} diff --git a/tests/run/unroll-caseclass/unrolledV1_1.scala b/tests/run/unroll-caseclass/unrolledV1_1.scala new file mode 100644 index 000000000000..a07e8499442e --- /dev/null +++ b/tests/run/unroll-caseclass/unrolledV1_1.scala @@ -0,0 +1,61 @@ +//> using options -experimental +package example +// !! IMPORTANT: If you remove this test, also remove unroll-caseclass.check + +// import scala.annotation.unroll <- v1 did not need to unroll yet + +// v1 of Unrolled +case class Unrolled(s: String, n: Int = 1) { + def foo = s + n +} + +// v1: original code that compiled against v1 of Unrolled +object UnrollTestMainV1 extends TestUtil { + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled("cow").foo, "cow1") + logAssertStartsWith(new Unrolled("cow", 2).foo, "cow2") + + logAssertStartsWith(Unrolled("cow").foo, "cow1") + logAssertStartsWith(Unrolled("cow", 2).foo, "cow2") + + val unrolled = Unrolled("cow") + + logAssertStartsWith(unrolled.copy(s = "cow").foo, "cow1") + logAssertStartsWith(unrolled.copy(s = "cow", n = 2).foo, "cow2") + + val Unrolled(s, n) = unrolled + + assert(s == "cow") + assert(n == 1) + + UnrollTestScalaSpecificV1.test() + } +} + +object UnrollTestScalaSpecificV1 extends TestUtil { + def test() = { + val unrolled = summon[scala.deriving.Mirror.Of[Unrolled]].fromProduct( + new Product{ + def canEqual(that: Any) = true + def productArity = 2 + def productElement(n: Int) = n match{ + case 0 => "hello" + case 1 => 31337 + } + } + ) + + logAssertStartsWith(unrolled.foo, "hello31337") + } +} + +trait TestUtil { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/tests/run/unroll-caseclass/unrolledV2_2.scala b/tests/run/unroll-caseclass/unrolledV2_2.scala new file mode 100644 index 000000000000..e1affa387652 --- /dev/null +++ b/tests/run/unroll-caseclass/unrolledV2_2.scala @@ -0,0 +1,55 @@ +//> using options -experimental +package example +// !! IMPORTANT: If you remove this test, also remove unroll-caseclass.check + +import scala.annotation.unroll + +// v2 of Unrolled +case class Unrolled(s: String, n: Int = 1, @unroll b: Boolean = true){ + def foo = s + n + b +} + +// v2: ammendments to code that exercise a new parameter +object UnrollTestMainV2 extends TestUtil { + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled("cow").foo, "cow1true") + logAssertStartsWith(new Unrolled("cow", 2).foo, "cow2true") + logAssertStartsWith(new Unrolled("cow", 2, false).foo, "cow2false") + + logAssertStartsWith(Unrolled("cow").foo, "cow1true") + logAssertStartsWith(Unrolled("cow", 2).foo, "cow2true") + logAssertStartsWith(Unrolled("cow", 2, false).foo, "cow2false") + + val unrolled = Unrolled("cow") + + logAssertStartsWith(unrolled.copy(s = "cow").foo, "cow1true") + logAssertStartsWith(unrolled.copy(s = "cow", n = 2).foo, "cow2true") + logAssertStartsWith(unrolled.copy(s = "cow", n = 2, b = false).foo, "cow2false") + + val Unrolled(s, n, b) = unrolled + + assert(s == "cow") + assert(n == 1) + assert(b == true) + + UnrollTestScalaSpecificV2.test() + } +} + +object UnrollTestScalaSpecificV2 extends TestUtil { + def test() = { + val unrolled = summon[scala.deriving.Mirror.Of[Unrolled]].fromProduct( + new Product { + def canEqual(that: Any) = true + def productArity = 3 + def productElement(n: Int) = n match { + case 0 => "hello" + case 1 => 31337 + case 2 => false + } + } + + ) + logAssertStartsWith(unrolled.foo, "hello31337false") + } +} diff --git a/tests/run/unroll-caseclass/unrolledV3_3.scala b/tests/run/unroll-caseclass/unrolledV3_3.scala new file mode 100644 index 000000000000..9bb2d66a0039 --- /dev/null +++ b/tests/run/unroll-caseclass/unrolledV3_3.scala @@ -0,0 +1,95 @@ +//> using options -experimental +package example +// !! IMPORTANT: If you remove this test, also remove unroll-caseclass.check + +import scala.annotation.unroll + +// v3 of Unrolled +case class Unrolled(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0){ + def foo = s + n + b + l +} + +// v3: ammendments to code that exercise a new parameter +object UnrollTestMainV3 extends TestUtil { + def main(args: Array[String]): Unit = { + UnrollTestScalaSpecificV3() + UnrollTestPlatformSpecificV3() + + logAssertStartsWith(new Unrolled("cow").foo, "cow1true0") + logAssertStartsWith(new Unrolled("cow", 2).foo, "cow2true0") + logAssertStartsWith(new Unrolled("cow", 2, false).foo, "cow2false0") + logAssertStartsWith(new Unrolled("cow", 2, false, 9L).foo, "cow2false9") + + logAssertStartsWith(Unrolled("cow").foo, "cow1true0") + logAssertStartsWith(Unrolled("cow", 2).foo, "cow2true0") + logAssertStartsWith(Unrolled("cow", 2, false).foo, "cow2false0") + logAssertStartsWith(Unrolled("cow", 2, false, 9L).foo, "cow2false9") + + val unrolled = Unrolled("cow") + + logAssertStartsWith(unrolled.copy(s = "cow").foo, "cow1true0") + logAssertStartsWith(unrolled.copy(s = "cow", n = 2).foo, "cow2true0") + logAssertStartsWith(unrolled.copy(s = "cow", n = 2, b = false).foo, "cow2false0") + logAssertStartsWith(unrolled.copy(s = "cow", n = 2, b = false, l = 9L).foo, "cow2false9") + + val Unrolled(s, n, b, l) = unrolled + + assert(s == "cow") + assert(n == 1) + assert(b == true) + assert(l == 0L) + + + } +} + +object UnrollTestScalaSpecificV3 extends TestUtil { + def apply() = { + val unrolled = summon[scala.deriving.Mirror.Of[Unrolled]].fromProduct( + new Product { + def canEqual(that: Any) = true + def productArity = 4 + def productElement(n: Int) = n match { + case 0 => "hello" + case 1 => 31337 + case 2 => false + case 3 => 12345L + } + } + ) + + logAssertStartsWith(unrolled.foo, "hello31337false12345") + } +} + +object UnrollTestPlatformSpecificV3 extends TestUtil { + def apply() = { + val cls = classOf[Unrolled] + + assert(scala.util.Try(cls.getConstructor(classOf[String])).isFailure) + println("as expected, no constructor for Unrolled(s: String)") + assert( + cls.getConstructor(classOf[String], classOf[Int]) + .newInstance("hello", 2: Integer) + .asInstanceOf[Unrolled] + .foo == + "hello2true0" + ) + assert( + cls.getConstructor(classOf[String], classOf[Int], classOf[Boolean]) + .newInstance("hello", 2: Integer, java.lang.Boolean.FALSE) + .asInstanceOf[Unrolled] + .foo == + "hello2false0" + ) + assert( + cls.getConstructor(classOf[String], classOf[Int], classOf[Boolean], classOf[Long]) + .newInstance("hello", 2: Integer, java.lang.Boolean.FALSE, 3: Integer) + .asInstanceOf[Unrolled] + .foo == + "hello2false3" + ) + + cls.getConstructors.foreach(println) + } +} From 97497741cc976e6f7c5cdfb19f66b3dc365fe394 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Mon, 30 Sep 2024 15:26:42 +0200 Subject: [PATCH 03/35] SIP 61 - add sbt scripted test template --- sbt-test/unroll-annot/caseclass/build.sbt | 48 +++++++++++++++++++ .../project/DottyInjectedPlugin.scala | 12 +++++ sbt-test/unroll-annot/caseclass/test | 14 ++++++ .../utils/src/main/scala/TestUtils.scala | 12 +++++ .../v1/src/main/scala/Unrolled.scala | 5 ++ .../src/main/scala/UnrollTestMain.scala | 24 ++++++++++ .../main/scala/UnrollTestScalaSpecific.scala | 20 ++++++++ .../v2/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 29 +++++++++++ .../main/scala/UnrollTestScalaSpecific.scala | 21 ++++++++ .../v3/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 35 ++++++++++++++ .../scala/UnrollTestPlatformSpecific.scala | 33 +++++++++++++ .../main/scala/UnrollTestScalaSpecific.scala | 20 ++++++++ sbt-test/unroll-annot/classMethod/build.sbt | 48 +++++++++++++++++++ .../project/DottyInjectedPlugin.scala | 12 +++++ sbt-test/unroll-annot/classMethod/test | 14 ++++++ .../utils/src/main/scala/TestUtils.scala | 12 +++++ .../v1/src/main/scala/Unrolled.scala | 5 ++ .../src/main/scala/UnrollTestMain.scala | 9 ++++ .../v2/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 11 +++++ .../v3/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 14 ++++++ .../scala/UnrollTestPlatformSpecific.scala | 30 ++++++++++++ sbt-test/unroll-annot/curriedMethod/build.sbt | 48 +++++++++++++++++++ .../project/DottyInjectedPlugin.scala | 12 +++++ sbt-test/unroll-annot/curriedMethod/test | 14 ++++++ .../utils/src/main/scala/TestUtils.scala | 12 +++++ .../v1/src/main/scala/Unrolled.scala | 5 ++ .../src/main/scala/UnrollTestMain.scala | 9 ++++ .../v2/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 11 +++++ .../v3/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 14 ++++++ .../scala/UnrollTestPlatformSpecific.scala | 29 +++++++++++ .../unroll-annot/methodWithImplicit/build.sbt | 48 +++++++++++++++++++ .../project/DottyInjectedPlugin.scala | 12 +++++ sbt-test/unroll-annot/methodWithImplicit/test | 14 ++++++ .../utils/src/main/scala/TestUtils.scala | 12 +++++ .../v1/src/main/scala/Unrolled.scala | 5 ++ .../src/main/scala/UnrollTestMain.scala | 10 ++++ .../v2/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 12 +++++ .../v3/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 15 ++++++ .../scala/UnrollTestPlatformSpecific.scala | 29 +++++++++++ sbt-test/unroll-annot/objectMethod/build.sbt | 48 +++++++++++++++++++ .../project/DottyInjectedPlugin.scala | 12 +++++ sbt-test/unroll-annot/objectMethod/test | 14 ++++++ .../utils/src/main/scala/TestUtils.scala | 12 +++++ .../v1/src/main/scala/Unrolled.scala | 6 +++ .../src/main/scala/UnrollTestMain.scala | 10 ++++ .../v2/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 11 +++++ .../v3/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 14 ++++++ .../scala/UnrollTestPlatformSpecific.scala | 48 +++++++++++++++++++ .../unroll-annot/primaryConstructor/build.sbt | 48 +++++++++++++++++++ .../project/DottyInjectedPlugin.scala | 12 +++++ sbt-test/unroll-annot/primaryConstructor/test | 14 ++++++ .../utils/src/main/scala/TestUtils.scala | 12 +++++ .../v1/src/main/scala/Unrolled.scala | 5 ++ .../src/main/scala/UnrollTestMain.scala | 10 ++++ .../v2/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 11 +++++ .../v3/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 14 ++++++ .../scala/UnrollTestPlatformSpecific.scala | 33 +++++++++++++ .../secondParameterList/build.sbt | 48 +++++++++++++++++++ .../project/DottyInjectedPlugin.scala | 12 +++++ .../unroll-annot/secondParameterList/test | 14 ++++++ .../utils/src/main/scala/TestUtils.scala | 12 +++++ .../v1/src/main/scala/Unrolled.scala | 5 ++ .../src/main/scala/UnrollTestMain.scala | 9 ++++ .../v2/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 11 +++++ .../v3/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 14 ++++++ .../scala/UnrollTestPlatformSpecific.scala | 30 ++++++++++++ .../secondaryConstructor/build.sbt | 48 +++++++++++++++++++ .../project/DottyInjectedPlugin.scala | 12 +++++ .../unroll-annot/secondaryConstructor/test | 14 ++++++ .../utils/src/main/scala/TestUtils.scala | 12 +++++ .../v1/src/main/scala/Unrolled.scala | 9 ++++ .../src/main/scala/UnrollTestMain.scala | 10 ++++ .../v2/src/main/scala/Unrolled.scala | 12 +++++ .../src/main/scala/UnrollTestMain.scala | 11 +++++ .../v3/src/main/scala/Unrolled.scala | 12 +++++ .../src/main/scala/UnrollTestMain.scala | 14 ++++++ .../scala/UnrollTestPlatformSpecific.scala | 33 +++++++++++++ sbt-test/unroll-annot/traitMethod/build.sbt | 48 +++++++++++++++++++ .../project/DottyInjectedPlugin.scala | 12 +++++ sbt-test/unroll-annot/traitMethod/test | 14 ++++++ .../utils/src/main/scala/TestUtils.scala | 12 +++++ .../v1/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 14 ++++++ .../v2/src/main/scala/Unrolled.scala | 9 ++++ .../src/main/scala/UnrollTestMain.scala | 16 +++++++ .../v3/src/main/scala/Unrolled.scala | 9 ++++ .../src/main/scala/UnrollTestMain.scala | 20 ++++++++ .../scala/UnrollTestPlatformSpecific.scala | 28 +++++++++++ 102 files changed, 1702 insertions(+) create mode 100644 sbt-test/unroll-annot/caseclass/build.sbt create mode 100644 sbt-test/unroll-annot/caseclass/project/DottyInjectedPlugin.scala create mode 100644 sbt-test/unroll-annot/caseclass/test create mode 100644 sbt-test/unroll-annot/caseclass/utils/src/main/scala/TestUtils.scala create mode 100644 sbt-test/unroll-annot/caseclass/v1/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/caseclass/v1_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/caseclass/v1_app/src/main/scala/UnrollTestScalaSpecific.scala create mode 100644 sbt-test/unroll-annot/caseclass/v2/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/caseclass/v2_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/caseclass/v2_app/src/main/scala/UnrollTestScalaSpecific.scala create mode 100644 sbt-test/unroll-annot/caseclass/v3/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala create mode 100644 sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestScalaSpecific.scala create mode 100644 sbt-test/unroll-annot/classMethod/build.sbt create mode 100644 sbt-test/unroll-annot/classMethod/project/DottyInjectedPlugin.scala create mode 100644 sbt-test/unroll-annot/classMethod/test create mode 100644 sbt-test/unroll-annot/classMethod/utils/src/main/scala/TestUtils.scala create mode 100644 sbt-test/unroll-annot/classMethod/v1/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/classMethod/v1_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/classMethod/v2/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/classMethod/v2_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/classMethod/v3/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/classMethod/v3_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/classMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala create mode 100644 sbt-test/unroll-annot/curriedMethod/build.sbt create mode 100644 sbt-test/unroll-annot/curriedMethod/project/DottyInjectedPlugin.scala create mode 100644 sbt-test/unroll-annot/curriedMethod/test create mode 100644 sbt-test/unroll-annot/curriedMethod/utils/src/main/scala/TestUtils.scala create mode 100644 sbt-test/unroll-annot/curriedMethod/v1/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/curriedMethod/v1_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/curriedMethod/v2/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/curriedMethod/v2_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/curriedMethod/v3/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/curriedMethod/v3_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/curriedMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala create mode 100644 sbt-test/unroll-annot/methodWithImplicit/build.sbt create mode 100644 sbt-test/unroll-annot/methodWithImplicit/project/DottyInjectedPlugin.scala create mode 100644 sbt-test/unroll-annot/methodWithImplicit/test create mode 100644 sbt-test/unroll-annot/methodWithImplicit/utils/src/main/scala/TestUtils.scala create mode 100644 sbt-test/unroll-annot/methodWithImplicit/v1/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/methodWithImplicit/v1_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/methodWithImplicit/v2/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/methodWithImplicit/v2_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/methodWithImplicit/v3/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/methodWithImplicit/v3_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/methodWithImplicit/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala create mode 100644 sbt-test/unroll-annot/objectMethod/build.sbt create mode 100644 sbt-test/unroll-annot/objectMethod/project/DottyInjectedPlugin.scala create mode 100644 sbt-test/unroll-annot/objectMethod/test create mode 100644 sbt-test/unroll-annot/objectMethod/utils/src/main/scala/TestUtils.scala create mode 100644 sbt-test/unroll-annot/objectMethod/v1/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/objectMethod/v1_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/objectMethod/v2/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/objectMethod/v2_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/objectMethod/v3/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/objectMethod/v3_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/objectMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala create mode 100644 sbt-test/unroll-annot/primaryConstructor/build.sbt create mode 100644 sbt-test/unroll-annot/primaryConstructor/project/DottyInjectedPlugin.scala create mode 100644 sbt-test/unroll-annot/primaryConstructor/test create mode 100644 sbt-test/unroll-annot/primaryConstructor/utils/src/main/scala/TestUtils.scala create mode 100644 sbt-test/unroll-annot/primaryConstructor/v1/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/primaryConstructor/v1_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/primaryConstructor/v2/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/primaryConstructor/v2_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/primaryConstructor/v3/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/primaryConstructor/v3_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/primaryConstructor/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala create mode 100644 sbt-test/unroll-annot/secondParameterList/build.sbt create mode 100644 sbt-test/unroll-annot/secondParameterList/project/DottyInjectedPlugin.scala create mode 100644 sbt-test/unroll-annot/secondParameterList/test create mode 100644 sbt-test/unroll-annot/secondParameterList/utils/src/main/scala/TestUtils.scala create mode 100644 sbt-test/unroll-annot/secondParameterList/v1/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/secondParameterList/v1_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/secondParameterList/v2/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/secondParameterList/v2_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/secondParameterList/v3/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/secondParameterList/v3_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/secondParameterList/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala create mode 100644 sbt-test/unroll-annot/secondaryConstructor/build.sbt create mode 100644 sbt-test/unroll-annot/secondaryConstructor/project/DottyInjectedPlugin.scala create mode 100644 sbt-test/unroll-annot/secondaryConstructor/test create mode 100644 sbt-test/unroll-annot/secondaryConstructor/utils/src/main/scala/TestUtils.scala create mode 100644 sbt-test/unroll-annot/secondaryConstructor/v1/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/secondaryConstructor/v1_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/secondaryConstructor/v2/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/secondaryConstructor/v2_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/secondaryConstructor/v3/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/secondaryConstructor/v3_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/secondaryConstructor/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala create mode 100644 sbt-test/unroll-annot/traitMethod/build.sbt create mode 100644 sbt-test/unroll-annot/traitMethod/project/DottyInjectedPlugin.scala create mode 100644 sbt-test/unroll-annot/traitMethod/test create mode 100644 sbt-test/unroll-annot/traitMethod/utils/src/main/scala/TestUtils.scala create mode 100644 sbt-test/unroll-annot/traitMethod/v1/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/traitMethod/v1_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/traitMethod/v2/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/traitMethod/v2_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/traitMethod/v3/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/traitMethod/v3_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/traitMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala diff --git a/sbt-test/unroll-annot/caseclass/build.sbt b/sbt-test/unroll-annot/caseclass/build.sbt new file mode 100644 index 000000000000..0568d222bf5a --- /dev/null +++ b/sbt-test/unroll-annot/caseclass/build.sbt @@ -0,0 +1,48 @@ +lazy val utils = project.in(file("utils")) + +lazy val sharedSettings = Seq( + scalacOptions ++= Seq("-Ycheck:all", "-experimental") +) + +lazy val v1 = project.in(file("v1")) + .settings(sharedSettings) + +lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Compile / unmanagedClasspath := Seq( + Attributed.blank((v1 / Compile / classDirectory).value) + ), + ) + +lazy val v2 = project.in(file("v2")) + .settings(sharedSettings) + +lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value) + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v2 / Compile / classDirectory).value) + ), + ) + +lazy val v3 = project.in(file("v3")) + .settings(sharedSettings) + +lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value), + // add v2_app, compiled against v2, to the classpath + Attributed.blank((v2_app / Runtime / classDirectory).value), + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v3 / Compile / classDirectory).value) + ), + ) diff --git a/sbt-test/unroll-annot/caseclass/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/caseclass/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..69f15d168bfc --- /dev/null +++ b/sbt-test/unroll-annot/caseclass/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-source:3.0-migration" + ) +} diff --git a/sbt-test/unroll-annot/caseclass/test b/sbt-test/unroll-annot/caseclass/test new file mode 100644 index 000000000000..e8e500857b0e --- /dev/null +++ b/sbt-test/unroll-annot/caseclass/test @@ -0,0 +1,14 @@ +# compile and run a basic version of Unrolled (v1), and an app that uses it +> v1/compile +> v1_app/runMain unroll.UnrollTestMainV1 +# add a field to the case class (v2), and update the app to use it, +# and ensure the old version (v1) still links +> v2/compile +> v2_app/runMain unroll.UnrollTestMainV1 +> v2_app/runMain unroll.UnrollTestMainV2 +# add a field to the case class (v3), and update the app to use it, +# and ensure the old versions (v1, v2) still link +> v3/compile +> v3_app/runMain unroll.UnrollTestMainV1 +> v3_app/runMain unroll.UnrollTestMainV2 +> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/caseclass/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/caseclass/utils/src/main/scala/TestUtils.scala new file mode 100644 index 000000000000..6ac413c9fe98 --- /dev/null +++ b/sbt-test/unroll-annot/caseclass/utils/src/main/scala/TestUtils.scala @@ -0,0 +1,12 @@ +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/sbt-test/unroll-annot/caseclass/v1/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/caseclass/v1/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..997ae2f2dc00 --- /dev/null +++ b/sbt-test/unroll-annot/caseclass/v1/src/main/scala/Unrolled.scala @@ -0,0 +1,5 @@ +package unroll + +case class Unrolled(s: String, n: Int = 1){ + def foo = s + n +} diff --git a/sbt-test/unroll-annot/caseclass/v1_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/caseclass/v1_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..e0b058ad0230 --- /dev/null +++ b/sbt-test/unroll-annot/caseclass/v1_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,24 @@ +package unroll +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV1{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled("cow").foo, "cow1") + logAssertStartsWith(new Unrolled("cow", 2).foo, "cow2") + + logAssertStartsWith(Unrolled("cow").foo, "cow1") + logAssertStartsWith(Unrolled("cow", 2).foo, "cow2") + + val unrolled = Unrolled("cow") + + logAssertStartsWith(unrolled.copy(s = "cow").foo, "cow1") + logAssertStartsWith(unrolled.copy(s = "cow", n = 2).foo, "cow2") + + val Unrolled(s, n) = unrolled + + assert(s == "cow") + assert(n == 1) + + UnrollTestScalaSpecificV1.test() + } +} diff --git a/sbt-test/unroll-annot/caseclass/v1_app/src/main/scala/UnrollTestScalaSpecific.scala b/sbt-test/unroll-annot/caseclass/v1_app/src/main/scala/UnrollTestScalaSpecific.scala new file mode 100644 index 000000000000..514905a741f4 --- /dev/null +++ b/sbt-test/unroll-annot/caseclass/v1_app/src/main/scala/UnrollTestScalaSpecific.scala @@ -0,0 +1,20 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestScalaSpecificV1{ + def test() = { + val unrolled = Unrolled.fromProduct( + new Product{ + def canEqual(that: Any) = true + def productArity = 2 + def productElement(n: Int) = n match{ + case 0 => "hello" + case 1 => 31337 + } + } + ) + + logAssertStartsWith(unrolled.foo, "hello31337") + } +} diff --git a/sbt-test/unroll-annot/caseclass/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/caseclass/v2/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..916c44550a13 --- /dev/null +++ b/sbt-test/unroll-annot/caseclass/v2/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +case class Unrolled(s: String, n: Int = 1, @unroll b: Boolean = true){ + def foo = s + n + b +} diff --git a/sbt-test/unroll-annot/caseclass/v2_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/caseclass/v2_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..c266a5f8c88e --- /dev/null +++ b/sbt-test/unroll-annot/caseclass/v2_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,29 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV2{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled("cow").foo, "cow1true") + logAssertStartsWith(new Unrolled("cow", 2).foo, "cow2true") + logAssertStartsWith(new Unrolled("cow", 2, false).foo, "cow2false") + + logAssertStartsWith(Unrolled("cow").foo, "cow1true") + logAssertStartsWith(Unrolled("cow", 2).foo, "cow2true") + logAssertStartsWith(Unrolled("cow", 2, false).foo, "cow2false") + + val unrolled = Unrolled("cow") + + logAssertStartsWith(unrolled.copy(s = "cow").foo, "cow1true") + logAssertStartsWith(unrolled.copy(s = "cow", n = 2).foo, "cow2true") + logAssertStartsWith(unrolled.copy(s = "cow", n = 2, b = false).foo, "cow2false") + + val Unrolled(s, n, b) = unrolled + + assert(s == "cow") + assert(n == 1) + assert(b == true) + + UnrollTestScalaSpecificV2.test() + } +} diff --git a/sbt-test/unroll-annot/caseclass/v2_app/src/main/scala/UnrollTestScalaSpecific.scala b/sbt-test/unroll-annot/caseclass/v2_app/src/main/scala/UnrollTestScalaSpecific.scala new file mode 100644 index 000000000000..88ead065de6e --- /dev/null +++ b/sbt-test/unroll-annot/caseclass/v2_app/src/main/scala/UnrollTestScalaSpecific.scala @@ -0,0 +1,21 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestScalaSpecificV2{ + def test() = { + val unrolled = Unrolled.fromProduct( + new Product { + def canEqual(that: Any) = true + def productArity = 3 + def productElement(n: Int) = n match { + case 0 => "hello" + case 1 => 31337 + case 2 => false + } + } + + ) + logAssertStartsWith(unrolled.foo, "hello31337false") + } +} \ No newline at end of file diff --git a/sbt-test/unroll-annot/caseclass/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/caseclass/v3/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..f1bf8c01ad2a --- /dev/null +++ b/sbt-test/unroll-annot/caseclass/v3/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +case class Unrolled(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0){ + def foo = s + n + b + l +} diff --git a/sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..a58303a6bdad --- /dev/null +++ b/sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,35 @@ +package unroll +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV3{ + def main(args: Array[String]): Unit = { + UnrollTestScalaSpecificV3() + UnrollTestPlatformSpecificV3() + + logAssertStartsWith(new Unrolled("cow").foo, "cow1true0") + logAssertStartsWith(new Unrolled("cow", 2).foo, "cow2true0") + logAssertStartsWith(new Unrolled("cow", 2, false).foo, "cow2false0") + logAssertStartsWith(new Unrolled("cow", 2, false, 9L).foo, "cow2false9") + + logAssertStartsWith(Unrolled("cow").foo, "cow1true0") + logAssertStartsWith(Unrolled("cow", 2).foo, "cow2true0") + logAssertStartsWith(Unrolled("cow", 2, false).foo, "cow2false0") + logAssertStartsWith(Unrolled("cow", 2, false, 9L).foo, "cow2false9") + + val unrolled = Unrolled("cow") + + logAssertStartsWith(unrolled.copy(s = "cow").foo, "cow1true0") + logAssertStartsWith(unrolled.copy(s = "cow", n = 2).foo, "cow2true0") + logAssertStartsWith(unrolled.copy(s = "cow", n = 2, b = false).foo, "cow2false0") + logAssertStartsWith(unrolled.copy(s = "cow", n = 2, b = false, l = 9L).foo, "cow2false9") + + val Unrolled(s, n, b, l) = unrolled + + assert(s == "cow") + assert(n == 1) + assert(b == true) + assert(l == 0L) + + + } +} diff --git a/sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala new file mode 100644 index 000000000000..07dee69cd8a7 --- /dev/null +++ b/sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala @@ -0,0 +1,33 @@ +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + val cls = classOf[Unrolled] + + assert(scala.util.Try(cls.getConstructor(classOf[String])).isFailure) + println() + assert( + cls.getConstructor(classOf[String], classOf[Int]) + .newInstance("hello", 2: Integer) + .asInstanceOf[Unrolled] + .foo == + "hello2true0" + ) + assert( + cls.getConstructor(classOf[String], classOf[Int], classOf[Boolean]) + .newInstance("hello", 2: Integer, java.lang.Boolean.FALSE) + .asInstanceOf[Unrolled] + .foo == + "hello2false0" + ) + assert( + cls.getConstructor(classOf[String], classOf[Int], classOf[Boolean], classOf[Long]) + .newInstance("hello", 2: Integer, java.lang.Boolean.FALSE, 3: Integer) + .asInstanceOf[Unrolled] + .foo == + "hello2false3" + ) + + cls.getConstructors.foreach(println) + } +} \ No newline at end of file diff --git a/sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestScalaSpecific.scala b/sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestScalaSpecific.scala new file mode 100644 index 000000000000..13d4fffe7f62 --- /dev/null +++ b/sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestScalaSpecific.scala @@ -0,0 +1,20 @@ +package unroll +import unroll.TestUtils.logAssertStartsWith +object UnrollTestScalaSpecificV3{ + def apply() = { + val unrolled = Unrolled.fromProduct( + new Product { + def canEqual(that: Any) = true + def productArity = 4 + def productElement(n: Int) = n match { + case 0 => "hello" + case 1 => 31337 + case 2 => false + case 3 => 12345L + } + } + ) + + logAssertStartsWith(unrolled.foo, "hello31337false12345") + } +} \ No newline at end of file diff --git a/sbt-test/unroll-annot/classMethod/build.sbt b/sbt-test/unroll-annot/classMethod/build.sbt new file mode 100644 index 000000000000..0568d222bf5a --- /dev/null +++ b/sbt-test/unroll-annot/classMethod/build.sbt @@ -0,0 +1,48 @@ +lazy val utils = project.in(file("utils")) + +lazy val sharedSettings = Seq( + scalacOptions ++= Seq("-Ycheck:all", "-experimental") +) + +lazy val v1 = project.in(file("v1")) + .settings(sharedSettings) + +lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Compile / unmanagedClasspath := Seq( + Attributed.blank((v1 / Compile / classDirectory).value) + ), + ) + +lazy val v2 = project.in(file("v2")) + .settings(sharedSettings) + +lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value) + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v2 / Compile / classDirectory).value) + ), + ) + +lazy val v3 = project.in(file("v3")) + .settings(sharedSettings) + +lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value), + // add v2_app, compiled against v2, to the classpath + Attributed.blank((v2_app / Runtime / classDirectory).value), + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v3 / Compile / classDirectory).value) + ), + ) diff --git a/sbt-test/unroll-annot/classMethod/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/classMethod/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..69f15d168bfc --- /dev/null +++ b/sbt-test/unroll-annot/classMethod/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-source:3.0-migration" + ) +} diff --git a/sbt-test/unroll-annot/classMethod/test b/sbt-test/unroll-annot/classMethod/test new file mode 100644 index 000000000000..e8e500857b0e --- /dev/null +++ b/sbt-test/unroll-annot/classMethod/test @@ -0,0 +1,14 @@ +# compile and run a basic version of Unrolled (v1), and an app that uses it +> v1/compile +> v1_app/runMain unroll.UnrollTestMainV1 +# add a field to the case class (v2), and update the app to use it, +# and ensure the old version (v1) still links +> v2/compile +> v2_app/runMain unroll.UnrollTestMainV1 +> v2_app/runMain unroll.UnrollTestMainV2 +# add a field to the case class (v3), and update the app to use it, +# and ensure the old versions (v1, v2) still link +> v3/compile +> v3_app/runMain unroll.UnrollTestMainV1 +> v3_app/runMain unroll.UnrollTestMainV2 +> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/classMethod/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/classMethod/utils/src/main/scala/TestUtils.scala new file mode 100644 index 000000000000..6ac413c9fe98 --- /dev/null +++ b/sbt-test/unroll-annot/classMethod/utils/src/main/scala/TestUtils.scala @@ -0,0 +1,12 @@ +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/sbt-test/unroll-annot/classMethod/v1/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/classMethod/v1/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..638bcfdeb96d --- /dev/null +++ b/sbt-test/unroll-annot/classMethod/v1/src/main/scala/Unrolled.scala @@ -0,0 +1,5 @@ +package unroll + +class Unrolled{ + def foo(s: String) = s +} diff --git a/sbt-test/unroll-annot/classMethod/v1_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/classMethod/v1_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..f36e9dd07fed --- /dev/null +++ b/sbt-test/unroll-annot/classMethod/v1_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,9 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV1{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled().foo("cow"), "cow") + } +} diff --git a/sbt-test/unroll-annot/classMethod/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/classMethod/v2/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..e46d36c230c0 --- /dev/null +++ b/sbt-test/unroll-annot/classMethod/v2/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +class Unrolled{ + def foo(s: String, @unroll n: Int = 1, b: Boolean = true) = s + n + b +} diff --git a/sbt-test/unroll-annot/classMethod/v2_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/classMethod/v2_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..91b54aa9742c --- /dev/null +++ b/sbt-test/unroll-annot/classMethod/v2_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,11 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV2{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled().foo("cow"), "cow1true") + logAssertStartsWith(new Unrolled().foo("cow", 2), "cow2true") + logAssertStartsWith(new Unrolled().foo("cow", 2, false), "cow2false") + } +} diff --git a/sbt-test/unroll-annot/classMethod/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/classMethod/v3/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..5f0b76a799d4 --- /dev/null +++ b/sbt-test/unroll-annot/classMethod/v3/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +class Unrolled{ + def foo(s: String, @unroll n: Int = 1, b: Boolean = true, @unroll l: Long = 0) = s + n + b + l +} diff --git a/sbt-test/unroll-annot/classMethod/v3_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/classMethod/v3_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..9a6daa3a9210 --- /dev/null +++ b/sbt-test/unroll-annot/classMethod/v3_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,14 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV3{ + def main(args: Array[String]): Unit = { + UnrollTestPlatformSpecificV3() + + logAssertStartsWith(new Unrolled().foo("cow"), "cow1true0") + logAssertStartsWith(new Unrolled().foo("cow", 2), "cow2true0") + logAssertStartsWith(new Unrolled().foo("cow", 2, false), "cow2false0") + logAssertStartsWith(new Unrolled().foo("cow", 2, false, 3), "cow2false3") + } +} diff --git a/sbt-test/unroll-annot/classMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/sbt-test/unroll-annot/classMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala new file mode 100644 index 000000000000..e8527808befd --- /dev/null +++ b/sbt-test/unroll-annot/classMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala @@ -0,0 +1,30 @@ +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + val instance = new Unrolled() + val cls = classOf[Unrolled] + + assert( + cls.getMethod("foo", classOf[String]).invoke(instance, "hello") == + "hello1true0" + ) + + // Only generate unrolled methods for annotated params + // (b: Boolean) is not annotated so this method should not exist + assert(scala.util.Try(cls.getMethod("foo", classOf[String], classOf[Int])).isFailure) + + assert( + cls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean]) + .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE) == + "hello2false0" + ) + assert( + cls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean], classOf[Long]) + .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE, 3: Integer) == + "hello2false3" + ) + + cls.getMethods.filter(_.getName.contains("foo")).foreach(println) + } +} \ No newline at end of file diff --git a/sbt-test/unroll-annot/curriedMethod/build.sbt b/sbt-test/unroll-annot/curriedMethod/build.sbt new file mode 100644 index 000000000000..0568d222bf5a --- /dev/null +++ b/sbt-test/unroll-annot/curriedMethod/build.sbt @@ -0,0 +1,48 @@ +lazy val utils = project.in(file("utils")) + +lazy val sharedSettings = Seq( + scalacOptions ++= Seq("-Ycheck:all", "-experimental") +) + +lazy val v1 = project.in(file("v1")) + .settings(sharedSettings) + +lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Compile / unmanagedClasspath := Seq( + Attributed.blank((v1 / Compile / classDirectory).value) + ), + ) + +lazy val v2 = project.in(file("v2")) + .settings(sharedSettings) + +lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value) + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v2 / Compile / classDirectory).value) + ), + ) + +lazy val v3 = project.in(file("v3")) + .settings(sharedSettings) + +lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value), + // add v2_app, compiled against v2, to the classpath + Attributed.blank((v2_app / Runtime / classDirectory).value), + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v3 / Compile / classDirectory).value) + ), + ) diff --git a/sbt-test/unroll-annot/curriedMethod/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/curriedMethod/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..69f15d168bfc --- /dev/null +++ b/sbt-test/unroll-annot/curriedMethod/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-source:3.0-migration" + ) +} diff --git a/sbt-test/unroll-annot/curriedMethod/test b/sbt-test/unroll-annot/curriedMethod/test new file mode 100644 index 000000000000..e8e500857b0e --- /dev/null +++ b/sbt-test/unroll-annot/curriedMethod/test @@ -0,0 +1,14 @@ +# compile and run a basic version of Unrolled (v1), and an app that uses it +> v1/compile +> v1_app/runMain unroll.UnrollTestMainV1 +# add a field to the case class (v2), and update the app to use it, +# and ensure the old version (v1) still links +> v2/compile +> v2_app/runMain unroll.UnrollTestMainV1 +> v2_app/runMain unroll.UnrollTestMainV2 +# add a field to the case class (v3), and update the app to use it, +# and ensure the old versions (v1, v2) still link +> v3/compile +> v3_app/runMain unroll.UnrollTestMainV1 +> v3_app/runMain unroll.UnrollTestMainV2 +> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/curriedMethod/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/curriedMethod/utils/src/main/scala/TestUtils.scala new file mode 100644 index 000000000000..6ac413c9fe98 --- /dev/null +++ b/sbt-test/unroll-annot/curriedMethod/utils/src/main/scala/TestUtils.scala @@ -0,0 +1,12 @@ +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/sbt-test/unroll-annot/curriedMethod/v1/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/curriedMethod/v1/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..e508d4345313 --- /dev/null +++ b/sbt-test/unroll-annot/curriedMethod/v1/src/main/scala/Unrolled.scala @@ -0,0 +1,5 @@ +package unroll + +class Unrolled{ + def foo(s: String)(f: String => String) = f(s) +} diff --git a/sbt-test/unroll-annot/curriedMethod/v1_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/curriedMethod/v1_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..8d1ca388477b --- /dev/null +++ b/sbt-test/unroll-annot/curriedMethod/v1_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,9 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV1{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled().foo("cow")(identity), "cow") + } +} diff --git a/sbt-test/unroll-annot/curriedMethod/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/curriedMethod/v2/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..843dbaa73e16 --- /dev/null +++ b/sbt-test/unroll-annot/curriedMethod/v2/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +class Unrolled{ + def foo(s: String, @unroll n: Int = 1, b: Boolean = true)(f: String => String) = f(s + n + b) +} diff --git a/sbt-test/unroll-annot/curriedMethod/v2_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/curriedMethod/v2_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..66ad8fdf8698 --- /dev/null +++ b/sbt-test/unroll-annot/curriedMethod/v2_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,11 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV2{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled().foo("cow")(identity), "cow1true") + logAssertStartsWith(new Unrolled().foo("cow", 2)(identity), "cow2true") + logAssertStartsWith(new Unrolled().foo("cow", 2, false)(identity), "cow2false") + } +} diff --git a/sbt-test/unroll-annot/curriedMethod/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/curriedMethod/v3/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..01c34e9548cf --- /dev/null +++ b/sbt-test/unroll-annot/curriedMethod/v3/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +class Unrolled{ + def foo(s: String, @unroll n: Int = 1, b: Boolean = true, @unroll l: Long = 0)(f: String => String) = f(s + n + b + l) +} diff --git a/sbt-test/unroll-annot/curriedMethod/v3_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/curriedMethod/v3_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..02b839fc07d2 --- /dev/null +++ b/sbt-test/unroll-annot/curriedMethod/v3_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,14 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV3{ + def main(args: Array[String]): Unit = { + UnrollTestPlatformSpecificV3() + + logAssertStartsWith(new Unrolled().foo("cow")(identity), "cow1true0") + logAssertStartsWith(new Unrolled().foo("cow", 2)(identity), "cow2true0") + logAssertStartsWith(new Unrolled().foo("cow", 2, false)(identity), "cow2false0") + logAssertStartsWith(new Unrolled().foo("cow", 2, false, 3)(identity), "cow2false3") + } +} diff --git a/sbt-test/unroll-annot/curriedMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/sbt-test/unroll-annot/curriedMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala new file mode 100644 index 000000000000..61eeeb8756b8 --- /dev/null +++ b/sbt-test/unroll-annot/curriedMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala @@ -0,0 +1,29 @@ +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + val instance = new Unrolled() + val cls = classOf[Unrolled] + + assert( + cls.getMethod("foo", classOf[String], classOf[String => String]).invoke(instance, "hello", identity[String](_)) == + "hello1true0" + ) + + assert( + scala.util.Try(cls.getMethod("foo", classOf[String], classOf[Int], classOf[String => String])).isFailure + ) + assert( + cls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean], classOf[String => String]) + .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE, identity[String](_)) == + "hello2false0" + ) + assert( + cls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean], classOf[Long], classOf[String => String]) + .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE, 3: Integer, identity[String](_)) == + "hello2false3" + ) + + cls.getMethods.filter(_.getName.contains("foo")).foreach(println) + } +} \ No newline at end of file diff --git a/sbt-test/unroll-annot/methodWithImplicit/build.sbt b/sbt-test/unroll-annot/methodWithImplicit/build.sbt new file mode 100644 index 000000000000..0568d222bf5a --- /dev/null +++ b/sbt-test/unroll-annot/methodWithImplicit/build.sbt @@ -0,0 +1,48 @@ +lazy val utils = project.in(file("utils")) + +lazy val sharedSettings = Seq( + scalacOptions ++= Seq("-Ycheck:all", "-experimental") +) + +lazy val v1 = project.in(file("v1")) + .settings(sharedSettings) + +lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Compile / unmanagedClasspath := Seq( + Attributed.blank((v1 / Compile / classDirectory).value) + ), + ) + +lazy val v2 = project.in(file("v2")) + .settings(sharedSettings) + +lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value) + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v2 / Compile / classDirectory).value) + ), + ) + +lazy val v3 = project.in(file("v3")) + .settings(sharedSettings) + +lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value), + // add v2_app, compiled against v2, to the classpath + Attributed.blank((v2_app / Runtime / classDirectory).value), + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v3 / Compile / classDirectory).value) + ), + ) diff --git a/sbt-test/unroll-annot/methodWithImplicit/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/methodWithImplicit/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..69f15d168bfc --- /dev/null +++ b/sbt-test/unroll-annot/methodWithImplicit/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-source:3.0-migration" + ) +} diff --git a/sbt-test/unroll-annot/methodWithImplicit/test b/sbt-test/unroll-annot/methodWithImplicit/test new file mode 100644 index 000000000000..e8e500857b0e --- /dev/null +++ b/sbt-test/unroll-annot/methodWithImplicit/test @@ -0,0 +1,14 @@ +# compile and run a basic version of Unrolled (v1), and an app that uses it +> v1/compile +> v1_app/runMain unroll.UnrollTestMainV1 +# add a field to the case class (v2), and update the app to use it, +# and ensure the old version (v1) still links +> v2/compile +> v2_app/runMain unroll.UnrollTestMainV1 +> v2_app/runMain unroll.UnrollTestMainV2 +# add a field to the case class (v3), and update the app to use it, +# and ensure the old versions (v1, v2) still link +> v3/compile +> v3_app/runMain unroll.UnrollTestMainV1 +> v3_app/runMain unroll.UnrollTestMainV2 +> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/methodWithImplicit/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/methodWithImplicit/utils/src/main/scala/TestUtils.scala new file mode 100644 index 000000000000..6ac413c9fe98 --- /dev/null +++ b/sbt-test/unroll-annot/methodWithImplicit/utils/src/main/scala/TestUtils.scala @@ -0,0 +1,12 @@ +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/sbt-test/unroll-annot/methodWithImplicit/v1/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/methodWithImplicit/v1/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..44137480e239 --- /dev/null +++ b/sbt-test/unroll-annot/methodWithImplicit/v1/src/main/scala/Unrolled.scala @@ -0,0 +1,5 @@ +package unroll + +class Unrolled{ + def foo(s: String)(implicit f: String => String) = f(s) +} diff --git a/sbt-test/unroll-annot/methodWithImplicit/v1_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/methodWithImplicit/v1_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..928a74f57d5e --- /dev/null +++ b/sbt-test/unroll-annot/methodWithImplicit/v1_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,10 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV1{ + def main(args: Array[String]): Unit = { + implicit def f(s: String): String = s + logAssertStartsWith(new Unrolled().foo("cow"), "cow") + } +} diff --git a/sbt-test/unroll-annot/methodWithImplicit/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/methodWithImplicit/v2/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..fa98747d28d8 --- /dev/null +++ b/sbt-test/unroll-annot/methodWithImplicit/v2/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +class Unrolled{ + def foo(s: String, @unroll n: Int = 1, b: Boolean = true)(implicit f: String => String) = f(s + n + b) +} diff --git a/sbt-test/unroll-annot/methodWithImplicit/v2_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/methodWithImplicit/v2_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..de14613be3cc --- /dev/null +++ b/sbt-test/unroll-annot/methodWithImplicit/v2_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,12 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV2{ + def main(args: Array[String]): Unit = { + implicit def f(s: String): String = s + logAssertStartsWith(new Unrolled().foo("cow"), "cow1true") + logAssertStartsWith(new Unrolled().foo("cow", 2), "cow2true") + logAssertStartsWith(new Unrolled().foo("cow", 2, false), "cow2false") + } +} diff --git a/sbt-test/unroll-annot/methodWithImplicit/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/methodWithImplicit/v3/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..58e88f581b4c --- /dev/null +++ b/sbt-test/unroll-annot/methodWithImplicit/v3/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +class Unrolled{ + def foo(s: String, @unroll n: Int = 1, b: Boolean = true, @unroll l: Long = 0)(implicit f: String => String) = f(s + n + b + l) +} diff --git a/sbt-test/unroll-annot/methodWithImplicit/v3_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/methodWithImplicit/v3_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..1982d7dff344 --- /dev/null +++ b/sbt-test/unroll-annot/methodWithImplicit/v3_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,15 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV3{ + def main(args: Array[String]): Unit = { + UnrollTestPlatformSpecificV3() + + implicit def f(s: String): String = s + logAssertStartsWith(new Unrolled().foo("cow"), "cow1true0") + logAssertStartsWith(new Unrolled().foo("cow", 2), "cow2true0") + logAssertStartsWith(new Unrolled().foo("cow", 2, false), "cow2false0") + logAssertStartsWith(new Unrolled().foo("cow", 2, false, 3), "cow2false3") + } +} diff --git a/sbt-test/unroll-annot/methodWithImplicit/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/sbt-test/unroll-annot/methodWithImplicit/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala new file mode 100644 index 000000000000..3537a6373e0d --- /dev/null +++ b/sbt-test/unroll-annot/methodWithImplicit/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala @@ -0,0 +1,29 @@ +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + val instance = new Unrolled() + val cls = classOf[Unrolled] + + assert( + cls.getMethod("foo", classOf[String], classOf[String => String]).invoke(instance, "hello", identity[String](_)) == + "hello1true0" + ) + + assert(scala.util.Try(cls.getMethod("foo", classOf[String], classOf[Int], classOf[String => String])).isFailure) + + assert( + cls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean], classOf[String => String]) + .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE, identity[String](_)) == + "hello2false0" + ) + assert( + cls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean], classOf[Long], classOf[String => String]) + .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE, 3: Integer, identity[String](_)) == + "hello2false3" + ) + + cls.getMethods.filter(_.getName.contains("foo")).foreach(println) + + } +} \ No newline at end of file diff --git a/sbt-test/unroll-annot/objectMethod/build.sbt b/sbt-test/unroll-annot/objectMethod/build.sbt new file mode 100644 index 000000000000..0568d222bf5a --- /dev/null +++ b/sbt-test/unroll-annot/objectMethod/build.sbt @@ -0,0 +1,48 @@ +lazy val utils = project.in(file("utils")) + +lazy val sharedSettings = Seq( + scalacOptions ++= Seq("-Ycheck:all", "-experimental") +) + +lazy val v1 = project.in(file("v1")) + .settings(sharedSettings) + +lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Compile / unmanagedClasspath := Seq( + Attributed.blank((v1 / Compile / classDirectory).value) + ), + ) + +lazy val v2 = project.in(file("v2")) + .settings(sharedSettings) + +lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value) + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v2 / Compile / classDirectory).value) + ), + ) + +lazy val v3 = project.in(file("v3")) + .settings(sharedSettings) + +lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value), + // add v2_app, compiled against v2, to the classpath + Attributed.blank((v2_app / Runtime / classDirectory).value), + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v3 / Compile / classDirectory).value) + ), + ) diff --git a/sbt-test/unroll-annot/objectMethod/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/objectMethod/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..69f15d168bfc --- /dev/null +++ b/sbt-test/unroll-annot/objectMethod/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-source:3.0-migration" + ) +} diff --git a/sbt-test/unroll-annot/objectMethod/test b/sbt-test/unroll-annot/objectMethod/test new file mode 100644 index 000000000000..e8e500857b0e --- /dev/null +++ b/sbt-test/unroll-annot/objectMethod/test @@ -0,0 +1,14 @@ +# compile and run a basic version of Unrolled (v1), and an app that uses it +> v1/compile +> v1_app/runMain unroll.UnrollTestMainV1 +# add a field to the case class (v2), and update the app to use it, +# and ensure the old version (v1) still links +> v2/compile +> v2_app/runMain unroll.UnrollTestMainV1 +> v2_app/runMain unroll.UnrollTestMainV2 +# add a field to the case class (v3), and update the app to use it, +# and ensure the old versions (v1, v2) still link +> v3/compile +> v3_app/runMain unroll.UnrollTestMainV1 +> v3_app/runMain unroll.UnrollTestMainV2 +> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/objectMethod/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/objectMethod/utils/src/main/scala/TestUtils.scala new file mode 100644 index 000000000000..6ac413c9fe98 --- /dev/null +++ b/sbt-test/unroll-annot/objectMethod/utils/src/main/scala/TestUtils.scala @@ -0,0 +1,12 @@ +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/sbt-test/unroll-annot/objectMethod/v1/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/objectMethod/v1/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..f4559bc2a820 --- /dev/null +++ b/sbt-test/unroll-annot/objectMethod/v1/src/main/scala/Unrolled.scala @@ -0,0 +1,6 @@ +package unroll + +object Unrolled{ + + def foo(s: String, n: Int = 1) = s + n +} diff --git a/sbt-test/unroll-annot/objectMethod/v1_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/objectMethod/v1_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..23f8e15b40cb --- /dev/null +++ b/sbt-test/unroll-annot/objectMethod/v1_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,10 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV1{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(Unrolled.foo("cow"), "cow1") + logAssertStartsWith(Unrolled.foo("cow", 2), "cow2") + } +} diff --git a/sbt-test/unroll-annot/objectMethod/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/objectMethod/v2/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..53d777884939 --- /dev/null +++ b/sbt-test/unroll-annot/objectMethod/v2/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +object Unrolled{ + def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b +} diff --git a/sbt-test/unroll-annot/objectMethod/v2_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/objectMethod/v2_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..ee5337bd4689 --- /dev/null +++ b/sbt-test/unroll-annot/objectMethod/v2_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,11 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV2{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(Unrolled.foo("cow"), "cow1true") + logAssertStartsWith(Unrolled.foo("cow", 2), "cow2true") + logAssertStartsWith(Unrolled.foo("cow", 2, false), "cow2false") + } +} diff --git a/sbt-test/unroll-annot/objectMethod/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/objectMethod/v3/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..5e3464c184a4 --- /dev/null +++ b/sbt-test/unroll-annot/objectMethod/v3/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +object Unrolled{ + def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0) = s + n + b + l +} diff --git a/sbt-test/unroll-annot/objectMethod/v3_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/objectMethod/v3_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..b1996bd6d5d7 --- /dev/null +++ b/sbt-test/unroll-annot/objectMethod/v3_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,14 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV3{ + def main(args: Array[String]): Unit = { + UnrollTestPlatformSpecificV3() + + logAssertStartsWith(Unrolled.foo("cow"), "cow1true0") + logAssertStartsWith(Unrolled.foo("cow", 2), "cow2true0") + logAssertStartsWith(Unrolled.foo("cow", 2, false), "cow2false0") + logAssertStartsWith(Unrolled.foo("cow", 2, false, 3), "cow2false3") + } +} diff --git a/sbt-test/unroll-annot/objectMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/sbt-test/unroll-annot/objectMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala new file mode 100644 index 000000000000..2b5578dd482d --- /dev/null +++ b/sbt-test/unroll-annot/objectMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala @@ -0,0 +1,48 @@ +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + val instance = Unrolled + val instanceCls = Class.forName("unroll.Unrolled$") + + instanceCls.getMethods.filter(_.getName.contains("foo")).foreach(println) + + // Make sure singleton instance forwarder methods are generated + assert(scala.util.Try(instanceCls.getMethod("foo", classOf[String])).isFailure) + assert( + instanceCls.getMethod("foo", classOf[String], classOf[Int]).invoke(instance, "hello", 2: Integer) == + "hello2true0" + ) + assert( + instanceCls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean]) + .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE) == + "hello2false0" + ) + assert( + instanceCls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean], classOf[Long]) + .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE, 3: Integer) == + "hello2false3" + ) + + // Make sure static forwarder methods are generated + val staticCls = Class.forName("unroll.Unrolled") + staticCls.getMethods.filter(_.getName.contains("foo")).foreach(println) + + assert(scala.util.Try(staticCls.getMethod("foo", classOf[String])).isFailure) + assert( + staticCls.getMethod("foo", classOf[String], classOf[Int]).invoke(null, "hello", 2: Integer) == + "hello2true0" + ) + assert( + staticCls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean]) + .invoke(null, "hello", 2: Integer, java.lang.Boolean.FALSE) == + "hello2false0" + ) + assert( + staticCls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean], classOf[Long]) + .invoke(null, "hello", 2: Integer, java.lang.Boolean.FALSE, 3: Integer) == + "hello2false3" + ) + + } +} \ No newline at end of file diff --git a/sbt-test/unroll-annot/primaryConstructor/build.sbt b/sbt-test/unroll-annot/primaryConstructor/build.sbt new file mode 100644 index 000000000000..0568d222bf5a --- /dev/null +++ b/sbt-test/unroll-annot/primaryConstructor/build.sbt @@ -0,0 +1,48 @@ +lazy val utils = project.in(file("utils")) + +lazy val sharedSettings = Seq( + scalacOptions ++= Seq("-Ycheck:all", "-experimental") +) + +lazy val v1 = project.in(file("v1")) + .settings(sharedSettings) + +lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Compile / unmanagedClasspath := Seq( + Attributed.blank((v1 / Compile / classDirectory).value) + ), + ) + +lazy val v2 = project.in(file("v2")) + .settings(sharedSettings) + +lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value) + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v2 / Compile / classDirectory).value) + ), + ) + +lazy val v3 = project.in(file("v3")) + .settings(sharedSettings) + +lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value), + // add v2_app, compiled against v2, to the classpath + Attributed.blank((v2_app / Runtime / classDirectory).value), + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v3 / Compile / classDirectory).value) + ), + ) diff --git a/sbt-test/unroll-annot/primaryConstructor/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/primaryConstructor/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..69f15d168bfc --- /dev/null +++ b/sbt-test/unroll-annot/primaryConstructor/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-source:3.0-migration" + ) +} diff --git a/sbt-test/unroll-annot/primaryConstructor/test b/sbt-test/unroll-annot/primaryConstructor/test new file mode 100644 index 000000000000..e8e500857b0e --- /dev/null +++ b/sbt-test/unroll-annot/primaryConstructor/test @@ -0,0 +1,14 @@ +# compile and run a basic version of Unrolled (v1), and an app that uses it +> v1/compile +> v1_app/runMain unroll.UnrollTestMainV1 +# add a field to the case class (v2), and update the app to use it, +# and ensure the old version (v1) still links +> v2/compile +> v2_app/runMain unroll.UnrollTestMainV1 +> v2_app/runMain unroll.UnrollTestMainV2 +# add a field to the case class (v3), and update the app to use it, +# and ensure the old versions (v1, v2) still link +> v3/compile +> v3_app/runMain unroll.UnrollTestMainV1 +> v3_app/runMain unroll.UnrollTestMainV2 +> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/primaryConstructor/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/primaryConstructor/utils/src/main/scala/TestUtils.scala new file mode 100644 index 000000000000..6ac413c9fe98 --- /dev/null +++ b/sbt-test/unroll-annot/primaryConstructor/utils/src/main/scala/TestUtils.scala @@ -0,0 +1,12 @@ +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/sbt-test/unroll-annot/primaryConstructor/v1/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/primaryConstructor/v1/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..c7574f4346e0 --- /dev/null +++ b/sbt-test/unroll-annot/primaryConstructor/v1/src/main/scala/Unrolled.scala @@ -0,0 +1,5 @@ +package unroll + +class Unrolled(s: String, n: Int = 1){ + def foo = s + n +} diff --git a/sbt-test/unroll-annot/primaryConstructor/v1_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/primaryConstructor/v1_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..9bed955a9bc8 --- /dev/null +++ b/sbt-test/unroll-annot/primaryConstructor/v1_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,10 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV1{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled("cow").foo, "cow1") + logAssertStartsWith(new Unrolled("cow", 2).foo, "cow2") + } +} diff --git a/sbt-test/unroll-annot/primaryConstructor/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/primaryConstructor/v2/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..dfe3c2f0e46d --- /dev/null +++ b/sbt-test/unroll-annot/primaryConstructor/v2/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +class Unrolled(s: String, n: Int = 1, @unroll b: Boolean = true){ + def foo = s + n + b +} diff --git a/sbt-test/unroll-annot/primaryConstructor/v2_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/primaryConstructor/v2_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..7a88d263a213 --- /dev/null +++ b/sbt-test/unroll-annot/primaryConstructor/v2_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,11 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV2{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled("cow").foo, "cow1true") + logAssertStartsWith(new Unrolled("cow", 2).foo, "cow2true") + logAssertStartsWith(new Unrolled("cow", 2, false).foo, "cow2false") + } +} diff --git a/sbt-test/unroll-annot/primaryConstructor/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/primaryConstructor/v3/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..b570bcaadbb3 --- /dev/null +++ b/sbt-test/unroll-annot/primaryConstructor/v3/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +class Unrolled(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0){ + def foo = s + n + b + l +} diff --git a/sbt-test/unroll-annot/primaryConstructor/v3_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/primaryConstructor/v3_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..f05ca8808c3d --- /dev/null +++ b/sbt-test/unroll-annot/primaryConstructor/v3_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,14 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV3{ + def main(args: Array[String]): Unit = { + UnrollTestPlatformSpecificV3() + + logAssertStartsWith(new Unrolled("cow").foo, "cow1true0") + logAssertStartsWith(new Unrolled("cow", 2).foo, "cow2true0") + logAssertStartsWith(new Unrolled("cow", 2, false).foo, "cow2false0") + logAssertStartsWith(new Unrolled("cow", 2, false, 3).foo, "cow2false3") + } +} diff --git a/sbt-test/unroll-annot/primaryConstructor/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/sbt-test/unroll-annot/primaryConstructor/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala new file mode 100644 index 000000000000..07dee69cd8a7 --- /dev/null +++ b/sbt-test/unroll-annot/primaryConstructor/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala @@ -0,0 +1,33 @@ +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + val cls = classOf[Unrolled] + + assert(scala.util.Try(cls.getConstructor(classOf[String])).isFailure) + println() + assert( + cls.getConstructor(classOf[String], classOf[Int]) + .newInstance("hello", 2: Integer) + .asInstanceOf[Unrolled] + .foo == + "hello2true0" + ) + assert( + cls.getConstructor(classOf[String], classOf[Int], classOf[Boolean]) + .newInstance("hello", 2: Integer, java.lang.Boolean.FALSE) + .asInstanceOf[Unrolled] + .foo == + "hello2false0" + ) + assert( + cls.getConstructor(classOf[String], classOf[Int], classOf[Boolean], classOf[Long]) + .newInstance("hello", 2: Integer, java.lang.Boolean.FALSE, 3: Integer) + .asInstanceOf[Unrolled] + .foo == + "hello2false3" + ) + + cls.getConstructors.foreach(println) + } +} \ No newline at end of file diff --git a/sbt-test/unroll-annot/secondParameterList/build.sbt b/sbt-test/unroll-annot/secondParameterList/build.sbt new file mode 100644 index 000000000000..0568d222bf5a --- /dev/null +++ b/sbt-test/unroll-annot/secondParameterList/build.sbt @@ -0,0 +1,48 @@ +lazy val utils = project.in(file("utils")) + +lazy val sharedSettings = Seq( + scalacOptions ++= Seq("-Ycheck:all", "-experimental") +) + +lazy val v1 = project.in(file("v1")) + .settings(sharedSettings) + +lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Compile / unmanagedClasspath := Seq( + Attributed.blank((v1 / Compile / classDirectory).value) + ), + ) + +lazy val v2 = project.in(file("v2")) + .settings(sharedSettings) + +lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value) + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v2 / Compile / classDirectory).value) + ), + ) + +lazy val v3 = project.in(file("v3")) + .settings(sharedSettings) + +lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value), + // add v2_app, compiled against v2, to the classpath + Attributed.blank((v2_app / Runtime / classDirectory).value), + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v3 / Compile / classDirectory).value) + ), + ) diff --git a/sbt-test/unroll-annot/secondParameterList/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/secondParameterList/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..69f15d168bfc --- /dev/null +++ b/sbt-test/unroll-annot/secondParameterList/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-source:3.0-migration" + ) +} diff --git a/sbt-test/unroll-annot/secondParameterList/test b/sbt-test/unroll-annot/secondParameterList/test new file mode 100644 index 000000000000..e8e500857b0e --- /dev/null +++ b/sbt-test/unroll-annot/secondParameterList/test @@ -0,0 +1,14 @@ +# compile and run a basic version of Unrolled (v1), and an app that uses it +> v1/compile +> v1_app/runMain unroll.UnrollTestMainV1 +# add a field to the case class (v2), and update the app to use it, +# and ensure the old version (v1) still links +> v2/compile +> v2_app/runMain unroll.UnrollTestMainV1 +> v2_app/runMain unroll.UnrollTestMainV2 +# add a field to the case class (v3), and update the app to use it, +# and ensure the old versions (v1, v2) still link +> v3/compile +> v3_app/runMain unroll.UnrollTestMainV1 +> v3_app/runMain unroll.UnrollTestMainV2 +> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/secondParameterList/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/secondParameterList/utils/src/main/scala/TestUtils.scala new file mode 100644 index 000000000000..6ac413c9fe98 --- /dev/null +++ b/sbt-test/unroll-annot/secondParameterList/utils/src/main/scala/TestUtils.scala @@ -0,0 +1,12 @@ +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/sbt-test/unroll-annot/secondParameterList/v1/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/secondParameterList/v1/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..f9ddac201c59 --- /dev/null +++ b/sbt-test/unroll-annot/secondParameterList/v1/src/main/scala/Unrolled.scala @@ -0,0 +1,5 @@ +package unroll + +class Unrolled{ + def foo(f: String => String)(s: String) = f(s) +} diff --git a/sbt-test/unroll-annot/secondParameterList/v1_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/secondParameterList/v1_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..ef6fd3b68102 --- /dev/null +++ b/sbt-test/unroll-annot/secondParameterList/v1_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,9 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV1{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled().foo(identity)("cow"), "cow") + } +} diff --git a/sbt-test/unroll-annot/secondParameterList/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/secondParameterList/v2/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..81d481deff38 --- /dev/null +++ b/sbt-test/unroll-annot/secondParameterList/v2/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +class Unrolled{ + def foo(f: String => String)(s: String, @unroll n: Int = 1, b: Boolean = true) = f(s + n + b) +} diff --git a/sbt-test/unroll-annot/secondParameterList/v2_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/secondParameterList/v2_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..09c06869f617 --- /dev/null +++ b/sbt-test/unroll-annot/secondParameterList/v2_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,11 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV2{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled().foo(identity)("cow"), "cow1true") + logAssertStartsWith(new Unrolled().foo(identity)("cow", 2), "cow2true") + logAssertStartsWith(new Unrolled().foo(identity)("cow", 2, false), "cow2false") + } +} diff --git a/sbt-test/unroll-annot/secondParameterList/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/secondParameterList/v3/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..35803d5220a8 --- /dev/null +++ b/sbt-test/unroll-annot/secondParameterList/v3/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +class Unrolled{ + def foo(f: String => String)(s: String, @unroll n: Int = 1, b: Boolean = true, @unroll l: Long = 0) = f(s + n + b + l) +} diff --git a/sbt-test/unroll-annot/secondParameterList/v3_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/secondParameterList/v3_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..468f24956d94 --- /dev/null +++ b/sbt-test/unroll-annot/secondParameterList/v3_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,14 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV3{ + def main(args: Array[String]): Unit = { + UnrollTestPlatformSpecificV3() + + logAssertStartsWith(new Unrolled().foo(identity)("cow"), "cow1true0") + logAssertStartsWith(new Unrolled().foo(identity)("cow", 2), "cow2true0") + logAssertStartsWith(new Unrolled().foo(identity)("cow", 2, false), "cow2false0") + logAssertStartsWith(new Unrolled().foo(identity)("cow", 2, false, 3), "cow2false3") + } +} diff --git a/sbt-test/unroll-annot/secondParameterList/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/sbt-test/unroll-annot/secondParameterList/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala new file mode 100644 index 000000000000..4663b2220cd7 --- /dev/null +++ b/sbt-test/unroll-annot/secondParameterList/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala @@ -0,0 +1,30 @@ +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + val instance = new Unrolled() + val cls = classOf[Unrolled] + + assert( + cls.getMethod("foo", classOf[String => String], classOf[String]) + .invoke(instance, identity[String](_), "hello") == + "hello1true0" + ) + + assert( + scala.util.Try(cls.getMethod("foo", classOf[String => String], classOf[String], classOf[Int])).isFailure + ) + assert( + cls.getMethod("foo", classOf[String => String], classOf[String], classOf[Int], classOf[Boolean]) + .invoke(instance, identity[String](_), "hello", 2: Integer, java.lang.Boolean.FALSE) == + "hello2false0" + ) + assert( + cls.getMethod("foo", classOf[String => String], classOf[String], classOf[Int], classOf[Boolean], classOf[Long]) + .invoke(instance, identity[String](_), "hello", 2: Integer, java.lang.Boolean.FALSE, 3: Integer) == + "hello2false3" + ) + + cls.getMethods.filter(_.getName.contains("foo")).foreach(println) + } +} \ No newline at end of file diff --git a/sbt-test/unroll-annot/secondaryConstructor/build.sbt b/sbt-test/unroll-annot/secondaryConstructor/build.sbt new file mode 100644 index 000000000000..0568d222bf5a --- /dev/null +++ b/sbt-test/unroll-annot/secondaryConstructor/build.sbt @@ -0,0 +1,48 @@ +lazy val utils = project.in(file("utils")) + +lazy val sharedSettings = Seq( + scalacOptions ++= Seq("-Ycheck:all", "-experimental") +) + +lazy val v1 = project.in(file("v1")) + .settings(sharedSettings) + +lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Compile / unmanagedClasspath := Seq( + Attributed.blank((v1 / Compile / classDirectory).value) + ), + ) + +lazy val v2 = project.in(file("v2")) + .settings(sharedSettings) + +lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value) + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v2 / Compile / classDirectory).value) + ), + ) + +lazy val v3 = project.in(file("v3")) + .settings(sharedSettings) + +lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value), + // add v2_app, compiled against v2, to the classpath + Attributed.blank((v2_app / Runtime / classDirectory).value), + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v3 / Compile / classDirectory).value) + ), + ) diff --git a/sbt-test/unroll-annot/secondaryConstructor/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/secondaryConstructor/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..69f15d168bfc --- /dev/null +++ b/sbt-test/unroll-annot/secondaryConstructor/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-source:3.0-migration" + ) +} diff --git a/sbt-test/unroll-annot/secondaryConstructor/test b/sbt-test/unroll-annot/secondaryConstructor/test new file mode 100644 index 000000000000..e8e500857b0e --- /dev/null +++ b/sbt-test/unroll-annot/secondaryConstructor/test @@ -0,0 +1,14 @@ +# compile and run a basic version of Unrolled (v1), and an app that uses it +> v1/compile +> v1_app/runMain unroll.UnrollTestMainV1 +# add a field to the case class (v2), and update the app to use it, +# and ensure the old version (v1) still links +> v2/compile +> v2_app/runMain unroll.UnrollTestMainV1 +> v2_app/runMain unroll.UnrollTestMainV2 +# add a field to the case class (v3), and update the app to use it, +# and ensure the old versions (v1, v2) still link +> v3/compile +> v3_app/runMain unroll.UnrollTestMainV1 +> v3_app/runMain unroll.UnrollTestMainV2 +> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/secondaryConstructor/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/secondaryConstructor/utils/src/main/scala/TestUtils.scala new file mode 100644 index 000000000000..6ac413c9fe98 --- /dev/null +++ b/sbt-test/unroll-annot/secondaryConstructor/utils/src/main/scala/TestUtils.scala @@ -0,0 +1,12 @@ +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/sbt-test/unroll-annot/secondaryConstructor/v1/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/secondaryConstructor/v1/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..529a3fc66de3 --- /dev/null +++ b/sbt-test/unroll-annot/secondaryConstructor/v1/src/main/scala/Unrolled.scala @@ -0,0 +1,9 @@ +package unroll + +class Unrolled(){ + var foo = "" + def this(s: String, n: Int = 1) = { + this() + foo = s + n + } +} diff --git a/sbt-test/unroll-annot/secondaryConstructor/v1_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/secondaryConstructor/v1_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..9bed955a9bc8 --- /dev/null +++ b/sbt-test/unroll-annot/secondaryConstructor/v1_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,10 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV1{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled("cow").foo, "cow1") + logAssertStartsWith(new Unrolled("cow", 2).foo, "cow2") + } +} diff --git a/sbt-test/unroll-annot/secondaryConstructor/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/secondaryConstructor/v2/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..382066698f31 --- /dev/null +++ b/sbt-test/unroll-annot/secondaryConstructor/v2/src/main/scala/Unrolled.scala @@ -0,0 +1,12 @@ +package unroll + +import scala.annotation.unroll + +class Unrolled() { + var foo = "" + + def this(s: String, n: Int = 1, @unroll b: Boolean = true) = { + this() + foo = s + n + b + } +} diff --git a/sbt-test/unroll-annot/secondaryConstructor/v2_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/secondaryConstructor/v2_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..7a88d263a213 --- /dev/null +++ b/sbt-test/unroll-annot/secondaryConstructor/v2_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,11 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV2{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled("cow").foo, "cow1true") + logAssertStartsWith(new Unrolled("cow", 2).foo, "cow2true") + logAssertStartsWith(new Unrolled("cow", 2, false).foo, "cow2false") + } +} diff --git a/sbt-test/unroll-annot/secondaryConstructor/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/secondaryConstructor/v3/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..89411d6576ca --- /dev/null +++ b/sbt-test/unroll-annot/secondaryConstructor/v3/src/main/scala/Unrolled.scala @@ -0,0 +1,12 @@ +package unroll + +import scala.annotation.unroll + +class Unrolled() { + var foo = "" + + def this(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0) = { + this() + foo = s + n + b + l + } +} diff --git a/sbt-test/unroll-annot/secondaryConstructor/v3_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/secondaryConstructor/v3_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..f05ca8808c3d --- /dev/null +++ b/sbt-test/unroll-annot/secondaryConstructor/v3_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,14 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV3{ + def main(args: Array[String]): Unit = { + UnrollTestPlatformSpecificV3() + + logAssertStartsWith(new Unrolled("cow").foo, "cow1true0") + logAssertStartsWith(new Unrolled("cow", 2).foo, "cow2true0") + logAssertStartsWith(new Unrolled("cow", 2, false).foo, "cow2false0") + logAssertStartsWith(new Unrolled("cow", 2, false, 3).foo, "cow2false3") + } +} diff --git a/sbt-test/unroll-annot/secondaryConstructor/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/sbt-test/unroll-annot/secondaryConstructor/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala new file mode 100644 index 000000000000..07dee69cd8a7 --- /dev/null +++ b/sbt-test/unroll-annot/secondaryConstructor/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala @@ -0,0 +1,33 @@ +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + val cls = classOf[Unrolled] + + assert(scala.util.Try(cls.getConstructor(classOf[String])).isFailure) + println() + assert( + cls.getConstructor(classOf[String], classOf[Int]) + .newInstance("hello", 2: Integer) + .asInstanceOf[Unrolled] + .foo == + "hello2true0" + ) + assert( + cls.getConstructor(classOf[String], classOf[Int], classOf[Boolean]) + .newInstance("hello", 2: Integer, java.lang.Boolean.FALSE) + .asInstanceOf[Unrolled] + .foo == + "hello2false0" + ) + assert( + cls.getConstructor(classOf[String], classOf[Int], classOf[Boolean], classOf[Long]) + .newInstance("hello", 2: Integer, java.lang.Boolean.FALSE, 3: Integer) + .asInstanceOf[Unrolled] + .foo == + "hello2false3" + ) + + cls.getConstructors.foreach(println) + } +} \ No newline at end of file diff --git a/sbt-test/unroll-annot/traitMethod/build.sbt b/sbt-test/unroll-annot/traitMethod/build.sbt new file mode 100644 index 000000000000..0568d222bf5a --- /dev/null +++ b/sbt-test/unroll-annot/traitMethod/build.sbt @@ -0,0 +1,48 @@ +lazy val utils = project.in(file("utils")) + +lazy val sharedSettings = Seq( + scalacOptions ++= Seq("-Ycheck:all", "-experimental") +) + +lazy val v1 = project.in(file("v1")) + .settings(sharedSettings) + +lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Compile / unmanagedClasspath := Seq( + Attributed.blank((v1 / Compile / classDirectory).value) + ), + ) + +lazy val v2 = project.in(file("v2")) + .settings(sharedSettings) + +lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value) + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v2 / Compile / classDirectory).value) + ), + ) + +lazy val v3 = project.in(file("v3")) + .settings(sharedSettings) + +lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value), + // add v2_app, compiled against v2, to the classpath + Attributed.blank((v2_app / Runtime / classDirectory).value), + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v3 / Compile / classDirectory).value) + ), + ) diff --git a/sbt-test/unroll-annot/traitMethod/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/traitMethod/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..69f15d168bfc --- /dev/null +++ b/sbt-test/unroll-annot/traitMethod/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-source:3.0-migration" + ) +} diff --git a/sbt-test/unroll-annot/traitMethod/test b/sbt-test/unroll-annot/traitMethod/test new file mode 100644 index 000000000000..e8e500857b0e --- /dev/null +++ b/sbt-test/unroll-annot/traitMethod/test @@ -0,0 +1,14 @@ +# compile and run a basic version of Unrolled (v1), and an app that uses it +> v1/compile +> v1_app/runMain unroll.UnrollTestMainV1 +# add a field to the case class (v2), and update the app to use it, +# and ensure the old version (v1) still links +> v2/compile +> v2_app/runMain unroll.UnrollTestMainV1 +> v2_app/runMain unroll.UnrollTestMainV2 +# add a field to the case class (v3), and update the app to use it, +# and ensure the old versions (v1, v2) still link +> v3/compile +> v3_app/runMain unroll.UnrollTestMainV1 +> v3_app/runMain unroll.UnrollTestMainV2 +> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/traitMethod/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/traitMethod/utils/src/main/scala/TestUtils.scala new file mode 100644 index 000000000000..6ac413c9fe98 --- /dev/null +++ b/sbt-test/unroll-annot/traitMethod/utils/src/main/scala/TestUtils.scala @@ -0,0 +1,12 @@ +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/sbt-test/unroll-annot/traitMethod/v1/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/traitMethod/v1/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..eaadde758ac7 --- /dev/null +++ b/sbt-test/unroll-annot/traitMethod/v1/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +trait Unrolled{ + def foo(s: String, n: Int = 1) = s + n +} + +object Unrolled extends Unrolled \ No newline at end of file diff --git a/sbt-test/unroll-annot/traitMethod/v1_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/traitMethod/v1_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..f1c7c8bb88a4 --- /dev/null +++ b/sbt-test/unroll-annot/traitMethod/v1_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,14 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV1{ + def main(args: Array[String]): Unit = { + val unrolled = new Unrolled{} + logAssertStartsWith(unrolled.foo("cow"), "cow1") + logAssertStartsWith(unrolled.foo("cow", 2), "cow2") + + logAssertStartsWith(Unrolled.foo("cow"), "cow1") + logAssertStartsWith(Unrolled.foo("cow", 2), "cow2") + } +} diff --git a/sbt-test/unroll-annot/traitMethod/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/traitMethod/v2/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..7d40ff17b846 --- /dev/null +++ b/sbt-test/unroll-annot/traitMethod/v2/src/main/scala/Unrolled.scala @@ -0,0 +1,9 @@ +package unroll + +import scala.annotation.unroll + +trait Unrolled{ + def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b +} + +object Unrolled extends Unrolled \ No newline at end of file diff --git a/sbt-test/unroll-annot/traitMethod/v2_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/traitMethod/v2_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..30eb52263e50 --- /dev/null +++ b/sbt-test/unroll-annot/traitMethod/v2_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,16 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV2{ + def main(args: Array[String]): Unit = { + val unrolled = new Unrolled{} + logAssertStartsWith(unrolled.foo("cow"), "cow1true") + logAssertStartsWith(unrolled.foo("cow", 2), "cow2true") + logAssertStartsWith(unrolled.foo("cow", 2, false), "cow2false") + + logAssertStartsWith(Unrolled.foo("cow"), "cow1true") + logAssertStartsWith(Unrolled.foo("cow", 2), "cow2true") + logAssertStartsWith(Unrolled.foo("cow", 2, false), "cow2false") + } +} diff --git a/sbt-test/unroll-annot/traitMethod/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/traitMethod/v3/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..085ecea52adf --- /dev/null +++ b/sbt-test/unroll-annot/traitMethod/v3/src/main/scala/Unrolled.scala @@ -0,0 +1,9 @@ +package unroll + +import scala.annotation.unroll + +trait Unrolled{ + def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0) = s + n + b + l +} + +object Unrolled extends Unrolled \ No newline at end of file diff --git a/sbt-test/unroll-annot/traitMethod/v3_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/traitMethod/v3_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..89154b958161 --- /dev/null +++ b/sbt-test/unroll-annot/traitMethod/v3_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,20 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV3{ + def main(args: Array[String]): Unit = { + UnrollTestPlatformSpecificV3() + + val unrolled = new Unrolled{} + logAssertStartsWith(unrolled.foo("cow"), "cow1true0") + logAssertStartsWith(unrolled.foo("cow", 2), "cow2true0") + logAssertStartsWith(unrolled.foo("cow", 2, false), "cow2false0") + logAssertStartsWith(unrolled.foo("cow", 2, false, 3), "cow2false3") + + logAssertStartsWith(Unrolled.foo("cow"), "cow1true0") + logAssertStartsWith(Unrolled.foo("cow", 2), "cow2true0") + logAssertStartsWith(Unrolled.foo("cow", 2, false), "cow2false0") + logAssertStartsWith(Unrolled.foo("cow", 2, false, 3), "cow2false3") + } +} diff --git a/sbt-test/unroll-annot/traitMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/sbt-test/unroll-annot/traitMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala new file mode 100644 index 000000000000..e8367679233f --- /dev/null +++ b/sbt-test/unroll-annot/traitMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala @@ -0,0 +1,28 @@ +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + val instance = new Unrolled {} + val cls = classOf[Unrolled] + + assert(scala.util.Try(cls.getMethod("foo", classOf[String])).isFailure) + println() + assert( + cls.getMethod("foo", classOf[String], classOf[Int]).invoke(instance, "hello", 2: Integer) == + "hello2true0" + ) + assert( + cls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean]) + .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE) == + "hello2false0" + ) + assert( + cls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean], classOf[Long]) + .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE, 3: Integer) == + "hello2false3" + ) + + cls.getMethods.filter(_.getName.contains("foo")).foreach(println) + + } +} \ No newline at end of file From 75a9275a35026ea1b024a05f52cabef6b814b550 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 1 Oct 2024 12:20:41 +0200 Subject: [PATCH 04/35] SIP 61 - substitute types in the forwarder def, enable genericMethod test --- .../tools/dotc/transform/UnrollDefs.scala | 154 ++++++++---------- sbt-test/unroll-annot/genericMethod/build.sbt | 56 +++++++ .../project/DottyInjectedPlugin.scala | 12 ++ sbt-test/unroll-annot/genericMethod/test | 14 ++ .../utils/src/main/scala/TestUtils.scala | 12 ++ .../v1/src/main/scala/Unrolled.scala | 5 + .../src/main/scala/UnrollTestMain.scala | 9 + .../v2/src/main/scala/Unrolled.scala | 7 + .../src/main/scala/UnrollTestMain.scala | 11 ++ .../v3/src/main/scala/Unrolled.scala | 7 + .../src/main/scala/UnrollTestMain.scala | 14 ++ .../scala/UnrollTestPlatformSpecific.scala | 27 +++ 12 files changed, 243 insertions(+), 85 deletions(-) create mode 100644 sbt-test/unroll-annot/genericMethod/build.sbt create mode 100644 sbt-test/unroll-annot/genericMethod/project/DottyInjectedPlugin.scala create mode 100644 sbt-test/unroll-annot/genericMethod/test create mode 100644 sbt-test/unroll-annot/genericMethod/utils/src/main/scala/TestUtils.scala create mode 100644 sbt-test/unroll-annot/genericMethod/v1/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/genericMethod/v1_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/genericMethod/v2/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/genericMethod/v2_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/genericMethod/v3/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/genericMethod/v3_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/genericMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala index 5fcf6f0ba9ab..3ed2460a267f 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala @@ -17,7 +17,7 @@ import dotty.tools.dotc.core.NameKinds.DefaultGetterName import dotty.tools.dotc.core.Types.{MethodType, NamedType, PolyType, Type} import dotty.tools.dotc.core.Symbols -import scala.language.implicitConversions +import dotty.tools.dotc.core.Types.NoType class UnrollDefs extends MiniPhase { import tpd._ @@ -26,19 +26,8 @@ class UnrollDefs extends MiniPhase { override val runsAfter = Set(FirstTransform.name) - def copyParam(p: ValDef, parent: Symbol)(using Context) = { - implicitly[Context].typeAssigner.assignType( - cpy.ValDef(p)(p.name, p.tpt, p.rhs), - Symbols.newSymbol(parent, p.name, p.symbol.flags, p.symbol.info) - ) - } - - def copyParam2(p: TypeDef, parent: Symbol)(using Context) = { - implicitly[Context].typeAssigner.assignType( - cpy.TypeDef(p)(p.name, p.rhs), - Symbols.newSymbol(parent, p.name, p.symbol.flags, p.symbol.info) - ) - } + def copyParamSym(sym: Symbol, parent: Symbol)(using Context): (Symbol, Symbol) = + sym -> sym.copy(owner = parent) def findUnrollAnnotations(params: List[Symbol])(using Context): List[Int] = { params @@ -49,26 +38,21 @@ class UnrollDefs extends MiniPhase { } } def isTypeClause(p: ParamClause) = p.headOption.exists(_.isInstanceOf[TypeDef]) + def generateSingleForwarder(defdef: DefDef, prevMethodType: Type, paramIndex: Int, nextParamIndex: Int, nextSymbol: Symbol, annotatedParamListIndex: Int, - paramLists: List[ParamClause], - isCaseApply: Boolean) - (using Context) = { - - def truncateMethodType0(tpe: Type, n: Int): Type = { - tpe match{ - case pt: PolyType => PolyType(pt.paramNames, pt.paramInfos, truncateMethodType0(pt.resType, n + 1)) - case mt: MethodType => - if (n == annotatedParamListIndex) MethodType(mt.paramInfos.take(paramIndex), mt.resType) - else MethodType(mt.paramInfos, truncateMethodType0(mt.resType, n + 1)) + isCaseApply: Boolean)(using Context) = { + + def extractParamSymss(parent: Symbol)(using Context): List[List[(Symbol, Symbol)]] = + defdef.paramss.zipWithIndex.map{ case (ps, i) => + if (i == annotatedParamListIndex) ps.take(paramIndex).map(p => copyParamSym(p.symbol, parent)) + else ps.map(p => copyParamSym(p.symbol, parent)) } - } - val truncatedMethodType = truncateMethodType0(prevMethodType, 0) val forwarderDefSymbol = Symbols.newSymbol( defdef.symbol.owner, defdef.name, @@ -76,74 +60,76 @@ class UnrollDefs extends MiniPhase { HasDefaultParams &~ (if (nextParamIndex == -1) Flags.EmptyFlags else Deferred) | Invisible, - truncatedMethodType + NoType, // fill in later ) - val newParamLists: List[ParamClause] = paramLists.zipWithIndex.map{ case (ps, i) => - if (i == annotatedParamListIndex) ps.take(paramIndex).map(p => copyParam(p.asInstanceOf[ValDef], forwarderDefSymbol)) - else { - if (isTypeClause(ps)) ps.map(p => copyParam2(p.asInstanceOf[TypeDef], forwarderDefSymbol)) - else ps.map(p => copyParam(p.asInstanceOf[ValDef], forwarderDefSymbol)) - } - } - forwarderDefSymbol.setParamssFromDefs(newParamLists) - - val defaultOffset = paramLists - .iterator - .take(annotatedParamListIndex) - .filter(!isTypeClause(_)) - .map(_.size) - .sum - - val defaultCalls = Range(paramIndex, nextParamIndex).map(n => - val inner = if (defdef.symbol.isConstructor) { - ref(defdef.symbol.owner.companionModule) - .select(DefaultGetterName(defdef.name, n + defaultOffset)) - } else if (isCaseApply) { - ref(defdef.symbol.owner.companionModule) - .select(DefaultGetterName(termName(""), n + defaultOffset)) - } else { - This(defdef.symbol.owner.asClass) - .select(DefaultGetterName(defdef.name, n + defaultOffset)) - } + val newParamSymMappings = extractParamSymss(forwarderDefSymbol) + val (oldParams, newParams) = newParamSymMappings.flatten.unzip - newParamLists + val newParamSymLists = + newParamSymMappings.map: pairss => + pairss.map: (oldSym, newSym) => + newSym.info = oldSym.info.substSym(oldParams, newParams) + newSym + + val newResType = defdef.tpt.tpe.substSym(oldParams, newParams) + forwarderDefSymbol.info = NamerOps.methodType(newParamSymLists, newResType) + forwarderDefSymbol.setParamss(newParamSymLists) + + def forwarderRhs(): tpd.Tree = { + val defaultOffset = defdef.paramss + .iterator .take(annotatedParamListIndex) - .map(_.map(p => ref(p.symbol))) - .foldLeft[Tree](inner){ - case (lhs: Tree, newParams) => - if (newParams.headOption.exists(_.isInstanceOf[TypeTree])) TypeApply(lhs, newParams) - else Apply(lhs, newParams) + .filter(!isTypeClause(_)) + .map(_.size) + .sum + + val defaultCalls = Range(paramIndex, nextParamIndex).map(n => + val inner = if (defdef.symbol.isConstructor) { + ref(defdef.symbol.owner.companionModule) + .select(DefaultGetterName(defdef.name, n + defaultOffset)) + } else if (isCaseApply) { + ref(defdef.symbol.owner.companionModule) + .select(DefaultGetterName(termName(""), n + defaultOffset)) + } else { + This(defdef.symbol.owner.asClass) + .select(DefaultGetterName(defdef.name, n + defaultOffset)) } - ) - val forwarderInner: Tree = This(defdef.symbol.owner.asClass).select(nextSymbol) + newParamSymLists + .take(annotatedParamListIndex) + .map(_.map(p => ref(p))) + .foldLeft[Tree](inner){ + case (lhs: Tree, newParams) => + if (newParams.headOption.exists(_.isInstanceOf[TypeTree])) TypeApply(lhs, newParams) + else Apply(lhs, newParams) + } + ) + + val forwarderInner: Tree = This(defdef.symbol.owner.asClass).select(nextSymbol) + + val forwarderCallArgs = + newParamSymLists.zipWithIndex.map{case (ps, i) => + if (i == annotatedParamListIndex) ps.map(p => ref(p)).take(nextParamIndex) ++ defaultCalls + else ps.map(p => ref(p)) + } - val forwarderCallArgs = - newParamLists.zipWithIndex.map{case (ps, i) => - if (i == annotatedParamListIndex) ps.map(p => ref(p.symbol)).take(nextParamIndex) ++ defaultCalls - else ps.map(p => ref(p.symbol)) + val forwarderCall0 = forwarderCallArgs.foldLeft[Tree](forwarderInner){ + case (lhs: Tree, newParams) => + if (newParams.headOption.exists(_.isInstanceOf[TypeTree])) TypeApply(lhs, newParams) + else Apply(lhs, newParams) } - lazy val forwarderCall0 = forwarderCallArgs.foldLeft[Tree](forwarderInner){ - case (lhs: Tree, newParams) => - if (newParams.headOption.exists(_.isInstanceOf[TypeTree])) TypeApply(lhs, newParams) - else Apply(lhs, newParams) + val forwarderCall = + if (!defdef.symbol.isConstructor) forwarderCall0 + else Block(List(forwarderCall0), Literal(Constant(()))) + + forwarderCall } - lazy val forwarderCall = - if (!defdef.symbol.isConstructor) forwarderCall0 - else Block(List(forwarderCall0), Literal(Constant(()))) - - val forwarderDef = implicitly[Context].typeAssigner.assignType( - cpy.DefDef(defdef)( - name = forwarderDefSymbol.name, - paramss = newParamLists, - tpt = defdef.tpt, - rhs = if (nextParamIndex == -1) EmptyTree else forwarderCall - ), - forwarderDefSymbol - ) + val forwarderDef = + tpd.DefDef(forwarderDefSymbol, + rhs = if (nextParamIndex == -1) EmptyTree else forwarderRhs()) forwarderDef } @@ -225,7 +211,6 @@ class UnrollDefs extends MiniPhase { paramIndex, nextSymbol, paramClauseIndex, - defdef.paramss, isCaseApply ) (forwarder +: defdefs, forwarder.symbol) @@ -245,7 +230,6 @@ class UnrollDefs extends MiniPhase { nextParamIndex, nextSymbol, paramClauseIndex, - defdef.paramss, isCaseApply ) (forwarder +: defdefs, forwarder.symbol) diff --git a/sbt-test/unroll-annot/genericMethod/build.sbt b/sbt-test/unroll-annot/genericMethod/build.sbt new file mode 100644 index 000000000000..5a46bfa9f4ec --- /dev/null +++ b/sbt-test/unroll-annot/genericMethod/build.sbt @@ -0,0 +1,56 @@ +lazy val utils = project.in(file("utils")) + +lazy val sharedSettings = Seq( + scalacOptions ++= Seq( + "-Ycheck:all", + "-experimental" + ) +) + +lazy val v1 = project.in(file("v1")) + .settings(sharedSettings) + +lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Compile / unmanagedClasspath := Seq( + Attributed.blank((v1 / Compile / classDirectory).value) + ), + ) + +lazy val v2 = project.in(file("v2")) + .settings(sharedSettings) + .settings( + scalacOptions += "Xprint:unroll", + // scalacOptions += "-Yplain-printer", + // scalacOptions += "-Xprint-types", + ) + +lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value) + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v2 / Compile / classDirectory).value) + ), + ) + +lazy val v3 = project.in(file("v3")) + .settings(sharedSettings) + +lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value), + // add v2_app, compiled against v2, to the classpath + Attributed.blank((v2_app / Runtime / classDirectory).value), + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v3 / Compile / classDirectory).value) + ), + ) diff --git a/sbt-test/unroll-annot/genericMethod/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/genericMethod/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..69f15d168bfc --- /dev/null +++ b/sbt-test/unroll-annot/genericMethod/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-source:3.0-migration" + ) +} diff --git a/sbt-test/unroll-annot/genericMethod/test b/sbt-test/unroll-annot/genericMethod/test new file mode 100644 index 000000000000..e8e500857b0e --- /dev/null +++ b/sbt-test/unroll-annot/genericMethod/test @@ -0,0 +1,14 @@ +# compile and run a basic version of Unrolled (v1), and an app that uses it +> v1/compile +> v1_app/runMain unroll.UnrollTestMainV1 +# add a field to the case class (v2), and update the app to use it, +# and ensure the old version (v1) still links +> v2/compile +> v2_app/runMain unroll.UnrollTestMainV1 +> v2_app/runMain unroll.UnrollTestMainV2 +# add a field to the case class (v3), and update the app to use it, +# and ensure the old versions (v1, v2) still link +> v3/compile +> v3_app/runMain unroll.UnrollTestMainV1 +> v3_app/runMain unroll.UnrollTestMainV2 +> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/genericMethod/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/genericMethod/utils/src/main/scala/TestUtils.scala new file mode 100644 index 000000000000..6ac413c9fe98 --- /dev/null +++ b/sbt-test/unroll-annot/genericMethod/utils/src/main/scala/TestUtils.scala @@ -0,0 +1,12 @@ +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/sbt-test/unroll-annot/genericMethod/v1/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/genericMethod/v1/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..3f14c4f31544 --- /dev/null +++ b/sbt-test/unroll-annot/genericMethod/v1/src/main/scala/Unrolled.scala @@ -0,0 +1,5 @@ +package unroll + +class Unrolled{ + def foo[T](s: T) = s.toString +} diff --git a/sbt-test/unroll-annot/genericMethod/v1_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/genericMethod/v1_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..f36e9dd07fed --- /dev/null +++ b/sbt-test/unroll-annot/genericMethod/v1_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,9 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV1{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled().foo("cow"), "cow") + } +} diff --git a/sbt-test/unroll-annot/genericMethod/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/genericMethod/v2/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..9b5566ef6b27 --- /dev/null +++ b/sbt-test/unroll-annot/genericMethod/v2/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +class Unrolled{ + def foo[T](s: T, @unroll n: Int = 1, b: Boolean = true) = s.toString + n + b +} diff --git a/sbt-test/unroll-annot/genericMethod/v2_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/genericMethod/v2_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..91b54aa9742c --- /dev/null +++ b/sbt-test/unroll-annot/genericMethod/v2_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,11 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV2{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled().foo("cow"), "cow1true") + logAssertStartsWith(new Unrolled().foo("cow", 2), "cow2true") + logAssertStartsWith(new Unrolled().foo("cow", 2, false), "cow2false") + } +} diff --git a/sbt-test/unroll-annot/genericMethod/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/genericMethod/v3/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..29b1948cffbd --- /dev/null +++ b/sbt-test/unroll-annot/genericMethod/v3/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +class Unrolled{ + def foo[T](s: T, @unroll n: Int = 1, b: Boolean = true, @unroll l: Long = 0) = s.toString + n + b + l +} diff --git a/sbt-test/unroll-annot/genericMethod/v3_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/genericMethod/v3_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..9a6daa3a9210 --- /dev/null +++ b/sbt-test/unroll-annot/genericMethod/v3_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,14 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV3{ + def main(args: Array[String]): Unit = { + UnrollTestPlatformSpecificV3() + + logAssertStartsWith(new Unrolled().foo("cow"), "cow1true0") + logAssertStartsWith(new Unrolled().foo("cow", 2), "cow2true0") + logAssertStartsWith(new Unrolled().foo("cow", 2, false), "cow2false0") + logAssertStartsWith(new Unrolled().foo("cow", 2, false, 3), "cow2false3") + } +} diff --git a/sbt-test/unroll-annot/genericMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/sbt-test/unroll-annot/genericMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala new file mode 100644 index 000000000000..7e2e913ea1f6 --- /dev/null +++ b/sbt-test/unroll-annot/genericMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala @@ -0,0 +1,27 @@ +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + val instance = new Unrolled() + val cls = classOf[Unrolled] + + assert( + cls.getMethod("foo", classOf[Object]).invoke(instance, "hello") == + "hello1true0" + ) + + assert(scala.util.Try(cls.getMethod("foo", classOf[Object], classOf[Int])).isFailure) + assert( + cls.getMethod("foo", classOf[Object], classOf[Int], classOf[Boolean]) + .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE) == + "hello2false0" + ) + assert( + cls.getMethod("foo", classOf[Object], classOf[Int], classOf[Boolean], classOf[Long]) + .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE, 3: Integer) == + "hello2false3" + ) + cls.getMethods.filter(_.getName.contains("foo")).foreach(println) + + } +} \ No newline at end of file From a93c3f0bd86aeb4870ccc99bf28d61a3a4241ad6 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 1 Oct 2024 13:17:21 +0200 Subject: [PATCH 05/35] SIP 61 - add abstractMethod tests, unlink replaced definitions --- .../tools/dotc/transform/UnrollDefs.scala | 26 +++++--- .../abstractClassMethod/build.sbt | 60 +++++++++++++++++++ .../project/DottyInjectedPlugin.scala | 12 ++++ .../unroll-annot/abstractClassMethod/test | 17 ++++++ .../utils/src/main/scala/TestUtils.scala | 12 ++++ .../v1/src/main/scala/Unrolled.scala | 5 ++ .../src/main/scala/UnrollTestMain.scala | 16 +++++ .../src/main/scala/Downstream.scala | 13 ++++ .../v2/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 17 ++++++ .../src/main/scala/src/Downstream.scala | 15 +++++ .../v3/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 21 +++++++ .../scala/UnrollTestPlatformSpecific.scala | 28 +++++++++ .../src/main/scala/Downstream.scala | 15 +++++ .../abstractTraitMethod/build.sbt | 60 +++++++++++++++++++ .../project/DottyInjectedPlugin.scala | 12 ++++ .../unroll-annot/abstractTraitMethod/test | 17 ++++++ .../utils/src/main/scala/TestUtils.scala | 12 ++++ .../v1/src/main/scala/Unrolled.scala | 5 ++ .../src/main/scala/UnrollTestMain.scala | 16 +++++ .../src/main/scala/Downstream.scala | 13 ++++ .../v2/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 17 ++++++ .../src/main/scala/Downstream.scala | 15 +++++ .../v3/src/main/scala/Unrolled.scala | 7 +++ .../src/main/scala/UnrollTestMain.scala | 21 +++++++ .../scala/UnrollTestPlatformSpecific.scala | 28 +++++++++ .../src/main/scala/Downstream.scala | 15 +++++ 29 files changed, 509 insertions(+), 7 deletions(-) create mode 100644 sbt-test/unroll-annot/abstractClassMethod/build.sbt create mode 100644 sbt-test/unroll-annot/abstractClassMethod/project/DottyInjectedPlugin.scala create mode 100644 sbt-test/unroll-annot/abstractClassMethod/test create mode 100644 sbt-test/unroll-annot/abstractClassMethod/utils/src/main/scala/TestUtils.scala create mode 100644 sbt-test/unroll-annot/abstractClassMethod/v1/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/abstractClassMethod/v1_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/abstractClassMethod/v1_downstream/src/main/scala/Downstream.scala create mode 100644 sbt-test/unroll-annot/abstractClassMethod/v2/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/abstractClassMethod/v2_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/abstractClassMethod/v2_downstream/src/main/scala/src/Downstream.scala create mode 100644 sbt-test/unroll-annot/abstractClassMethod/v3/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/abstractClassMethod/v3_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/abstractClassMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala create mode 100644 sbt-test/unroll-annot/abstractClassMethod/v3_downstream/src/main/scala/Downstream.scala create mode 100644 sbt-test/unroll-annot/abstractTraitMethod/build.sbt create mode 100644 sbt-test/unroll-annot/abstractTraitMethod/project/DottyInjectedPlugin.scala create mode 100644 sbt-test/unroll-annot/abstractTraitMethod/test create mode 100644 sbt-test/unroll-annot/abstractTraitMethod/utils/src/main/scala/TestUtils.scala create mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v1/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v1_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v1_downstream/src/main/scala/Downstream.scala create mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v2/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v2_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v2_downstream/src/main/scala/Downstream.scala create mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v3/src/main/scala/Unrolled.scala create mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v3_app/src/main/scala/UnrollTestMain.scala create mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala create mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v3_downstream/src/main/scala/Downstream.scala diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala index 3ed2460a267f..a13ae54e3b6f 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala @@ -166,7 +166,7 @@ class UnrollDefs extends MiniPhase { ).setDefTree } - def generateSyntheticDefs(tree: Tree)(using Context): (Option[Symbol], Seq[Tree]) = tree match{ + def generateSyntheticDefs(tree: Tree)(using Context): (Option[(Symbol, Seq[Symbol])], Seq[Tree]) = tree match{ case defdef: DefDef if defdef.paramss.nonEmpty => import dotty.tools.dotc.core.NameOps.isConstructorName @@ -197,11 +197,12 @@ class UnrollDefs extends MiniPhase { case Seq((paramClauseIndex, annotationIndices)) => val paramCount = annotated.paramSymss(paramClauseIndex).size if (isCaseFromProduct) { - (Some(defdef.symbol), Seq(generateFromProduct(annotationIndices, paramCount, defdef))) + val newDef = generateFromProduct(annotationIndices, paramCount, defdef) + (Some(defdef.symbol, Seq(newDef.symbol)), Seq(newDef)) } else { if (defdef.symbol.is(Deferred)){ - ( - Some(defdef.symbol), + val replacements = Seq.newBuilder[Symbol] + val newDefs = (-1 +: annotationIndices :+ paramCount).sliding(2).toList.foldLeft((Seq.empty[DefDef], defdef.symbol))((m, v) => ((m, v): @unchecked) match { case ((defdefs, nextSymbol), Seq(paramIndex, nextParamIndex)) => val forwarder = generateSingleForwarder( @@ -213,8 +214,12 @@ class UnrollDefs extends MiniPhase { paramClauseIndex, isCaseApply ) + replacements += forwarder.symbol (forwarder +: defdefs, forwarder.symbol) })._1 + ( + Some(defdef.symbol, replacements.result()), + newDefs ) }else{ @@ -247,8 +252,15 @@ class UnrollDefs extends MiniPhase { override def transformTemplate(tmpl: tpd.Template)(using Context): tpd.Tree = { val (removed0, generatedDefs) = tmpl.body.map(generateSyntheticDefs).unzip - val (_, generatedConstr) = generateSyntheticDefs(tmpl.constr) - val removed = removed0.flatten + val (removedCtor, generatedConstr) = generateSyntheticDefs(tmpl.constr) + val removedFlat = removed0.flatten + val removedSymsBody = removedFlat.map(_(0)) + val allRemoved = removedFlat ++ removedCtor + for (sym, replacements) <- allRemoved do + def totalParamCount(sym: Symbol): Int = sym.paramSymss.view.map(_.size).sum + val symParamCount = totalParamCount(sym) + val replaced = replacements.find(totalParamCount(_) == symParamCount).get + sym.owner.asClass.replace(sym, replaced) super.transformTemplate( cpy.Template(tmpl)( @@ -256,7 +268,7 @@ class UnrollDefs extends MiniPhase { tmpl.parents, tmpl.derived, tmpl.self, - tmpl.body.filter(t => !removed.contains(t.symbol)) ++ generatedDefs.flatten ++ generatedConstr + tmpl.body.filter(t => !removedSymsBody.contains(t.symbol)) ++ generatedDefs.flatten ++ generatedConstr ) ) } diff --git a/sbt-test/unroll-annot/abstractClassMethod/build.sbt b/sbt-test/unroll-annot/abstractClassMethod/build.sbt new file mode 100644 index 000000000000..a2d4a103ef5f --- /dev/null +++ b/sbt-test/unroll-annot/abstractClassMethod/build.sbt @@ -0,0 +1,60 @@ +lazy val utils = project.in(file("utils")) + +lazy val sharedSettings = Seq( + scalacOptions ++= Seq("-Ycheck:all", "-experimental") +) + +lazy val v1 = project.in(file("v1")) + .settings(sharedSettings) + +lazy val v1_downstream = project.in(file("v1_downstream")).dependsOn(v1) + .settings(sharedSettings) + +lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Compile / unmanagedClasspath := Seq( + Attributed.blank((v1 / Compile / classDirectory).value), + Attributed.blank((v1_downstream / Compile / classDirectory).value), + ), + ) + +lazy val v2 = project.in(file("v2")) + .settings(sharedSettings) + +lazy val v2_downstream = project.in(file("v2_downstream")).dependsOn(v2) + .settings(sharedSettings) + +lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value) + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v2 / Compile / classDirectory).value), + Attributed.blank((v2_downstream / Compile / classDirectory).value) + ), + ) + +lazy val v3 = project.in(file("v3")) + .settings(sharedSettings) + +lazy val v3_downstream = project.in(file("v3_downstream")).dependsOn(v3) + .settings(sharedSettings) + +lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value), + // add v2_app, compiled against v2, to the classpath + Attributed.blank((v2_app / Runtime / classDirectory).value), + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v3 / Compile / classDirectory).value), + Attributed.blank((v3_downstream / Compile / classDirectory).value) + ), + ) diff --git a/sbt-test/unroll-annot/abstractClassMethod/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/abstractClassMethod/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..69f15d168bfc --- /dev/null +++ b/sbt-test/unroll-annot/abstractClassMethod/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-source:3.0-migration" + ) +} diff --git a/sbt-test/unroll-annot/abstractClassMethod/test b/sbt-test/unroll-annot/abstractClassMethod/test new file mode 100644 index 000000000000..7c0875518e21 --- /dev/null +++ b/sbt-test/unroll-annot/abstractClassMethod/test @@ -0,0 +1,17 @@ +# compile and run a basic version of Unrolled (v1), and an app that uses it +> v1/compile +> v1_downstream/compile +> v1_app/runMain unroll.UnrollTestMainV1 +# add a field to the case class (v2), and update the app to use it, +# and ensure the old version (v1) still links +> v2/compile +> v2_downstream/compile +> v2_app/runMain unroll.UnrollTestMainV1 +> v2_app/runMain unroll.UnrollTestMainV2 +# add a field to the case class (v3), and update the app to use it, +# and ensure the old versions (v1, v2) still link +> v3/compile +> v3_downstream/compile +> v3_app/runMain unroll.UnrollTestMainV1 +> v3_app/runMain unroll.UnrollTestMainV2 +> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/abstractClassMethod/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/abstractClassMethod/utils/src/main/scala/TestUtils.scala new file mode 100644 index 000000000000..6ac413c9fe98 --- /dev/null +++ b/sbt-test/unroll-annot/abstractClassMethod/utils/src/main/scala/TestUtils.scala @@ -0,0 +1,12 @@ +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/sbt-test/unroll-annot/abstractClassMethod/v1/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/abstractClassMethod/v1/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..4ff7eac61475 --- /dev/null +++ b/sbt-test/unroll-annot/abstractClassMethod/v1/src/main/scala/Unrolled.scala @@ -0,0 +1,5 @@ +package unroll + +abstract class Unrolled{ + def foo(s: String, n: Int = 1): String +} diff --git a/sbt-test/unroll-annot/abstractClassMethod/v1_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/abstractClassMethod/v1_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..d069e35063b7 --- /dev/null +++ b/sbt-test/unroll-annot/abstractClassMethod/v1_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,16 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + + + +object UnrollTestMainV1{ + def main(args: Array[String]): Unit = { + val unrolled = new UnrolledCls + logAssertStartsWith(unrolled.foo("cow"), "cow1") + logAssertStartsWith(unrolled.foo("cow", 2), "cow2") + + logAssertStartsWith(UnrolledObj.foo("cow"), "cow1") + logAssertStartsWith(UnrolledObj.foo("cow", 2), "cow2") + } +} diff --git a/sbt-test/unroll-annot/abstractClassMethod/v1_downstream/src/main/scala/Downstream.scala b/sbt-test/unroll-annot/abstractClassMethod/v1_downstream/src/main/scala/Downstream.scala new file mode 100644 index 000000000000..69b33c82b1c7 --- /dev/null +++ b/sbt-test/unroll-annot/abstractClassMethod/v1_downstream/src/main/scala/Downstream.scala @@ -0,0 +1,13 @@ +package unroll + +object UnrolledObj extends Unrolled { + def foo(s: String, n: Int = 1) = s + n +} + +class UnrolledCls extends Unrolled { + def foo(s: String, n: Int = 1) = s + n +} + +object UnrollMisc{ + def expectedLength = 4 +} \ No newline at end of file diff --git a/sbt-test/unroll-annot/abstractClassMethod/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/abstractClassMethod/v2/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..ab870c5fda3e --- /dev/null +++ b/sbt-test/unroll-annot/abstractClassMethod/v2/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +abstract class Unrolled{ + def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String +} diff --git a/sbt-test/unroll-annot/abstractClassMethod/v2_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/abstractClassMethod/v2_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..fd7a891222fc --- /dev/null +++ b/sbt-test/unroll-annot/abstractClassMethod/v2_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,17 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + + +object UnrollTestMainV2{ + def main(args: Array[String]): Unit = { + val unrolled = new UnrolledCls + logAssertStartsWith(unrolled.foo("cow"), "cow1true".take(UnrollMisc.expectedLength)) + logAssertStartsWith(unrolled.foo("cow", 2), "cow2true".take(UnrollMisc.expectedLength)) + logAssertStartsWith(unrolled.foo("cow", 2, false), "cow2fals".take(UnrollMisc.expectedLength)) + + logAssertStartsWith(UnrolledObj.foo("cow"), "cow1true".take(UnrollMisc.expectedLength)) + logAssertStartsWith(UnrolledObj.foo("cow", 2), "cow2true".take(UnrollMisc.expectedLength)) + logAssertStartsWith(UnrolledObj.foo("cow", 2, false), "cow2fals".take(UnrollMisc.expectedLength)) + } +} diff --git a/sbt-test/unroll-annot/abstractClassMethod/v2_downstream/src/main/scala/src/Downstream.scala b/sbt-test/unroll-annot/abstractClassMethod/v2_downstream/src/main/scala/src/Downstream.scala new file mode 100644 index 000000000000..e0545e97a42f --- /dev/null +++ b/sbt-test/unroll-annot/abstractClassMethod/v2_downstream/src/main/scala/src/Downstream.scala @@ -0,0 +1,15 @@ +package unroll + +import scala.annotation.unroll + +object UnrolledObj extends Unrolled { + def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b.toString.take(4) +} + +class UnrolledCls extends Unrolled { + def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b.toString.take(4) +} + +object UnrollMisc{ + def expectedLength = 6 +} \ No newline at end of file diff --git a/sbt-test/unroll-annot/abstractClassMethod/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/abstractClassMethod/v3/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..991407a0da45 --- /dev/null +++ b/sbt-test/unroll-annot/abstractClassMethod/v3/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +abstract class Unrolled{ + def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0): String +} diff --git a/sbt-test/unroll-annot/abstractClassMethod/v3_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/abstractClassMethod/v3_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..904538003026 --- /dev/null +++ b/sbt-test/unroll-annot/abstractClassMethod/v3_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,21 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + + +object UnrollTestMainV3{ + def main(args: Array[String]): Unit = { + UnrollTestPlatformSpecificV3() + + val unrolled = new UnrolledCls + logAssertStartsWith(unrolled.foo("cow"), "cow1true0".take(UnrollMisc.expectedLength)) + logAssertStartsWith(unrolled.foo("cow", 2), "cow2true0".take(UnrollMisc.expectedLength)) + logAssertStartsWith(unrolled.foo("cow", 2, false), "cow2fals0".take(UnrollMisc.expectedLength)) + logAssertStartsWith(unrolled.foo("cow", 2, false, 3), "cow2fals3".take(UnrollMisc.expectedLength)) + + logAssertStartsWith(UnrolledObj.foo("cow"), "cow1true0".take(UnrollMisc.expectedLength)) + logAssertStartsWith(UnrolledObj.foo("cow", 2), "cow2true0".take(UnrollMisc.expectedLength)) + logAssertStartsWith(UnrolledObj.foo("cow", 2, false), "cow2fals0".take(UnrollMisc.expectedLength)) + logAssertStartsWith(UnrolledObj.foo("cow", 2, false, 3), "cow2fals3".take(UnrollMisc.expectedLength)) + } +} diff --git a/sbt-test/unroll-annot/abstractClassMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/sbt-test/unroll-annot/abstractClassMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala new file mode 100644 index 000000000000..feef2fe677da --- /dev/null +++ b/sbt-test/unroll-annot/abstractClassMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala @@ -0,0 +1,28 @@ +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { +// val instance = new UnrolledCls {} +// val cls = classOf[UnrolledCls] +// +// cls.getMethods.filter(_.getName.contains("foo")).foreach(println) +// +// assert(scala.util.Try(cls.getMethod("foo", classOf[String])).isFailure) +// println() +// assert( +// cls.getMethod("foo", classOf[String], classOf[Int]).invoke(instance, "hello", 2: Integer) == +// "hello2true0" +// ) +// assert( +// cls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean]) +// .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE) == +// "hello2false0" +// ) +// assert( +// cls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean], classOf[Long]) +// .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE, 3: Integer) == +// "hello2false3" +// ) + + } +} \ No newline at end of file diff --git a/sbt-test/unroll-annot/abstractClassMethod/v3_downstream/src/main/scala/Downstream.scala b/sbt-test/unroll-annot/abstractClassMethod/v3_downstream/src/main/scala/Downstream.scala new file mode 100644 index 000000000000..be2a68e8efff --- /dev/null +++ b/sbt-test/unroll-annot/abstractClassMethod/v3_downstream/src/main/scala/Downstream.scala @@ -0,0 +1,15 @@ +package unroll + +import scala.annotation.unroll + +object UnrolledObj extends Unrolled { + def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0) = s + n + b.toString.take(4) + l +} + +class UnrolledCls extends Unrolled { + def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0) = s + n + b.toString.take(4) + l +} + +object UnrollMisc{ + def expectedLength = 9 +} \ No newline at end of file diff --git a/sbt-test/unroll-annot/abstractTraitMethod/build.sbt b/sbt-test/unroll-annot/abstractTraitMethod/build.sbt new file mode 100644 index 000000000000..a2d4a103ef5f --- /dev/null +++ b/sbt-test/unroll-annot/abstractTraitMethod/build.sbt @@ -0,0 +1,60 @@ +lazy val utils = project.in(file("utils")) + +lazy val sharedSettings = Seq( + scalacOptions ++= Seq("-Ycheck:all", "-experimental") +) + +lazy val v1 = project.in(file("v1")) + .settings(sharedSettings) + +lazy val v1_downstream = project.in(file("v1_downstream")).dependsOn(v1) + .settings(sharedSettings) + +lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Compile / unmanagedClasspath := Seq( + Attributed.blank((v1 / Compile / classDirectory).value), + Attributed.blank((v1_downstream / Compile / classDirectory).value), + ), + ) + +lazy val v2 = project.in(file("v2")) + .settings(sharedSettings) + +lazy val v2_downstream = project.in(file("v2_downstream")).dependsOn(v2) + .settings(sharedSettings) + +lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value) + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v2 / Compile / classDirectory).value), + Attributed.blank((v2_downstream / Compile / classDirectory).value) + ), + ) + +lazy val v3 = project.in(file("v3")) + .settings(sharedSettings) + +lazy val v3_downstream = project.in(file("v3_downstream")).dependsOn(v3) + .settings(sharedSettings) + +lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) + .settings(sharedSettings) + .settings( + Runtime / unmanagedClasspath := Seq( + // add v1_app, compiled against v1, to the classpath + Attributed.blank((v1_app / Runtime / classDirectory).value), + // add v2_app, compiled against v2, to the classpath + Attributed.blank((v2_app / Runtime / classDirectory).value), + ), + Compile / unmanagedClasspath := Seq( + Attributed.blank((v3 / Compile / classDirectory).value), + Attributed.blank((v3_downstream / Compile / classDirectory).value) + ), + ) diff --git a/sbt-test/unroll-annot/abstractTraitMethod/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/abstractTraitMethod/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..69f15d168bfc --- /dev/null +++ b/sbt-test/unroll-annot/abstractTraitMethod/project/DottyInjectedPlugin.scala @@ -0,0 +1,12 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion"), + scalacOptions += "-source:3.0-migration" + ) +} diff --git a/sbt-test/unroll-annot/abstractTraitMethod/test b/sbt-test/unroll-annot/abstractTraitMethod/test new file mode 100644 index 000000000000..7c0875518e21 --- /dev/null +++ b/sbt-test/unroll-annot/abstractTraitMethod/test @@ -0,0 +1,17 @@ +# compile and run a basic version of Unrolled (v1), and an app that uses it +> v1/compile +> v1_downstream/compile +> v1_app/runMain unroll.UnrollTestMainV1 +# add a field to the case class (v2), and update the app to use it, +# and ensure the old version (v1) still links +> v2/compile +> v2_downstream/compile +> v2_app/runMain unroll.UnrollTestMainV1 +> v2_app/runMain unroll.UnrollTestMainV2 +# add a field to the case class (v3), and update the app to use it, +# and ensure the old versions (v1, v2) still link +> v3/compile +> v3_downstream/compile +> v3_app/runMain unroll.UnrollTestMainV1 +> v3_app/runMain unroll.UnrollTestMainV2 +> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/abstractTraitMethod/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/abstractTraitMethod/utils/src/main/scala/TestUtils.scala new file mode 100644 index 000000000000..6ac413c9fe98 --- /dev/null +++ b/sbt-test/unroll-annot/abstractTraitMethod/utils/src/main/scala/TestUtils.scala @@ -0,0 +1,12 @@ +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v1/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/abstractTraitMethod/v1/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..a80db5e1fa73 --- /dev/null +++ b/sbt-test/unroll-annot/abstractTraitMethod/v1/src/main/scala/Unrolled.scala @@ -0,0 +1,5 @@ +package unroll + +trait Unrolled{ + def foo(s: String, n: Int = 1): String +} diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v1_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/abstractTraitMethod/v1_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..d069e35063b7 --- /dev/null +++ b/sbt-test/unroll-annot/abstractTraitMethod/v1_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,16 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + + + +object UnrollTestMainV1{ + def main(args: Array[String]): Unit = { + val unrolled = new UnrolledCls + logAssertStartsWith(unrolled.foo("cow"), "cow1") + logAssertStartsWith(unrolled.foo("cow", 2), "cow2") + + logAssertStartsWith(UnrolledObj.foo("cow"), "cow1") + logAssertStartsWith(UnrolledObj.foo("cow", 2), "cow2") + } +} diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v1_downstream/src/main/scala/Downstream.scala b/sbt-test/unroll-annot/abstractTraitMethod/v1_downstream/src/main/scala/Downstream.scala new file mode 100644 index 000000000000..69b33c82b1c7 --- /dev/null +++ b/sbt-test/unroll-annot/abstractTraitMethod/v1_downstream/src/main/scala/Downstream.scala @@ -0,0 +1,13 @@ +package unroll + +object UnrolledObj extends Unrolled { + def foo(s: String, n: Int = 1) = s + n +} + +class UnrolledCls extends Unrolled { + def foo(s: String, n: Int = 1) = s + n +} + +object UnrollMisc{ + def expectedLength = 4 +} \ No newline at end of file diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/abstractTraitMethod/v2/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..f701334aee60 --- /dev/null +++ b/sbt-test/unroll-annot/abstractTraitMethod/v2/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +trait Unrolled{ + def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String +} diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v2_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/abstractTraitMethod/v2_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..fd7a891222fc --- /dev/null +++ b/sbt-test/unroll-annot/abstractTraitMethod/v2_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,17 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + + +object UnrollTestMainV2{ + def main(args: Array[String]): Unit = { + val unrolled = new UnrolledCls + logAssertStartsWith(unrolled.foo("cow"), "cow1true".take(UnrollMisc.expectedLength)) + logAssertStartsWith(unrolled.foo("cow", 2), "cow2true".take(UnrollMisc.expectedLength)) + logAssertStartsWith(unrolled.foo("cow", 2, false), "cow2fals".take(UnrollMisc.expectedLength)) + + logAssertStartsWith(UnrolledObj.foo("cow"), "cow1true".take(UnrollMisc.expectedLength)) + logAssertStartsWith(UnrolledObj.foo("cow", 2), "cow2true".take(UnrollMisc.expectedLength)) + logAssertStartsWith(UnrolledObj.foo("cow", 2, false), "cow2fals".take(UnrollMisc.expectedLength)) + } +} diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v2_downstream/src/main/scala/Downstream.scala b/sbt-test/unroll-annot/abstractTraitMethod/v2_downstream/src/main/scala/Downstream.scala new file mode 100644 index 000000000000..e0545e97a42f --- /dev/null +++ b/sbt-test/unroll-annot/abstractTraitMethod/v2_downstream/src/main/scala/Downstream.scala @@ -0,0 +1,15 @@ +package unroll + +import scala.annotation.unroll + +object UnrolledObj extends Unrolled { + def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b.toString.take(4) +} + +class UnrolledCls extends Unrolled { + def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b.toString.take(4) +} + +object UnrollMisc{ + def expectedLength = 6 +} \ No newline at end of file diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/abstractTraitMethod/v3/src/main/scala/Unrolled.scala new file mode 100644 index 000000000000..fb26b7c114b1 --- /dev/null +++ b/sbt-test/unroll-annot/abstractTraitMethod/v3/src/main/scala/Unrolled.scala @@ -0,0 +1,7 @@ +package unroll + +import scala.annotation.unroll + +trait Unrolled{ + def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0): String +} diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v3_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/abstractTraitMethod/v3_app/src/main/scala/UnrollTestMain.scala new file mode 100644 index 000000000000..904538003026 --- /dev/null +++ b/sbt-test/unroll-annot/abstractTraitMethod/v3_app/src/main/scala/UnrollTestMain.scala @@ -0,0 +1,21 @@ +package unroll + +import unroll.TestUtils.logAssertStartsWith + + +object UnrollTestMainV3{ + def main(args: Array[String]): Unit = { + UnrollTestPlatformSpecificV3() + + val unrolled = new UnrolledCls + logAssertStartsWith(unrolled.foo("cow"), "cow1true0".take(UnrollMisc.expectedLength)) + logAssertStartsWith(unrolled.foo("cow", 2), "cow2true0".take(UnrollMisc.expectedLength)) + logAssertStartsWith(unrolled.foo("cow", 2, false), "cow2fals0".take(UnrollMisc.expectedLength)) + logAssertStartsWith(unrolled.foo("cow", 2, false, 3), "cow2fals3".take(UnrollMisc.expectedLength)) + + logAssertStartsWith(UnrolledObj.foo("cow"), "cow1true0".take(UnrollMisc.expectedLength)) + logAssertStartsWith(UnrolledObj.foo("cow", 2), "cow2true0".take(UnrollMisc.expectedLength)) + logAssertStartsWith(UnrolledObj.foo("cow", 2, false), "cow2fals0".take(UnrollMisc.expectedLength)) + logAssertStartsWith(UnrolledObj.foo("cow", 2, false, 3), "cow2fals3".take(UnrollMisc.expectedLength)) + } +} diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/sbt-test/unroll-annot/abstractTraitMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala new file mode 100644 index 000000000000..feef2fe677da --- /dev/null +++ b/sbt-test/unroll-annot/abstractTraitMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala @@ -0,0 +1,28 @@ +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { +// val instance = new UnrolledCls {} +// val cls = classOf[UnrolledCls] +// +// cls.getMethods.filter(_.getName.contains("foo")).foreach(println) +// +// assert(scala.util.Try(cls.getMethod("foo", classOf[String])).isFailure) +// println() +// assert( +// cls.getMethod("foo", classOf[String], classOf[Int]).invoke(instance, "hello", 2: Integer) == +// "hello2true0" +// ) +// assert( +// cls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean]) +// .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE) == +// "hello2false0" +// ) +// assert( +// cls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean], classOf[Long]) +// .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE, 3: Integer) == +// "hello2false3" +// ) + + } +} \ No newline at end of file diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v3_downstream/src/main/scala/Downstream.scala b/sbt-test/unroll-annot/abstractTraitMethod/v3_downstream/src/main/scala/Downstream.scala new file mode 100644 index 000000000000..be2a68e8efff --- /dev/null +++ b/sbt-test/unroll-annot/abstractTraitMethod/v3_downstream/src/main/scala/Downstream.scala @@ -0,0 +1,15 @@ +package unroll + +import scala.annotation.unroll + +object UnrolledObj extends Unrolled { + def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0) = s + n + b.toString.take(4) + l +} + +class UnrolledCls extends Unrolled { + def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0) = s + n + b.toString.take(4) + l +} + +object UnrollMisc{ + def expectedLength = 9 +} \ No newline at end of file From e2c298ecdf32df55c82c04d739d76453eea88929 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 1 Oct 2024 13:45:48 +0200 Subject: [PATCH 06/35] SIP 61 - detect duplicates --- .../tools/dotc/transform/UnrollDefs.scala | 55 +++++++++++++++---- tests/neg/unroll-clash.check | 6 ++ tests/neg/unroll-clash.scala | 11 ++++ 3 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 tests/neg/unroll-clash.check create mode 100644 tests/neg/unroll-clash.scala diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala index a13ae54e3b6f..72e61e3f45cc 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala @@ -16,8 +16,10 @@ import Constants.Constant import dotty.tools.dotc.core.NameKinds.DefaultGetterName import dotty.tools.dotc.core.Types.{MethodType, NamedType, PolyType, Type} import dotty.tools.dotc.core.Symbols +import dotty.tools.dotc.printing.Formatting.hl import dotty.tools.dotc.core.Types.NoType +import dotty.tools.dotc.typer.Checking class UnrollDefs extends MiniPhase { import tpd._ @@ -166,7 +168,7 @@ class UnrollDefs extends MiniPhase { ).setDefTree } - def generateSyntheticDefs(tree: Tree)(using Context): (Option[(Symbol, Seq[Symbol])], Seq[Tree]) = tree match{ + def generateSyntheticDefs(tree: Tree)(using Context): (Option[(Symbol, Seq[Symbol])], Seq[(Symbol, Tree)]) = tree match{ case defdef: DefDef if defdef.paramss.nonEmpty => import dotty.tools.dotc.core.NameOps.isConstructorName @@ -198,12 +200,12 @@ class UnrollDefs extends MiniPhase { val paramCount = annotated.paramSymss(paramClauseIndex).size if (isCaseFromProduct) { val newDef = generateFromProduct(annotationIndices, paramCount, defdef) - (Some(defdef.symbol, Seq(newDef.symbol)), Seq(newDef)) + (Some(defdef.symbol, Seq(newDef.symbol)), Seq(defdef.symbol -> newDef)) } else { if (defdef.symbol.is(Deferred)){ val replacements = Seq.newBuilder[Symbol] val newDefs = - (-1 +: annotationIndices :+ paramCount).sliding(2).toList.foldLeft((Seq.empty[DefDef], defdef.symbol))((m, v) => ((m, v): @unchecked) match { + (-1 +: annotationIndices :+ paramCount).sliding(2).toList.foldLeft((Seq.empty[(Symbol, DefDef)], defdef.symbol))((m, v) => ((m, v): @unchecked) match { case ((defdefs, nextSymbol), Seq(paramIndex, nextParamIndex)) => val forwarder = generateSingleForwarder( defdef, @@ -215,7 +217,7 @@ class UnrollDefs extends MiniPhase { isCaseApply ) replacements += forwarder.symbol - (forwarder +: defdefs, forwarder.symbol) + ((defdef.symbol -> forwarder) +: defdefs, forwarder.symbol) })._1 ( Some(defdef.symbol, replacements.result()), @@ -226,7 +228,7 @@ class UnrollDefs extends MiniPhase { ( None, - (annotationIndices :+ paramCount).sliding(2).toList.reverse.foldLeft((Seq.empty[DefDef], defdef.symbol))((m, v) => ((m, v): @unchecked) match { + (annotationIndices :+ paramCount).sliding(2).toList.reverse.foldLeft((Seq.empty[(Symbol, DefDef)], defdef.symbol))((m, v) => ((m, v): @unchecked) match { case ((defdefs, nextSymbol), Seq(paramIndex, nextParamIndex)) => val forwarder = generateSingleForwarder( defdef, @@ -237,7 +239,7 @@ class UnrollDefs extends MiniPhase { paramClauseIndex, isCaseApply ) - (forwarder +: defdefs, forwarder.symbol) + ((defdef.symbol -> forwarder) +: defdefs, forwarder.symbol) })._1 ) } @@ -251,16 +253,49 @@ class UnrollDefs extends MiniPhase { override def transformTemplate(tmpl: tpd.Template)(using Context): tpd.Tree = { - val (removed0, generatedDefs) = tmpl.body.map(generateSyntheticDefs).unzip - val (removedCtor, generatedConstr) = generateSyntheticDefs(tmpl.constr) + val (removed0, generatedDefs0) = tmpl.body.map(generateSyntheticDefs).unzip + val (removedCtor, generatedConstr0) = generateSyntheticDefs(tmpl.constr) val removedFlat = removed0.flatten val removedSymsBody = removedFlat.map(_(0)) val allRemoved = removedFlat ++ removedCtor + + val generatedDefOrigins = generatedDefs0.flatten + val generatedDefs = generatedDefOrigins.map(_(1)) + val generatedConstr = generatedConstr0.map(_(1)) + + val otherDecls = tmpl.body.filter(t => !removedSymsBody.contains(t.symbol)) + for (sym, replacements) <- allRemoved do + val cls = sym.owner.asClass def totalParamCount(sym: Symbol): Int = sym.paramSymss.view.map(_.size).sum val symParamCount = totalParamCount(sym) val replaced = replacements.find(totalParamCount(_) == symParamCount).get - sym.owner.asClass.replace(sym, replaced) + cls.replace(sym, replaced) + + /** inlined from compiler/src/dotty/tools/dotc/typer/Checking.scala */ + def checkClash(decl: Symbol, other: Symbol) = + def staticNonStaticPair = decl.isScalaStatic != other.isScalaStatic + decl.matches(other) && !staticNonStaticPair + + if generatedDefOrigins.nonEmpty then + val byName = otherDecls.groupMap(_.symbol.name.toString)(_.symbol) + for case (src, dcl: NamedDefTree) <- generatedDefOrigins do + val replaced = dcl.symbol + def symLocation(sym: Symbol) = { + val lineDesc = + if (sym.span.exists && sym.span != sym.owner.span) + s" at line ${sym.srcPos.line + 1}" + else "" + i"in ${sym.owner}${lineDesc}" + } + byName.get(dcl.name.toString).foreach { syms => + val clashes = syms.filter(checkClash(replaced, _)) + for existing <- clashes do + report.error(i"""Unrolled $replaced clashes with existing declaration. + |Please remove the clashing definition, or the @unroll annotation. + |Unrolled from ${hl(src.showDcl)} ${symLocation(src)}""".stripMargin, existing.srcPos) + } + end if super.transformTemplate( cpy.Template(tmpl)( @@ -268,7 +303,7 @@ class UnrollDefs extends MiniPhase { tmpl.parents, tmpl.derived, tmpl.self, - tmpl.body.filter(t => !removedSymsBody.contains(t.symbol)) ++ generatedDefs.flatten ++ generatedConstr + otherDecls ++ generatedDefs ++ generatedConstr ) ) } diff --git a/tests/neg/unroll-clash.check b/tests/neg/unroll-clash.check new file mode 100644 index 000000000000..9483e4611e7c --- /dev/null +++ b/tests/neg/unroll-clash.check @@ -0,0 +1,6 @@ +-- Error: tests/neg/unroll-clash.scala:7:6 ----------------------------------------------------------------------------- +7 | def foo(x: Int) = { // error + | ^ + | Unrolled method foo clashes with existing declaration. + | Please remove the clashing definition, or the @unroll annotation. + | Unrolled from def foo(x: Int, y: Int): Int in class Foo at line 6 diff --git a/tests/neg/unroll-clash.scala b/tests/neg/unroll-clash.scala new file mode 100644 index 000000000000..b6d146f75d76 --- /dev/null +++ b/tests/neg/unroll-clash.scala @@ -0,0 +1,11 @@ +//> using options -experimental + +import scala.annotation.unroll + +class Foo { + def foo(x: Int, @unroll y: Int = 0) = x + y + def foo(x: Int) = { // error + println("Not binary compatible!") + -1 + } +} From 1633c9c6d028f9216dbd18be757492e6d5d63ca2 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 1 Oct 2024 17:02:46 +0200 Subject: [PATCH 07/35] SIP 61 - check for default parameter --- .../tools/dotc/transform/UnrollDefs.scala | 58 +++++++++++-------- tests/neg/unroll-no-default.check | 4 ++ tests/neg/unroll-no-default.scala | 7 +++ 3 files changed, 46 insertions(+), 23 deletions(-) create mode 100644 tests/neg/unroll-no-default.check create mode 100644 tests/neg/unroll-no-default.scala diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala index 72e61e3f45cc..79330f4bcc10 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala @@ -14,7 +14,7 @@ import StdNames.nme import Names.* import Constants.Constant import dotty.tools.dotc.core.NameKinds.DefaultGetterName -import dotty.tools.dotc.core.Types.{MethodType, NamedType, PolyType, Type} +import dotty.tools.dotc.core.Types.{MethodType, NamedType, PolyType, Type, NoPrefix} import dotty.tools.dotc.core.Symbols import dotty.tools.dotc.printing.Formatting.hl @@ -31,6 +31,14 @@ class UnrollDefs extends MiniPhase { def copyParamSym(sym: Symbol, parent: Symbol)(using Context): (Symbol, Symbol) = sym -> sym.copy(owner = parent) + def symLocation(sym: Symbol)(using Context) = { + val lineDesc = + if (sym.span.exists && sym.span != sym.owner.span) + s" at line ${sym.srcPos.line + 1}" + else "" + i"in ${sym.owner}${lineDesc}" + } + def findUnrollAnnotations(params: List[Symbol])(using Context): List[Int] = { params .zipWithIndex @@ -87,33 +95,44 @@ class UnrollDefs extends MiniPhase { .sum val defaultCalls = Range(paramIndex, nextParamIndex).map(n => + + def makeSelect(ref: Tree, name: TermName): Tree = + val sym = ref.symbol + if !sym.findMember(name, NoPrefix, EmptyFlags, EmptyFlags).exists then + val param = defdef.paramss(annotatedParamListIndex)(n) + val methodStr = s"method ${defdef.name} ${symLocation(defdef.symbol)}" + val paramStr = s"parameter ${param.name}" + report.error(i"Cannot unroll $methodStr because $paramStr needs a default value", param.srcPos) + EmptyTree + else + ref.select(name) + val inner = if (defdef.symbol.isConstructor) { - ref(defdef.symbol.owner.companionModule) - .select(DefaultGetterName(defdef.name, n + defaultOffset)) + makeSelect(ref(defdef.symbol.owner.companionModule), + DefaultGetterName(defdef.name, n + defaultOffset)) } else if (isCaseApply) { - ref(defdef.symbol.owner.companionModule) - .select(DefaultGetterName(termName(""), n + defaultOffset)) + makeSelect(ref(defdef.symbol.owner.companionModule), + DefaultGetterName(termName(""), n + defaultOffset)) } else { - This(defdef.symbol.owner.asClass) - .select(DefaultGetterName(defdef.name, n + defaultOffset)) + makeSelect(This(defdef.symbol.owner.asClass), + DefaultGetterName(defdef.name, n + defaultOffset)) } - newParamSymLists + if inner.isEmpty then EmptyTree + else newParamSymLists .take(annotatedParamListIndex) - .map(_.map(p => ref(p))) - .foldLeft[Tree](inner){ - case (lhs: Tree, newParams) => - if (newParams.headOption.exists(_.isInstanceOf[TypeTree])) TypeApply(lhs, newParams) - else Apply(lhs, newParams) - } + .map(_.map(ref)) + .foldLeft(inner): (lhs, newParams) => + if (newParams.headOption.exists(_.isInstanceOf[TypeTree])) TypeApply(lhs, newParams) + else Apply(lhs, newParams) ) val forwarderInner: Tree = This(defdef.symbol.owner.asClass).select(nextSymbol) val forwarderCallArgs = newParamSymLists.zipWithIndex.map{case (ps, i) => - if (i == annotatedParamListIndex) ps.map(p => ref(p)).take(nextParamIndex) ++ defaultCalls - else ps.map(p => ref(p)) + if (i == annotatedParamListIndex) ps.map(ref).take(nextParamIndex) ++ defaultCalls + else ps.map(ref) } val forwarderCall0 = forwarderCallArgs.foldLeft[Tree](forwarderInner){ @@ -281,13 +300,6 @@ class UnrollDefs extends MiniPhase { val byName = otherDecls.groupMap(_.symbol.name.toString)(_.symbol) for case (src, dcl: NamedDefTree) <- generatedDefOrigins do val replaced = dcl.symbol - def symLocation(sym: Symbol) = { - val lineDesc = - if (sym.span.exists && sym.span != sym.owner.span) - s" at line ${sym.srcPos.line + 1}" - else "" - i"in ${sym.owner}${lineDesc}" - } byName.get(dcl.name.toString).foreach { syms => val clashes = syms.filter(checkClash(replaced, _)) for existing <- clashes do diff --git a/tests/neg/unroll-no-default.check b/tests/neg/unroll-no-default.check new file mode 100644 index 000000000000..a077e7558271 --- /dev/null +++ b/tests/neg/unroll-no-default.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/unroll-no-default.scala:6:26 ----------------------------------------------------------------------- +6 | def foo(x: Int, @unroll y: Int) = x + y // error + | ^^^^^^^^^^^^^^ + | Cannot unroll method foo in class Foo at line 6 because parameter y needs a default value diff --git a/tests/neg/unroll-no-default.scala b/tests/neg/unroll-no-default.scala new file mode 100644 index 000000000000..1fcdb513a4af --- /dev/null +++ b/tests/neg/unroll-no-default.scala @@ -0,0 +1,7 @@ +//> using options -experimental + +import scala.annotation.unroll + +class Foo { + def foo(x: Int, @unroll y: Int) = x + y // error +} From 5f3324bcafbc4d4bedaa8e3fde7f95e059a0c9ea Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 1 Oct 2024 21:37:47 +0200 Subject: [PATCH 08/35] SIP 61 - fix invalid pattern in generateFromProduct --- compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala index 79330f4bcc10..5110c7ade71b 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala @@ -14,7 +14,7 @@ import StdNames.nme import Names.* import Constants.Constant import dotty.tools.dotc.core.NameKinds.DefaultGetterName -import dotty.tools.dotc.core.Types.{MethodType, NamedType, PolyType, Type, NoPrefix} +import dotty.tools.dotc.core.Types.{MethodType, NamedType, PolyType, Type, NoPrefix, NoType} import dotty.tools.dotc.core.Symbols import dotty.tools.dotc.printing.Formatting.hl @@ -178,7 +178,7 @@ class UnrollDefs extends MiniPhase { ) } ++ Seq( CaseDef( - EmptyTree, + Underscore(defn.IntType), EmptyTree, defdef.rhs ) @@ -205,15 +205,14 @@ class UnrollDefs extends MiniPhase { else if (isCaseFromProduct) defdef.symbol.owner.companionClass.primaryConstructor else defdef.symbol - annotated .paramSymss .zipWithIndex - .flatMap{case (paramClause, paramClauseIndex) => + .flatMap { (paramClause, paramClauseIndex) => val annotationIndices = findUnrollAnnotations(paramClause) if (annotationIndices.isEmpty) None else Some((paramClauseIndex, annotationIndices)) - } match{ + } match { case Nil => (None, Nil) case Seq((paramClauseIndex, annotationIndices)) => val paramCount = annotated.paramSymss(paramClauseIndex).size From 17586b8fa7896e482bf430d27d170f6328dcc996 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 1 Oct 2024 22:39:50 +0200 Subject: [PATCH 09/35] SIP 61 - fork when running sbt-scripted apps with unmanaged classpaths --- .../abstractClassMethod/build.sbt | 11 +++++++++- .../abstractTraitMethod/build.sbt | 11 +++++++++- sbt-test/unroll-annot/caseclass/build.sbt | 10 +++++++++- sbt-test/unroll-annot/classMethod/build.sbt | 10 +++++++++- sbt-test/unroll-annot/curriedMethod/build.sbt | 10 +++++++++- sbt-test/unroll-annot/genericMethod/build.sbt | 20 +++++++++---------- .../unroll-annot/methodWithImplicit/build.sbt | 10 +++++++++- sbt-test/unroll-annot/objectMethod/build.sbt | 10 +++++++++- .../unroll-annot/primaryConstructor/build.sbt | 10 +++++++++- .../secondParameterList/build.sbt | 10 +++++++++- .../secondaryConstructor/build.sbt | 10 +++++++++- sbt-test/unroll-annot/traitMethod/build.sbt | 10 +++++++++- 12 files changed, 111 insertions(+), 21 deletions(-) diff --git a/sbt-test/unroll-annot/abstractClassMethod/build.sbt b/sbt-test/unroll-annot/abstractClassMethod/build.sbt index a2d4a103ef5f..387de257af72 100644 --- a/sbt-test/unroll-annot/abstractClassMethod/build.sbt +++ b/sbt-test/unroll-annot/abstractClassMethod/build.sbt @@ -13,6 +13,11 @@ lazy val v1_downstream = project.in(file("v1_downstream")).dependsOn(v1) lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, + Runtime / unmanagedClasspath := Seq( + Attributed.blank((v1 / Runtime / classDirectory).value), + Attributed.blank((v1_downstream / Runtime / classDirectory).value), + ), Compile / unmanagedClasspath := Seq( Attributed.blank((v1 / Compile / classDirectory).value), Attributed.blank((v1_downstream / Compile / classDirectory).value), @@ -30,7 +35,9 @@ lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) .settings( Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value) + Attributed.blank((v1_app / Runtime / classDirectory).value), + Attributed.blank((v2 / Runtime / classDirectory).value), + Attributed.blank((v2_downstream / Runtime / classDirectory).value), ), Compile / unmanagedClasspath := Seq( Attributed.blank((v2 / Compile / classDirectory).value), @@ -52,6 +59,8 @@ lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) Attributed.blank((v1_app / Runtime / classDirectory).value), // add v2_app, compiled against v2, to the classpath Attributed.blank((v2_app / Runtime / classDirectory).value), + Attributed.blank((v3 / Runtime / classDirectory).value), + Attributed.blank((v3_downstream / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v3 / Compile / classDirectory).value), diff --git a/sbt-test/unroll-annot/abstractTraitMethod/build.sbt b/sbt-test/unroll-annot/abstractTraitMethod/build.sbt index a2d4a103ef5f..387de257af72 100644 --- a/sbt-test/unroll-annot/abstractTraitMethod/build.sbt +++ b/sbt-test/unroll-annot/abstractTraitMethod/build.sbt @@ -13,6 +13,11 @@ lazy val v1_downstream = project.in(file("v1_downstream")).dependsOn(v1) lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, + Runtime / unmanagedClasspath := Seq( + Attributed.blank((v1 / Runtime / classDirectory).value), + Attributed.blank((v1_downstream / Runtime / classDirectory).value), + ), Compile / unmanagedClasspath := Seq( Attributed.blank((v1 / Compile / classDirectory).value), Attributed.blank((v1_downstream / Compile / classDirectory).value), @@ -30,7 +35,9 @@ lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) .settings( Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value) + Attributed.blank((v1_app / Runtime / classDirectory).value), + Attributed.blank((v2 / Runtime / classDirectory).value), + Attributed.blank((v2_downstream / Runtime / classDirectory).value), ), Compile / unmanagedClasspath := Seq( Attributed.blank((v2 / Compile / classDirectory).value), @@ -52,6 +59,8 @@ lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) Attributed.blank((v1_app / Runtime / classDirectory).value), // add v2_app, compiled against v2, to the classpath Attributed.blank((v2_app / Runtime / classDirectory).value), + Attributed.blank((v3 / Runtime / classDirectory).value), + Attributed.blank((v3_downstream / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v3 / Compile / classDirectory).value), diff --git a/sbt-test/unroll-annot/caseclass/build.sbt b/sbt-test/unroll-annot/caseclass/build.sbt index 0568d222bf5a..fc692a0473a6 100644 --- a/sbt-test/unroll-annot/caseclass/build.sbt +++ b/sbt-test/unroll-annot/caseclass/build.sbt @@ -10,6 +10,10 @@ lazy val v1 = project.in(file("v1")) lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, + Runtime / unmanagedClasspath := Seq( + Attributed.blank((v1 / Runtime / classDirectory).value) + ), Compile / unmanagedClasspath := Seq( Attributed.blank((v1 / Compile / classDirectory).value) ), @@ -21,9 +25,11 @@ lazy val v2 = project.in(file("v2")) lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value) + Attributed.blank((v1_app / Runtime / classDirectory).value), + Attributed.blank((v2 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v2 / Compile / classDirectory).value) @@ -36,11 +42,13 @@ lazy val v3 = project.in(file("v3")) lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath Attributed.blank((v1_app / Runtime / classDirectory).value), // add v2_app, compiled against v2, to the classpath Attributed.blank((v2_app / Runtime / classDirectory).value), + Attributed.blank((v3 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v3 / Compile / classDirectory).value) diff --git a/sbt-test/unroll-annot/classMethod/build.sbt b/sbt-test/unroll-annot/classMethod/build.sbt index 0568d222bf5a..fc692a0473a6 100644 --- a/sbt-test/unroll-annot/classMethod/build.sbt +++ b/sbt-test/unroll-annot/classMethod/build.sbt @@ -10,6 +10,10 @@ lazy val v1 = project.in(file("v1")) lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, + Runtime / unmanagedClasspath := Seq( + Attributed.blank((v1 / Runtime / classDirectory).value) + ), Compile / unmanagedClasspath := Seq( Attributed.blank((v1 / Compile / classDirectory).value) ), @@ -21,9 +25,11 @@ lazy val v2 = project.in(file("v2")) lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value) + Attributed.blank((v1_app / Runtime / classDirectory).value), + Attributed.blank((v2 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v2 / Compile / classDirectory).value) @@ -36,11 +42,13 @@ lazy val v3 = project.in(file("v3")) lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath Attributed.blank((v1_app / Runtime / classDirectory).value), // add v2_app, compiled against v2, to the classpath Attributed.blank((v2_app / Runtime / classDirectory).value), + Attributed.blank((v3 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v3 / Compile / classDirectory).value) diff --git a/sbt-test/unroll-annot/curriedMethod/build.sbt b/sbt-test/unroll-annot/curriedMethod/build.sbt index 0568d222bf5a..fc692a0473a6 100644 --- a/sbt-test/unroll-annot/curriedMethod/build.sbt +++ b/sbt-test/unroll-annot/curriedMethod/build.sbt @@ -10,6 +10,10 @@ lazy val v1 = project.in(file("v1")) lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, + Runtime / unmanagedClasspath := Seq( + Attributed.blank((v1 / Runtime / classDirectory).value) + ), Compile / unmanagedClasspath := Seq( Attributed.blank((v1 / Compile / classDirectory).value) ), @@ -21,9 +25,11 @@ lazy val v2 = project.in(file("v2")) lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value) + Attributed.blank((v1_app / Runtime / classDirectory).value), + Attributed.blank((v2 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v2 / Compile / classDirectory).value) @@ -36,11 +42,13 @@ lazy val v3 = project.in(file("v3")) lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath Attributed.blank((v1_app / Runtime / classDirectory).value), // add v2_app, compiled against v2, to the classpath Attributed.blank((v2_app / Runtime / classDirectory).value), + Attributed.blank((v3 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v3 / Compile / classDirectory).value) diff --git a/sbt-test/unroll-annot/genericMethod/build.sbt b/sbt-test/unroll-annot/genericMethod/build.sbt index 5a46bfa9f4ec..fc692a0473a6 100644 --- a/sbt-test/unroll-annot/genericMethod/build.sbt +++ b/sbt-test/unroll-annot/genericMethod/build.sbt @@ -1,10 +1,7 @@ lazy val utils = project.in(file("utils")) lazy val sharedSettings = Seq( - scalacOptions ++= Seq( - "-Ycheck:all", - "-experimental" - ) + scalacOptions ++= Seq("-Ycheck:all", "-experimental") ) lazy val v1 = project.in(file("v1")) @@ -13,6 +10,10 @@ lazy val v1 = project.in(file("v1")) lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, + Runtime / unmanagedClasspath := Seq( + Attributed.blank((v1 / Runtime / classDirectory).value) + ), Compile / unmanagedClasspath := Seq( Attributed.blank((v1 / Compile / classDirectory).value) ), @@ -20,18 +21,15 @@ lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) lazy val v2 = project.in(file("v2")) .settings(sharedSettings) - .settings( - scalacOptions += "Xprint:unroll", - // scalacOptions += "-Yplain-printer", - // scalacOptions += "-Xprint-types", - ) lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value) + Attributed.blank((v1_app / Runtime / classDirectory).value), + Attributed.blank((v2 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v2 / Compile / classDirectory).value) @@ -44,11 +42,13 @@ lazy val v3 = project.in(file("v3")) lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath Attributed.blank((v1_app / Runtime / classDirectory).value), // add v2_app, compiled against v2, to the classpath Attributed.blank((v2_app / Runtime / classDirectory).value), + Attributed.blank((v3 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v3 / Compile / classDirectory).value) diff --git a/sbt-test/unroll-annot/methodWithImplicit/build.sbt b/sbt-test/unroll-annot/methodWithImplicit/build.sbt index 0568d222bf5a..fc692a0473a6 100644 --- a/sbt-test/unroll-annot/methodWithImplicit/build.sbt +++ b/sbt-test/unroll-annot/methodWithImplicit/build.sbt @@ -10,6 +10,10 @@ lazy val v1 = project.in(file("v1")) lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, + Runtime / unmanagedClasspath := Seq( + Attributed.blank((v1 / Runtime / classDirectory).value) + ), Compile / unmanagedClasspath := Seq( Attributed.blank((v1 / Compile / classDirectory).value) ), @@ -21,9 +25,11 @@ lazy val v2 = project.in(file("v2")) lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value) + Attributed.blank((v1_app / Runtime / classDirectory).value), + Attributed.blank((v2 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v2 / Compile / classDirectory).value) @@ -36,11 +42,13 @@ lazy val v3 = project.in(file("v3")) lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath Attributed.blank((v1_app / Runtime / classDirectory).value), // add v2_app, compiled against v2, to the classpath Attributed.blank((v2_app / Runtime / classDirectory).value), + Attributed.blank((v3 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v3 / Compile / classDirectory).value) diff --git a/sbt-test/unroll-annot/objectMethod/build.sbt b/sbt-test/unroll-annot/objectMethod/build.sbt index 0568d222bf5a..fc692a0473a6 100644 --- a/sbt-test/unroll-annot/objectMethod/build.sbt +++ b/sbt-test/unroll-annot/objectMethod/build.sbt @@ -10,6 +10,10 @@ lazy val v1 = project.in(file("v1")) lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, + Runtime / unmanagedClasspath := Seq( + Attributed.blank((v1 / Runtime / classDirectory).value) + ), Compile / unmanagedClasspath := Seq( Attributed.blank((v1 / Compile / classDirectory).value) ), @@ -21,9 +25,11 @@ lazy val v2 = project.in(file("v2")) lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value) + Attributed.blank((v1_app / Runtime / classDirectory).value), + Attributed.blank((v2 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v2 / Compile / classDirectory).value) @@ -36,11 +42,13 @@ lazy val v3 = project.in(file("v3")) lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath Attributed.blank((v1_app / Runtime / classDirectory).value), // add v2_app, compiled against v2, to the classpath Attributed.blank((v2_app / Runtime / classDirectory).value), + Attributed.blank((v3 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v3 / Compile / classDirectory).value) diff --git a/sbt-test/unroll-annot/primaryConstructor/build.sbt b/sbt-test/unroll-annot/primaryConstructor/build.sbt index 0568d222bf5a..fc692a0473a6 100644 --- a/sbt-test/unroll-annot/primaryConstructor/build.sbt +++ b/sbt-test/unroll-annot/primaryConstructor/build.sbt @@ -10,6 +10,10 @@ lazy val v1 = project.in(file("v1")) lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, + Runtime / unmanagedClasspath := Seq( + Attributed.blank((v1 / Runtime / classDirectory).value) + ), Compile / unmanagedClasspath := Seq( Attributed.blank((v1 / Compile / classDirectory).value) ), @@ -21,9 +25,11 @@ lazy val v2 = project.in(file("v2")) lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value) + Attributed.blank((v1_app / Runtime / classDirectory).value), + Attributed.blank((v2 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v2 / Compile / classDirectory).value) @@ -36,11 +42,13 @@ lazy val v3 = project.in(file("v3")) lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath Attributed.blank((v1_app / Runtime / classDirectory).value), // add v2_app, compiled against v2, to the classpath Attributed.blank((v2_app / Runtime / classDirectory).value), + Attributed.blank((v3 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v3 / Compile / classDirectory).value) diff --git a/sbt-test/unroll-annot/secondParameterList/build.sbt b/sbt-test/unroll-annot/secondParameterList/build.sbt index 0568d222bf5a..fc692a0473a6 100644 --- a/sbt-test/unroll-annot/secondParameterList/build.sbt +++ b/sbt-test/unroll-annot/secondParameterList/build.sbt @@ -10,6 +10,10 @@ lazy val v1 = project.in(file("v1")) lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, + Runtime / unmanagedClasspath := Seq( + Attributed.blank((v1 / Runtime / classDirectory).value) + ), Compile / unmanagedClasspath := Seq( Attributed.blank((v1 / Compile / classDirectory).value) ), @@ -21,9 +25,11 @@ lazy val v2 = project.in(file("v2")) lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value) + Attributed.blank((v1_app / Runtime / classDirectory).value), + Attributed.blank((v2 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v2 / Compile / classDirectory).value) @@ -36,11 +42,13 @@ lazy val v3 = project.in(file("v3")) lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath Attributed.blank((v1_app / Runtime / classDirectory).value), // add v2_app, compiled against v2, to the classpath Attributed.blank((v2_app / Runtime / classDirectory).value), + Attributed.blank((v3 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v3 / Compile / classDirectory).value) diff --git a/sbt-test/unroll-annot/secondaryConstructor/build.sbt b/sbt-test/unroll-annot/secondaryConstructor/build.sbt index 0568d222bf5a..fc692a0473a6 100644 --- a/sbt-test/unroll-annot/secondaryConstructor/build.sbt +++ b/sbt-test/unroll-annot/secondaryConstructor/build.sbt @@ -10,6 +10,10 @@ lazy val v1 = project.in(file("v1")) lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, + Runtime / unmanagedClasspath := Seq( + Attributed.blank((v1 / Runtime / classDirectory).value) + ), Compile / unmanagedClasspath := Seq( Attributed.blank((v1 / Compile / classDirectory).value) ), @@ -21,9 +25,11 @@ lazy val v2 = project.in(file("v2")) lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value) + Attributed.blank((v1_app / Runtime / classDirectory).value), + Attributed.blank((v2 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v2 / Compile / classDirectory).value) @@ -36,11 +42,13 @@ lazy val v3 = project.in(file("v3")) lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath Attributed.blank((v1_app / Runtime / classDirectory).value), // add v2_app, compiled against v2, to the classpath Attributed.blank((v2_app / Runtime / classDirectory).value), + Attributed.blank((v3 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v3 / Compile / classDirectory).value) diff --git a/sbt-test/unroll-annot/traitMethod/build.sbt b/sbt-test/unroll-annot/traitMethod/build.sbt index 0568d222bf5a..fc692a0473a6 100644 --- a/sbt-test/unroll-annot/traitMethod/build.sbt +++ b/sbt-test/unroll-annot/traitMethod/build.sbt @@ -10,6 +10,10 @@ lazy val v1 = project.in(file("v1")) lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, + Runtime / unmanagedClasspath := Seq( + Attributed.blank((v1 / Runtime / classDirectory).value) + ), Compile / unmanagedClasspath := Seq( Attributed.blank((v1 / Compile / classDirectory).value) ), @@ -21,9 +25,11 @@ lazy val v2 = project.in(file("v2")) lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value) + Attributed.blank((v1_app / Runtime / classDirectory).value), + Attributed.blank((v2 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v2 / Compile / classDirectory).value) @@ -36,11 +42,13 @@ lazy val v3 = project.in(file("v3")) lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) .settings(sharedSettings) .settings( + fork := true, Runtime / unmanagedClasspath := Seq( // add v1_app, compiled against v1, to the classpath Attributed.blank((v1_app / Runtime / classDirectory).value), // add v2_app, compiled against v2, to the classpath Attributed.blank((v2_app / Runtime / classDirectory).value), + Attributed.blank((v3 / Runtime / classDirectory).value) ), Compile / unmanagedClasspath := Seq( Attributed.blank((v3 / Compile / classDirectory).value) From ed920800d77ee70e0f8ca5e2800d4eacba3bfdd4 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 1 Oct 2024 23:45:35 +0200 Subject: [PATCH 10/35] SIP 61 - move before pickling --- .../dotty/tools/dotc/CompilationUnit.scala | 3 + compiler/src/dotty/tools/dotc/Compiler.scala | 2 +- .../tools/dotc/transform/PostTyper.scala | 11 ++ ...rollDefs.scala => UnrollDefinitions.scala} | 110 ++++++++++++------ .../abstractTraitMethod/build.sbt | 6 + .../src/main/scala/Downstream.scala | 6 +- tests/neg/unroll-no-default.scala | 2 +- 7 files changed, 99 insertions(+), 41 deletions(-) rename compiler/src/dotty/tools/dotc/transform/{UnrollDefs.scala => UnrollDefinitions.scala} (81%) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index 0975c94e916a..b7171bb28853 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -59,6 +59,9 @@ class CompilationUnit protected (val source: SourceFile, val info: CompilationUn var hasMacroAnnotations: Boolean = false + def hasUnrollDefs: Boolean = unrolledClasses != null + var unrolledClasses: Set[Symbol] | Null = null + /** Set to `true` if inliner added anonymous mirrors that need to be completed */ var needsMirrorSupport: Boolean = false diff --git a/compiler/src/dotty/tools/dotc/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index 24a492b4fe77..6e1e65d03976 100644 --- a/compiler/src/dotty/tools/dotc/Compiler.scala +++ b/compiler/src/dotty/tools/dotc/Compiler.scala @@ -40,6 +40,7 @@ class Compiler { List(new sbt.ExtractDependencies) :: // Sends information on classes' dependencies to sbt via callbacks List(new semanticdb.ExtractSemanticDB.ExtractSemanticInfo) :: // Extract info into .semanticdb files List(new PostTyper) :: // Additional checks and cleanups after type checking + List(new UnrollDefinitions) :: // Unroll annotated methods if detected in PostTyper List(new sjs.PrepJSInterop) :: // Additional checks and transformations for Scala.js (Scala.js only) List(new SetRootTree) :: // Set the `rootTreeOrProvider` on class symbols Nil @@ -61,7 +62,6 @@ class Compiler { List(new InstrumentCoverage) :: // Perform instrumentation for code coverage (if -coverage-out is set) List(new CrossVersionChecks, // Check issues related to deprecated and experimental new FirstTransform, // Some transformations to put trees into a canonical form - new UnrollDefs, // Unroll annotated methods new CheckReentrant, // Internal use only: Check that compiled program has no data races involving global vars new ElimPackagePrefixes, // Eliminate references to package prefixes in Select nodes new CookComments, // Cook the comments: expand variables, doc, etc. diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 898517806e50..59b3bba2f89c 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -199,6 +199,16 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => tree } + private def registerIfUnrolledParam(sym: Symbol)(using Context): Unit = + if sym.getAnnotation(defn.UnrollAnnot).isDefined then + val cls = sym.enclosingClass + val classes = ctx.compilationUnit.unrolledClasses + val additions = Array(cls, cls.linkedClass).filter(_ != NoSymbol) + if classes == null then + ctx.compilationUnit.unrolledClasses = Set.from(additions) + else + ctx.compilationUnit.unrolledClasses = classes ++ additions + private def processValOrDefDef(tree: Tree)(using Context): tree.type = val sym = tree.symbol tree match @@ -215,6 +225,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => ++ sym.annotations) else if sym.is(Param) then + registerIfUnrolledParam(sym) sym.keepAnnotationsCarrying(thisPhase, Set(defn.ParamMetaAnnot), orNoneOf = defn.NonBeanMetaAnnots) else if sym.is(ParamAccessor) then // @publicInBinary is not a meta-annotation and therefore not kept by `keepAnnotationsCarrying` diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala similarity index 81% rename from compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala rename to compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala index 5110c7ade71b..b287a05356e9 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnrollDefs.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala @@ -1,35 +1,58 @@ -package dotty.tools.dotc.transform +package dotty.tools.dotc +package transform -import dotty.tools.dotc.* +import ast.tpd +import ast.Trees.* import core.* -import MegaPhase.MiniPhase +import Flags.* +import Decorators.* import Contexts.* import Symbols.* -import Flags.* -import SymDenotations.* +import Constants.Constant import Decorators.* -import ast.Trees.* -import ast.tpd -import StdNames.nme +import DenotTransformers.IdentityDenotTransformer import Names.* -import Constants.Constant import dotty.tools.dotc.core.NameKinds.DefaultGetterName + import dotty.tools.dotc.core.Types.{MethodType, NamedType, PolyType, Type, NoPrefix, NoType} -import dotty.tools.dotc.core.Symbols + import dotty.tools.dotc.printing.Formatting.hl -import dotty.tools.dotc.core.Types.NoType -import dotty.tools.dotc.typer.Checking +import scala.collection.mutable +import scala.util.boundary, boundary.break +import dotty.tools.dotc.core.StdNames.nme + +/**Implementation of SIP-61. + * Runs when `@unroll` annotations are found in a compilation unit, installing new definitions + */ +class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { + self => + + import tpd.* -class UnrollDefs extends MiniPhase { - import tpd._ + override def phaseName: String = UnrollDefinitions.name - val phaseName = "unroll" + override def description: String = UnrollDefinitions.description - override val runsAfter = Set(FirstTransform.name) + override def changesMembers: Boolean = true + + override def run(using Context): Unit = + if ctx.compilationUnit.hasUnrollDefs then + super.run + + def newTransformer(using Context): Transformer = + UnrollingTransformer(ctx.compilationUnit.unrolledClasses.nn) + + private class UnrollingTransformer(classes: Set[Symbol]) extends Transformer { + override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match + case tree @ TypeDef(_, impl: Template) if classes(tree.symbol) => + super.transform(cpy.TypeDef(tree)(rhs = unrollTemplate(impl))) + case tree => + super.transform(tree) + } def copyParamSym(sym: Symbol, parent: Symbol)(using Context): (Symbol, Symbol) = - sym -> sym.copy(owner = parent) + sym -> sym.copy(owner = parent, flags = (sym.flags &~ HasDefault)) def symLocation(sym: Symbol)(using Context) = { val lineDesc = @@ -47,6 +70,7 @@ class UnrollDefs extends MiniPhase { i } } + def isTypeClause(p: ParamClause) = p.headOption.exists(_.isInstanceOf[TypeDef]) def generateSingleForwarder(defdef: DefDef, @@ -63,15 +87,25 @@ class UnrollDefs extends MiniPhase { else ps.map(p => copyParamSym(p.symbol, parent)) } + val isOverride = { + val candidate = defdef.symbol.nextOverriddenSymbol + candidate.exists && !candidate.is(Deferred) + } + val forwarderDefSymbol = Symbols.newSymbol( defdef.symbol.owner, defdef.name, defdef.symbol.flags &~ HasDefaultParams &~ (if (nextParamIndex == -1) Flags.EmptyFlags else Deferred) | - Invisible, + Invisible | (if (isOverride) Override else EmptyFlags), NoType, // fill in later - ) + ).entered + + if nextParamIndex == -1 then + defdef.symbol.owner.asClass.info.decls.openForMutations.unlink(defdef.symbol) + else if isOverride then + defdef.symbol.flags_=(defdef.symbol.flags | Override) val newParamSymMappings = extractParamSymss(forwarderDefSymbol) val (oldParams, newParams) = newParamSymMappings.flatten.unzip @@ -96,16 +130,18 @@ class UnrollDefs extends MiniPhase { val defaultCalls = Range(paramIndex, nextParamIndex).map(n => - def makeSelect(ref: Tree, name: TermName): Tree = - val sym = ref.symbol + def makeSelect(refTree: Tree, name: TermName): Tree = + val sym = refTree.symbol if !sym.findMember(name, NoPrefix, EmptyFlags, EmptyFlags).exists then val param = defdef.paramss(annotatedParamListIndex)(n) val methodStr = s"method ${defdef.name} ${symLocation(defdef.symbol)}" val paramStr = s"parameter ${param.name}" - report.error(i"Cannot unroll $methodStr because $paramStr needs a default value", param.srcPos) - EmptyTree + val errorMessage = + i"Cannot unroll $methodStr because $paramStr needs a default value" + report.error(errorMessage, param.srcPos) + ref(newErrorSymbol(sym, nme.ERROR, errorMessage.toMessage)) else - ref.select(name) + refTree.select(name) val inner = if (defdef.symbol.isConstructor) { makeSelect(ref(defdef.symbol.owner.companionModule), @@ -118,8 +154,7 @@ class UnrollDefs extends MiniPhase { DefaultGetterName(defdef.name, n + defaultOffset)) } - if inner.isEmpty then EmptyTree - else newParamSymLists + newParamSymLists .take(annotatedParamListIndex) .map(_.map(ref)) .foldLeft(inner): (lhs, newParams) => @@ -187,7 +222,7 @@ class UnrollDefs extends MiniPhase { ).setDefTree } - def generateSyntheticDefs(tree: Tree)(using Context): (Option[(Symbol, Seq[Symbol])], Seq[(Symbol, Tree)]) = tree match{ + def generateSyntheticDefs(tree: Tree)(using Context): (Option[(Symbol, Seq[Symbol])], Seq[(Symbol, Tree)]) = tree match { case defdef: DefDef if defdef.paramss.nonEmpty => import dotty.tools.dotc.core.NameOps.isConstructorName @@ -269,7 +304,7 @@ class UnrollDefs extends MiniPhase { case _ => (None, Nil) } - override def transformTemplate(tmpl: tpd.Template)(using Context): tpd.Tree = { + def unrollTemplate(tmpl: tpd.Template)(using Context): tpd.Tree = { val (removed0, generatedDefs0) = tmpl.body.map(generateSyntheticDefs).unzip val (removedCtor, generatedConstr0) = generateSyntheticDefs(tmpl.constr) @@ -308,14 +343,17 @@ class UnrollDefs extends MiniPhase { } end if - super.transformTemplate( - cpy.Template(tmpl)( - tmpl.constr, - tmpl.parents, - tmpl.derived, - tmpl.self, - otherDecls ++ generatedDefs ++ generatedConstr - ) + cpy.Template(tmpl)( + tmpl.constr, + tmpl.parents, + tmpl.derived, + tmpl.self, + otherDecls ++ generatedDefs ++ generatedConstr ) } + } + +object UnrollDefinitions: + val name: String = "unrollDefs" + val description: String = "generates forwarders for methods annotated with @unroll" diff --git a/sbt-test/unroll-annot/abstractTraitMethod/build.sbt b/sbt-test/unroll-annot/abstractTraitMethod/build.sbt index 387de257af72..981e2667511d 100644 --- a/sbt-test/unroll-annot/abstractTraitMethod/build.sbt +++ b/sbt-test/unroll-annot/abstractTraitMethod/build.sbt @@ -4,6 +4,10 @@ lazy val sharedSettings = Seq( scalacOptions ++= Seq("-Ycheck:all", "-experimental") ) +lazy val printSettings = Seq( + scalacOptions ++= Seq("-Xprint:unrollDefs", "-Ydebug-flags") +) + lazy val v1 = project.in(file("v1")) .settings(sharedSettings) @@ -26,9 +30,11 @@ lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) lazy val v2 = project.in(file("v2")) .settings(sharedSettings) + .settings(printSettings) lazy val v2_downstream = project.in(file("v2_downstream")).dependsOn(v2) .settings(sharedSettings) + .settings(printSettings) lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) .settings(sharedSettings) diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v2_downstream/src/main/scala/Downstream.scala b/sbt-test/unroll-annot/abstractTraitMethod/v2_downstream/src/main/scala/Downstream.scala index e0545e97a42f..1e4f4e3992e4 100644 --- a/sbt-test/unroll-annot/abstractTraitMethod/v2_downstream/src/main/scala/Downstream.scala +++ b/sbt-test/unroll-annot/abstractTraitMethod/v2_downstream/src/main/scala/Downstream.scala @@ -3,13 +3,13 @@ package unroll import scala.annotation.unroll object UnrolledObj extends Unrolled { - def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b.toString.take(4) + override def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b.toString.take(4) } class UnrolledCls extends Unrolled { - def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b.toString.take(4) + override def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b.toString.take(4) } object UnrollMisc{ def expectedLength = 6 -} \ No newline at end of file +} diff --git a/tests/neg/unroll-no-default.scala b/tests/neg/unroll-no-default.scala index 1fcdb513a4af..dfb8ea70d931 100644 --- a/tests/neg/unroll-no-default.scala +++ b/tests/neg/unroll-no-default.scala @@ -1,4 +1,4 @@ -//> using options -experimental +//> using options -experimental -Xprint:unrollDefs import scala.annotation.unroll From b8871b7d9686c690185dde57fe4504d42803218b Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 2 Oct 2024 12:16:37 +0200 Subject: [PATCH 11/35] SIP-61 - fixed unpickling errors - invisible select and incorrect spans --- .../dotty/tools/dotc/core/Definitions.scala | 2 + .../tools/dotc/core/tasty/TreePickler.scala | 15 +- .../tools/dotc/core/tasty/TreeUnpickler.scala | 7 +- .../dotty/tools/dotc/transform/Pickler.scala | 3 + .../dotc/transform/UnrollDefinitions.scala | 186 +++++++++++------- .../annotation/internal/AbstractUnroll.scala | 10 + .../annotation/internal/UnrollForwarder.scala | 9 + .../abstractTraitMethod/build.sbt | 6 - .../src/main/scala/Downstream.scala | 4 +- tests/run/unroll-abstractMethod.scala | 28 +++ tests/run/unroll-abstractMethod2/Test_2.scala | 6 + .../unroll-abstractMethod2/Unrolled_1.scala | 12 ++ 12 files changed, 203 insertions(+), 85 deletions(-) create mode 100644 library/src/scala/annotation/internal/AbstractUnroll.scala create mode 100644 library/src/scala/annotation/internal/UnrollForwarder.scala create mode 100644 tests/run/unroll-abstractMethod.scala create mode 100644 tests/run/unroll-abstractMethod2/Test_2.scala create mode 100644 tests/run/unroll-abstractMethod2/Unrolled_1.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 4a136b4cea95..d0537f30c9d5 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1037,6 +1037,8 @@ class Definitions { @tu lazy val NowarnAnnot: ClassSymbol = requiredClass("scala.annotation.nowarn") @tu lazy val UnusedAnnot: ClassSymbol = requiredClass("scala.annotation.unused") @tu lazy val UnrollAnnot: ClassSymbol = requiredClass("scala.annotation.unroll") + @tu lazy val AbstractUnrollAnnot: ClassSymbol = requiredClass("scala.annotation.internal.AbstractUnroll") + @tu lazy val UnrollForwarderAnnot: ClassSymbol = requiredClass("scala.annotation.internal.UnrollForwarder") @tu lazy val TransparentTraitAnnot: ClassSymbol = requiredClass("scala.annotation.transparentTrait") @tu lazy val NativeAnnot: ClassSymbol = requiredClass("scala.native") @tu lazy val RepeatedAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Repeated") diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 7b80c7c80a21..d222f828ba9b 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -20,6 +20,7 @@ import collection.mutable import reporting.{Profile, NoProfile} import dotty.tools.tasty.TastyFormat.ASTsSection import quoted.QuotePatterns +import dotty.tools.dotc.config.Feature object TreePickler: class StackSizeExceeded(val mdef: tpd.MemberDef) extends Exception @@ -474,26 +475,30 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) { case _ => if passesConditionForErroringBestEffortCode(tree.hasType) then // #19951 The signature of a constructor of a Java annotation is irrelevant + val sym = tree.symbol val sig = - if name == nme.CONSTRUCTOR && tree.symbol.exists && tree.symbol.owner.is(JavaAnnotation) then Signature.NotAMethod + if name == nme.CONSTRUCTOR && sym.exists && sym.owner.is(JavaAnnotation) then Signature.NotAMethod else tree.tpe.signature - var ename = tree.symbol.targetName + var ename = sym.targetName val selectFromQualifier = name.isTypeName || qual.isInstanceOf[Hole] // holes have no symbol || sig == Signature.NotAMethod // no overload resolution necessary - || !tree.denot.symbol.exists // polymorphic function type + || !sym.exists // polymorphic function type || tree.denot.asSingleDenotation.isRefinedMethod // refined methods have no defining class symbol if selectFromQualifier then writeByte(if name.isTypeName then SELECTtpt else SELECT) pickleNameAndSig(name, sig, ename) pickleTree(qual) + else if sym.is(Invisible) && qual.isInstanceOf[This] && sym.hasAnnotation(defn.UnrollForwarderAnnot) then + writeByte(TERMREFdirect) + pickleSymRef(sym) // SIP-61 HACK: resolution from Signature filters out Invisible symbols else // select from owner writeByte(SELECTin) withLength { - pickleNameAndSig(name, tree.symbol.signature, ename) + pickleNameAndSig(name, sym.signature, ename) pickleTree(qual) - pickleType(tree.symbol.owner.typeRef) + pickleType(sym.owner.typeRef) } else writeByte(if name.isTypeName then SELECTtpt else SELECT) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index d9ae4ddb6006..1cd82a27d7b9 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1260,7 +1260,12 @@ class TreeUnpickler(reader: TastyReader, goto(start) readType() match { case path: TypeRef => TypeTree(path) - case path: TermRef => ref(path) + case path: TermRef => + val sym = path.symbol + if sym.is(Invisible) && sym.hasAnnotation(defn.UnrollForwarderAnnot) then + This(sym.owner.asClass).select(sym) + else + ref(path) case path: ThisType => untpd.This(untpd.EmptyTypeIdent).withType(path) case path: ConstantType => Literal(path.value) case path: ErrorType if isBestEffortTasty => TypeTree(path) diff --git a/compiler/src/dotty/tools/dotc/transform/Pickler.scala b/compiler/src/dotty/tools/dotc/transform/Pickler.scala index c8c071064ab8..d78d68ea95f9 100644 --- a/compiler/src/dotty/tools/dotc/transform/Pickler.scala +++ b/compiler/src/dotty/tools/dotc/transform/Pickler.scala @@ -457,6 +457,9 @@ class Pickler extends Phase { case None => () + if ctx.settings.YtestPicklerCheck.value then + sys.error(printedTasty(cls)) + inContext(printerContext(testJava)(using rootCtx.fresh.setCompilationUnit(freshUnit))): testSame(i"$unpickled%\n%", beforePickling(cls), cls) diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala index b287a05356e9..df364c76737c 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala @@ -30,6 +30,17 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { import tpd.* + private var _unrolledDefs: util.HashMap[Symbol, ComputedIndicies] | Null = null + private def initializeUnrolledDefs(): util.HashMap[Symbol, ComputedIndicies] = + val local = _unrolledDefs + if local == null then + val map = new util.HashMap[Symbol, ComputedIndicies] + _unrolledDefs = map + map + else + local.clear() + local + override def phaseName: String = UnrollDefinitions.name override def description: String = UnrollDefinitions.description @@ -38,21 +49,40 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { override def run(using Context): Unit = if ctx.compilationUnit.hasUnrollDefs then - super.run + super.run // create and run the transformer on the current compilation unit def newTransformer(using Context): Transformer = UnrollingTransformer(ctx.compilationUnit.unrolledClasses.nn) + type ComputedIndicies = Seq[(Int, List[Int])] + type ComputeIndicies = Context ?=> Symbol => ComputedIndicies + private class UnrollingTransformer(classes: Set[Symbol]) extends Transformer { + private val unrolledDefs = initializeUnrolledDefs() + + def computeIndices(annotated: Symbol)(using Context): ComputedIndicies = + unrolledDefs.getOrElseUpdate(annotated, { + annotated + .paramSymss + .zipWithIndex + .flatMap { (paramClause, paramClauseIndex) => + val annotationIndices = findUnrollAnnotations(paramClause) + if (annotationIndices.isEmpty) None + else Some((paramClauseIndex, annotationIndices)) + } + }) + end computeIndices + override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match case tree @ TypeDef(_, impl: Template) if classes(tree.symbol) => - super.transform(cpy.TypeDef(tree)(rhs = unrollTemplate(impl))) + super.transform(cpy.TypeDef(tree)(rhs = unrollTemplate(impl, computeIndices))) case tree => super.transform(tree) } def copyParamSym(sym: Symbol, parent: Symbol)(using Context): (Symbol, Symbol) = - sym -> sym.copy(owner = parent, flags = (sym.flags &~ HasDefault)) + val copied = sym.copy(owner = parent, flags = (sym.flags &~ HasDefault), coord = sym.coord) + sym -> copied def symLocation(sym: Symbol)(using Context) = { val lineDesc = @@ -79,46 +109,62 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { nextParamIndex: Int, nextSymbol: Symbol, annotatedParamListIndex: Int, - isCaseApply: Boolean)(using Context) = { + isCaseApply: Boolean, + inferOverride: Boolean)(using Context) = { + + def initNewForwarder()(using Context): (TermSymbol, List[List[Symbol]]) = { + val forwarderDefSymbol0 = Symbols.newSymbol( + defdef.symbol.owner, + defdef.name, + (defdef.symbol.flags &~ + HasDefaultParams &~ + (if nextParamIndex == -1 then EmptyFlags else Deferred)) | + Invisible | Synthetic | + (if inferOverride then Override else EmptyFlags), + NoType, // fill in later + coord = nextSymbol.span.shift(1) // shift by 1 to avoid "secondary constructor must call preceding" error + ).entered + + // we need this such that when unpickling a TERMREFdirect, if we see this annotation, + // we restore the tree to a Select + forwarderDefSymbol0.addAnnotation(defn.UnrollForwarderAnnot) + + val newParamSymMappings = extractParamSymss(copyParamSym(_, forwarderDefSymbol0)) + val (oldParams, newParams) = newParamSymMappings.flatten.unzip + + val newParamSymLists0 = + newParamSymMappings.map: pairss => + pairss.map: (oldSym, newSym) => + newSym.info = oldSym.info.substSym(oldParams, newParams) + newSym + + val newResType = defdef.tpt.tpe.substSym(oldParams, newParams) + forwarderDefSymbol0.info = NamerOps.methodType(newParamSymLists0, newResType) + forwarderDefSymbol0.setParamss(newParamSymLists0) + forwarderDefSymbol0 -> newParamSymLists0 + } - def extractParamSymss(parent: Symbol)(using Context): List[List[(Symbol, Symbol)]] = + def extractParamSymss[T](onSymbol: Symbol => T): List[List[T]] = defdef.paramss.zipWithIndex.map{ case (ps, i) => - if (i == annotatedParamListIndex) ps.take(paramIndex).map(p => copyParamSym(p.symbol, parent)) - else ps.map(p => copyParamSym(p.symbol, parent)) + if (i == annotatedParamListIndex) ps.take(paramIndex).map(p => onSymbol(p.symbol)) + else ps.map(p => onSymbol(p.symbol)) } - val isOverride = { - val candidate = defdef.symbol.nextOverriddenSymbol - candidate.exists && !candidate.is(Deferred) - } - - val forwarderDefSymbol = Symbols.newSymbol( - defdef.symbol.owner, - defdef.name, - defdef.symbol.flags &~ - HasDefaultParams &~ - (if (nextParamIndex == -1) Flags.EmptyFlags else Deferred) | - Invisible | (if (isOverride) Override else EmptyFlags), - NoType, // fill in later - ).entered - - if nextParamIndex == -1 then - defdef.symbol.owner.asClass.info.decls.openForMutations.unlink(defdef.symbol) - else if isOverride then - defdef.symbol.flags_=(defdef.symbol.flags | Override) - - val newParamSymMappings = extractParamSymss(forwarderDefSymbol) - val (oldParams, newParams) = newParamSymMappings.flatten.unzip + val paramCount = defdef.symbol.paramSymss(annotatedParamListIndex).size + val isDeferredInitial = paramCount == paramIndex && defdef.symbol.is(Deferred) - val newParamSymLists = - newParamSymMappings.map: pairss => - pairss.map: (oldSym, newSym) => - newSym.info = oldSym.info.substSym(oldParams, newParams) - newSym + val (forwarderDefSymbol, newParamSymLists) = + if isDeferredInitial then + val existing = defdef.symbol.asTerm + existing.addAnnotation(defn.AbstractUnrollAnnot) // mark as previously abstract + existing.flags = (existing.flags &~ Deferred) // going to implement its rhs + existing -> extractParamSymss(identity) + else + initNewForwarder() - val newResType = defdef.tpt.tpe.substSym(oldParams, newParams) - forwarderDefSymbol.info = NamerOps.methodType(newParamSymLists, newResType) - forwarderDefSymbol.setParamss(newParamSymLists) + if inferOverride then + // in this case we will not replace the source method, but we will add the override flag + defdef.symbol.flags_=(defdef.symbol.flags | Override) def forwarderRhs(): tpd.Tree = { val defaultOffset = defdef.paramss @@ -185,9 +231,9 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { val forwarderDef = tpd.DefDef(forwarderDefSymbol, - rhs = if (nextParamIndex == -1) EmptyTree else forwarderRhs()) + rhs = if nextParamIndex == -1 then EmptyTree else forwarderRhs()) - forwarderDef + forwarderDef.withSpan(if isDeferredInitial then defdef.span else nextSymbol.span.shift(1)) } def generateFromProduct(startParamIndices: List[Int], paramCount: Int, defdef: DefDef)(using Context) = { @@ -222,10 +268,25 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { ).setDefTree } - def generateSyntheticDefs(tree: Tree)(using Context): (Option[(Symbol, Seq[Symbol])], Seq[(Symbol, Tree)]) = tree match { + def generateSyntheticDefs(tree: Tree, compute: ComputeIndicies)(using Context): (Option[Symbol], Seq[(Symbol, Tree)]) = tree match { case defdef: DefDef if defdef.paramss.nonEmpty => import dotty.tools.dotc.core.NameOps.isConstructorName + // infer an override when we are implementing a method that matches the signature and has unroll annotations + // in the same positions + lazy val inferOverride = { + def unrollIndices(sym: Symbol): List[Int] = + sym.paramSymss.flatten.zipWithIndex.collect({ + case (p, i) if p.hasAnnotation(defn.UnrollAnnot) => i + }) + + val candidate = defdef.symbol.nextOverriddenSymbol + candidate.exists && !candidate.is(Deferred) && candidate.hasAnnotation(defn.AbstractUnrollAnnot) && { + // check unroll indices match + unrollIndices(candidate) == unrollIndices(defdef.symbol) + } + } + val isCaseCopy = defdef.name.toString == "copy" && defdef.symbol.owner.is(CaseClass) @@ -240,24 +301,17 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { else if (isCaseFromProduct) defdef.symbol.owner.companionClass.primaryConstructor else defdef.symbol - annotated - .paramSymss - .zipWithIndex - .flatMap { (paramClause, paramClauseIndex) => - val annotationIndices = findUnrollAnnotations(paramClause) - if (annotationIndices.isEmpty) None - else Some((paramClauseIndex, annotationIndices)) - } match { + compute(annotated) match { case Nil => (None, Nil) case Seq((paramClauseIndex, annotationIndices)) => val paramCount = annotated.paramSymss(paramClauseIndex).size if (isCaseFromProduct) { val newDef = generateFromProduct(annotationIndices, paramCount, defdef) - (Some(defdef.symbol, Seq(newDef.symbol)), Seq(defdef.symbol -> newDef)) + (Some(defdef.symbol), Seq(defdef.symbol -> newDef)) } else { if (defdef.symbol.is(Deferred)){ - val replacements = Seq.newBuilder[Symbol] - val newDefs = + ( + Some(defdef.symbol), (-1 +: annotationIndices :+ paramCount).sliding(2).toList.foldLeft((Seq.empty[(Symbol, DefDef)], defdef.symbol))((m, v) => ((m, v): @unchecked) match { case ((defdefs, nextSymbol), Seq(paramIndex, nextParamIndex)) => val forwarder = generateSingleForwarder( @@ -267,18 +321,15 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { paramIndex, nextSymbol, paramClauseIndex, - isCaseApply + isCaseApply, + inferOverride ) - replacements += forwarder.symbol + // replacements += forwarder.symbol ((defdef.symbol -> forwarder) +: defdefs, forwarder.symbol) })._1 - ( - Some(defdef.symbol, replacements.result()), - newDefs ) }else{ - ( None, (annotationIndices :+ paramCount).sliding(2).toList.reverse.foldLeft((Seq.empty[(Symbol, DefDef)], defdef.symbol))((m, v) => ((m, v): @unchecked) match { @@ -290,7 +341,8 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { nextParamIndex, nextSymbol, paramClauseIndex, - isCaseApply + isCaseApply, + inferOverride ) ((defdef.symbol -> forwarder) +: defdefs, forwarder.symbol) })._1 @@ -304,13 +356,12 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { case _ => (None, Nil) } - def unrollTemplate(tmpl: tpd.Template)(using Context): tpd.Tree = { + def unrollTemplate(tmpl: tpd.Template, compute: ComputeIndicies)(using Context): tpd.Tree = { - val (removed0, generatedDefs0) = tmpl.body.map(generateSyntheticDefs).unzip - val (removedCtor, generatedConstr0) = generateSyntheticDefs(tmpl.constr) - val removedFlat = removed0.flatten - val removedSymsBody = removedFlat.map(_(0)) - val allRemoved = removedFlat ++ removedCtor + val (removed0, generatedDefs0) = tmpl.body.map(generateSyntheticDefs(_, compute)).unzip + val (removedCtor, generatedConstr0) = generateSyntheticDefs(tmpl.constr, compute) + val removedSymsBody = removed0.flatten + val allRemoved = removedSymsBody ++ removedCtor val generatedDefOrigins = generatedDefs0.flatten val generatedDefs = generatedDefOrigins.map(_(1)) @@ -318,13 +369,6 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { val otherDecls = tmpl.body.filter(t => !removedSymsBody.contains(t.symbol)) - for (sym, replacements) <- allRemoved do - val cls = sym.owner.asClass - def totalParamCount(sym: Symbol): Int = sym.paramSymss.view.map(_.size).sum - val symParamCount = totalParamCount(sym) - val replaced = replacements.find(totalParamCount(_) == symParamCount).get - cls.replace(sym, replaced) - /** inlined from compiler/src/dotty/tools/dotc/typer/Checking.scala */ def checkClash(decl: Symbol, other: Symbol) = def staticNonStaticPair = decl.isScalaStatic != other.isScalaStatic diff --git a/library/src/scala/annotation/internal/AbstractUnroll.scala b/library/src/scala/annotation/internal/AbstractUnroll.scala new file mode 100644 index 000000000000..1f19081b0d84 --- /dev/null +++ b/library/src/scala/annotation/internal/AbstractUnroll.scala @@ -0,0 +1,10 @@ +package scala.annotation.internal + +import scala.annotation.Annotation +import scala.annotation.experimental + +/** Indicates the method was abstract in source code before unrolling transformation was added. + * downstream direct overrides, with matching `@unroll` annotations will infer an override. + */ +@experimental("under review as part of SIP-61") +final class AbstractUnroll extends Annotation diff --git a/library/src/scala/annotation/internal/UnrollForwarder.scala b/library/src/scala/annotation/internal/UnrollForwarder.scala new file mode 100644 index 000000000000..cfa9c3534737 --- /dev/null +++ b/library/src/scala/annotation/internal/UnrollForwarder.scala @@ -0,0 +1,9 @@ +package scala.annotation.internal + +import scala.annotation.Annotation +import scala.annotation.experimental + +/** This method was generated via `@unroll` annotation. + */ +@experimental("under review as part of SIP-61") +final class UnrollForwarder extends Annotation diff --git a/sbt-test/unroll-annot/abstractTraitMethod/build.sbt b/sbt-test/unroll-annot/abstractTraitMethod/build.sbt index 981e2667511d..387de257af72 100644 --- a/sbt-test/unroll-annot/abstractTraitMethod/build.sbt +++ b/sbt-test/unroll-annot/abstractTraitMethod/build.sbt @@ -4,10 +4,6 @@ lazy val sharedSettings = Seq( scalacOptions ++= Seq("-Ycheck:all", "-experimental") ) -lazy val printSettings = Seq( - scalacOptions ++= Seq("-Xprint:unrollDefs", "-Ydebug-flags") -) - lazy val v1 = project.in(file("v1")) .settings(sharedSettings) @@ -30,11 +26,9 @@ lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) lazy val v2 = project.in(file("v2")) .settings(sharedSettings) - .settings(printSettings) lazy val v2_downstream = project.in(file("v2_downstream")).dependsOn(v2) .settings(sharedSettings) - .settings(printSettings) lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) .settings(sharedSettings) diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v2_downstream/src/main/scala/Downstream.scala b/sbt-test/unroll-annot/abstractTraitMethod/v2_downstream/src/main/scala/Downstream.scala index 1e4f4e3992e4..a194928948d6 100644 --- a/sbt-test/unroll-annot/abstractTraitMethod/v2_downstream/src/main/scala/Downstream.scala +++ b/sbt-test/unroll-annot/abstractTraitMethod/v2_downstream/src/main/scala/Downstream.scala @@ -3,11 +3,11 @@ package unroll import scala.annotation.unroll object UnrolledObj extends Unrolled { - override def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b.toString.take(4) + def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b.toString.take(4) } class UnrolledCls extends Unrolled { - override def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b.toString.take(4) + def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b.toString.take(4) } object UnrollMisc{ diff --git a/tests/run/unroll-abstractMethod.scala b/tests/run/unroll-abstractMethod.scala new file mode 100644 index 000000000000..f767530b895d --- /dev/null +++ b/tests/run/unroll-abstractMethod.scala @@ -0,0 +1,28 @@ +//> using options -experimental + +import scala.annotation.unroll + +trait Unrolled { + def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String +} + +abstract class UnrolledBase { + def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String +} + +class UnrolledCls extends Unrolled { + /** infers `override` even though Unrolled.foo is actually implemented. */ + def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b +} + +object UnrolledObject extends UnrolledBase { + /** infers `override` even though Unrolled.foo is actually implemented. */ + def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b +} + +@main def Test: Unit = { + // val cls = new UnrolledCls + // assert(cls.foo("foo") == "foo1true") + // assert(UnrolledObject.foo("foo") == "foo1true") + () +} diff --git a/tests/run/unroll-abstractMethod2/Test_2.scala b/tests/run/unroll-abstractMethod2/Test_2.scala new file mode 100644 index 000000000000..48d2acef560e --- /dev/null +++ b/tests/run/unroll-abstractMethod2/Test_2.scala @@ -0,0 +1,6 @@ +//> using options -experimental + +@main def Test = + + // ensure that impl.foo isn't `Invisible`, so can be resolved from TASTy + assert(UnrolledImpl.impl.foo("foo") == "foo1true") diff --git a/tests/run/unroll-abstractMethod2/Unrolled_1.scala b/tests/run/unroll-abstractMethod2/Unrolled_1.scala new file mode 100644 index 000000000000..a1f10681f75c --- /dev/null +++ b/tests/run/unroll-abstractMethod2/Unrolled_1.scala @@ -0,0 +1,12 @@ +//> using options -experimental +import scala.annotation.unroll + +trait Unrolled { + def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String +} + +object UnrolledImpl { + val impl: Unrolled = new Unrolled { + def foo(s: String, n: Int, @unroll b: Boolean) = s + n + b + } +} From 766c4dd6072dcea72d1c6819eb9a7a5cabefa3a1 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 2 Oct 2024 21:28:58 +0200 Subject: [PATCH 12/35] SIP 61 - update stdlibExperimentalDefinitions.scala --- .../run-tasty-inspector/stdlibExperimentalDefinitions.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala index 65e3a730ee7e..ab24d4e2ea5f 100644 --- a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala @@ -93,7 +93,10 @@ val experimentalDefinitionInLibrary = Set( "scala.quoted.runtime.Patterns$.higherOrderHoleWithTypes", // New feature: SIP 57 - runtimeChecked replacement of @unchecked - "scala.Predef$.runtimeChecked", "scala.annotation.internal.RuntimeChecked" + "scala.Predef$.runtimeChecked", "scala.annotation.internal.RuntimeChecked", + + // New feature: SIP 61 - @unroll annotation + "scala.annotation.unroll", "scala.annotation.internal.UnrollForwarder", "scala.annotation.internal.AbstractUnroll", ) From a85d2f502f4917cae43ee0c2ddb01681d8a588d6 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 2 Oct 2024 21:52:38 +0200 Subject: [PATCH 13/35] delete debug line --- compiler/src/dotty/tools/dotc/transform/Pickler.scala | 3 --- 1 file changed, 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/Pickler.scala b/compiler/src/dotty/tools/dotc/transform/Pickler.scala index d78d68ea95f9..c8c071064ab8 100644 --- a/compiler/src/dotty/tools/dotc/transform/Pickler.scala +++ b/compiler/src/dotty/tools/dotc/transform/Pickler.scala @@ -457,9 +457,6 @@ class Pickler extends Phase { case None => () - if ctx.settings.YtestPicklerCheck.value then - sys.error(printedTasty(cls)) - inContext(printerContext(testJava)(using rootCtx.fresh.setCompilationUnit(freshUnit))): testSame(i"$unpickled%\n%", beforePickling(cls), cls) From 19dfbdec59afe7264b8d385ebb37dfb22f899717 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 2 Oct 2024 22:41:21 +0200 Subject: [PATCH 14/35] skip reflection test on scala.js --- tests/run/unroll-caseclass/Test_4.scala | 2 ++ tests/run/unroll-caseclass/unrolledV1_1.scala | 1 + tests/run/unroll-caseclass/unrolledV2_2.scala | 1 + tests/run/unroll-caseclass/unrolledV3_3.scala | 1 + 4 files changed, 5 insertions(+) diff --git a/tests/run/unroll-caseclass/Test_4.scala b/tests/run/unroll-caseclass/Test_4.scala index 3040e9133652..8b6b72f79cf7 100644 --- a/tests/run/unroll-caseclass/Test_4.scala +++ b/tests/run/unroll-caseclass/Test_4.scala @@ -1,4 +1,6 @@ //> using options -experimental +// scalajs: --skip + import example.* // !! IMPORTANT: If you remove this test, also remove unroll-caseclass.check @main def Test(): Unit = { diff --git a/tests/run/unroll-caseclass/unrolledV1_1.scala b/tests/run/unroll-caseclass/unrolledV1_1.scala index a07e8499442e..639a994e7e36 100644 --- a/tests/run/unroll-caseclass/unrolledV1_1.scala +++ b/tests/run/unroll-caseclass/unrolledV1_1.scala @@ -1,4 +1,5 @@ //> using options -experimental +// scalajs: --skip package example // !! IMPORTANT: If you remove this test, also remove unroll-caseclass.check diff --git a/tests/run/unroll-caseclass/unrolledV2_2.scala b/tests/run/unroll-caseclass/unrolledV2_2.scala index e1affa387652..7833c0eb6892 100644 --- a/tests/run/unroll-caseclass/unrolledV2_2.scala +++ b/tests/run/unroll-caseclass/unrolledV2_2.scala @@ -1,4 +1,5 @@ //> using options -experimental +// scalajs: --skip package example // !! IMPORTANT: If you remove this test, also remove unroll-caseclass.check diff --git a/tests/run/unroll-caseclass/unrolledV3_3.scala b/tests/run/unroll-caseclass/unrolledV3_3.scala index 9bb2d66a0039..5892e416ce5c 100644 --- a/tests/run/unroll-caseclass/unrolledV3_3.scala +++ b/tests/run/unroll-caseclass/unrolledV3_3.scala @@ -1,4 +1,5 @@ //> using options -experimental +// scalajs: --skip package example // !! IMPORTANT: If you remove this test, also remove unroll-caseclass.check From a464a2b0ab93dd6a5d2c9499e0ee55b23603585d Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 3 Oct 2024 11:57:16 +0200 Subject: [PATCH 15/35] Review: require final, remove special treatment of abstract methods --- .../dotty/tools/dotc/core/Definitions.scala | 1 - .../tools/dotc/transform/PostTyper.scala | 24 ++- .../dotc/transform/UnrollDefinitions.scala | 145 ++++++------------ .../annotation/internal/AbstractUnroll.scala | 10 -- .../abstractClassMethod/build.sbt | 69 --------- .../project/DottyInjectedPlugin.scala | 12 -- .../unroll-annot/abstractClassMethod/test | 17 -- .../utils/src/main/scala/TestUtils.scala | 12 -- .../v1/src/main/scala/Unrolled.scala | 5 - .../src/main/scala/UnrollTestMain.scala | 16 -- .../src/main/scala/Downstream.scala | 13 -- .../v2/src/main/scala/Unrolled.scala | 7 - .../src/main/scala/UnrollTestMain.scala | 17 -- .../src/main/scala/src/Downstream.scala | 15 -- .../v3/src/main/scala/Unrolled.scala | 7 - .../src/main/scala/UnrollTestMain.scala | 21 --- .../scala/UnrollTestPlatformSpecific.scala | 28 ---- .../src/main/scala/Downstream.scala | 15 -- .../abstractTraitMethod/build.sbt | 69 --------- .../project/DottyInjectedPlugin.scala | 12 -- .../unroll-annot/abstractTraitMethod/test | 17 -- .../utils/src/main/scala/TestUtils.scala | 12 -- .../v1/src/main/scala/Unrolled.scala | 5 - .../src/main/scala/UnrollTestMain.scala | 16 -- .../src/main/scala/Downstream.scala | 13 -- .../v2/src/main/scala/Unrolled.scala | 7 - .../src/main/scala/UnrollTestMain.scala | 17 -- .../src/main/scala/Downstream.scala | 15 -- .../v3/src/main/scala/Unrolled.scala | 7 - .../src/main/scala/UnrollTestMain.scala | 21 --- .../scala/UnrollTestPlatformSpecific.scala | 28 ---- .../src/main/scala/Downstream.scala | 15 -- .../v2/src/main/scala/Unrolled.scala | 2 +- .../v3/src/main/scala/Unrolled.scala | 2 +- .../v2/src/main/scala/Unrolled.scala | 2 +- .../v3/src/main/scala/Unrolled.scala | 2 +- .../v2/src/main/scala/Unrolled.scala | 2 +- .../v3/src/main/scala/Unrolled.scala | 2 +- .../v2/src/main/scala/Unrolled.scala | 2 +- .../v3/src/main/scala/Unrolled.scala | 2 +- .../v2/src/main/scala/Unrolled.scala | 2 +- .../v3/src/main/scala/Unrolled.scala | 2 +- .../v2/src/main/scala/Unrolled.scala | 2 +- .../v2/src/main/scala/Unrolled.scala | 2 +- .../v3/src/main/scala/Unrolled.scala | 2 +- .../v2/src/main/scala/Unrolled.scala | 4 +- .../v3/src/main/scala/Unrolled.scala | 4 +- tests/neg/unroll-abstractMethod.check | 8 + .../unroll-abstractMethod.scala} | 9 +- tests/neg/unroll-clash.check | 2 +- tests/neg/unroll-clash.scala | 2 +- tests/neg/unroll-no-default.check | 8 +- tests/neg/unroll-no-default.scala | 2 +- .../stdlibExperimentalDefinitions.scala | 2 +- tests/run/unroll-abstractMethod.scala | 28 ---- .../Test_2.scala | 0 tests/run/unroll-traitMethod/Unrolled_1.scala | 10 ++ 57 files changed, 118 insertions(+), 675 deletions(-) delete mode 100644 library/src/scala/annotation/internal/AbstractUnroll.scala delete mode 100644 sbt-test/unroll-annot/abstractClassMethod/build.sbt delete mode 100644 sbt-test/unroll-annot/abstractClassMethod/project/DottyInjectedPlugin.scala delete mode 100644 sbt-test/unroll-annot/abstractClassMethod/test delete mode 100644 sbt-test/unroll-annot/abstractClassMethod/utils/src/main/scala/TestUtils.scala delete mode 100644 sbt-test/unroll-annot/abstractClassMethod/v1/src/main/scala/Unrolled.scala delete mode 100644 sbt-test/unroll-annot/abstractClassMethod/v1_app/src/main/scala/UnrollTestMain.scala delete mode 100644 sbt-test/unroll-annot/abstractClassMethod/v1_downstream/src/main/scala/Downstream.scala delete mode 100644 sbt-test/unroll-annot/abstractClassMethod/v2/src/main/scala/Unrolled.scala delete mode 100644 sbt-test/unroll-annot/abstractClassMethod/v2_app/src/main/scala/UnrollTestMain.scala delete mode 100644 sbt-test/unroll-annot/abstractClassMethod/v2_downstream/src/main/scala/src/Downstream.scala delete mode 100644 sbt-test/unroll-annot/abstractClassMethod/v3/src/main/scala/Unrolled.scala delete mode 100644 sbt-test/unroll-annot/abstractClassMethod/v3_app/src/main/scala/UnrollTestMain.scala delete mode 100644 sbt-test/unroll-annot/abstractClassMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala delete mode 100644 sbt-test/unroll-annot/abstractClassMethod/v3_downstream/src/main/scala/Downstream.scala delete mode 100644 sbt-test/unroll-annot/abstractTraitMethod/build.sbt delete mode 100644 sbt-test/unroll-annot/abstractTraitMethod/project/DottyInjectedPlugin.scala delete mode 100644 sbt-test/unroll-annot/abstractTraitMethod/test delete mode 100644 sbt-test/unroll-annot/abstractTraitMethod/utils/src/main/scala/TestUtils.scala delete mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v1/src/main/scala/Unrolled.scala delete mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v1_app/src/main/scala/UnrollTestMain.scala delete mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v1_downstream/src/main/scala/Downstream.scala delete mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v2/src/main/scala/Unrolled.scala delete mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v2_app/src/main/scala/UnrollTestMain.scala delete mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v2_downstream/src/main/scala/Downstream.scala delete mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v3/src/main/scala/Unrolled.scala delete mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v3_app/src/main/scala/UnrollTestMain.scala delete mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala delete mode 100644 sbt-test/unroll-annot/abstractTraitMethod/v3_downstream/src/main/scala/Downstream.scala create mode 100644 tests/neg/unroll-abstractMethod.check rename tests/{run/unroll-abstractMethod2/Unrolled_1.scala => neg/unroll-abstractMethod.scala} (53%) delete mode 100644 tests/run/unroll-abstractMethod.scala rename tests/run/{unroll-abstractMethod2 => unroll-traitMethod}/Test_2.scala (100%) create mode 100644 tests/run/unroll-traitMethod/Unrolled_1.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index d0537f30c9d5..5b3b2fc47050 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1037,7 +1037,6 @@ class Definitions { @tu lazy val NowarnAnnot: ClassSymbol = requiredClass("scala.annotation.nowarn") @tu lazy val UnusedAnnot: ClassSymbol = requiredClass("scala.annotation.unused") @tu lazy val UnrollAnnot: ClassSymbol = requiredClass("scala.annotation.unroll") - @tu lazy val AbstractUnrollAnnot: ClassSymbol = requiredClass("scala.annotation.internal.AbstractUnroll") @tu lazy val UnrollForwarderAnnot: ClassSymbol = requiredClass("scala.annotation.internal.UnrollForwarder") @tu lazy val TransparentTraitAnnot: ClassSymbol = requiredClass("scala.annotation.transparentTrait") @tu lazy val NativeAnnot: ClassSymbol = requiredClass("scala.native") diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 59b3bba2f89c..2f980cf4f0da 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -119,8 +119,30 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => private var inJavaAnnot: Boolean = false + private var seenUnrolledMethods: util.EqHashMap[Symbol, Boolean] | Null = null + private var noCheckNews: Set[New] = Set() + def isValidUnrolledMethod(method: Symbol)(using Context): Boolean = + val seenMethods = + val local = seenUnrolledMethods + if local == null then + val map = new util.EqHashMap[Symbol, Boolean] + seenUnrolledMethods = map + map + else + local + seenMethods.getOrElseUpdate(method, { + var res = true + if method.is(Deferred) then + report.error("Unrolled method must be final and concrete", method.srcPos) + res = false + if !method.isConstructor && !method.is(Final) then + report.error("Unrolled method must be final", method.srcPos) + res = false + res + }) + def withNoCheckNews[T](ts: List[New])(op: => T): T = { val saved = noCheckNews noCheckNews ++= ts @@ -200,7 +222,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => } private def registerIfUnrolledParam(sym: Symbol)(using Context): Unit = - if sym.getAnnotation(defn.UnrollAnnot).isDefined then + if sym.hasAnnotation(defn.UnrollAnnot) && isValidUnrolledMethod(sym.owner) then val cls = sym.enclosingClass val classes = ctx.compilationUnit.unrolledClasses val additions = Array(cls, cls.linkedClass).filter(_ != NoSymbol) diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala index df364c76737c..a3153ea40ed8 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala @@ -21,6 +21,7 @@ import dotty.tools.dotc.printing.Formatting.hl import scala.collection.mutable import scala.util.boundary, boundary.break import dotty.tools.dotc.core.StdNames.nme +import dotty.tools.unreachable /**Implementation of SIP-61. * Runs when `@unroll` annotations are found in a compilation unit, installing new definitions @@ -30,11 +31,11 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { import tpd.* - private var _unrolledDefs: util.HashMap[Symbol, ComputedIndicies] | Null = null - private def initializeUnrolledDefs(): util.HashMap[Symbol, ComputedIndicies] = + private var _unrolledDefs: util.EqHashMap[Symbol, ComputedIndicies] | Null = null + private def initializeUnrolledDefs(): util.EqHashMap[Symbol, ComputedIndicies] = val local = _unrolledDefs if local == null then - val map = new util.HashMap[Symbol, ComputedIndicies] + val map = new util.EqHashMap[Symbol, ComputedIndicies] _unrolledDefs = map map else @@ -54,7 +55,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { def newTransformer(using Context): Transformer = UnrollingTransformer(ctx.compilationUnit.unrolledClasses.nn) - type ComputedIndicies = Seq[(Int, List[Int])] + type ComputedIndicies = List[(Int, List[Int])] type ComputeIndicies = Context ?=> Symbol => ComputedIndicies private class UnrollingTransformer(classes: Set[Symbol]) extends Transformer { @@ -68,7 +69,10 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { .flatMap { (paramClause, paramClauseIndex) => val annotationIndices = findUnrollAnnotations(paramClause) if (annotationIndices.isEmpty) None - else Some((paramClauseIndex, annotationIndices)) + else + require(annotated.is(Final, butNot = Deferred) || annotated.isConstructor, + i"${annotated} is not final&concrete, or a constructor") + Some((paramClauseIndex, annotationIndices)) } }) end computeIndices @@ -109,8 +113,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { nextParamIndex: Int, nextSymbol: Symbol, annotatedParamListIndex: Int, - isCaseApply: Boolean, - inferOverride: Boolean)(using Context) = { + isCaseApply: Boolean)(using Context) = { def initNewForwarder()(using Context): (TermSymbol, List[List[Symbol]]) = { val forwarderDefSymbol0 = Symbols.newSymbol( @@ -119,8 +122,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { (defdef.symbol.flags &~ HasDefaultParams &~ (if nextParamIndex == -1 then EmptyFlags else Deferred)) | - Invisible | Synthetic | - (if inferOverride then Override else EmptyFlags), + Invisible | Synthetic, NoType, // fill in later coord = nextSymbol.span.shift(1) // shift by 1 to avoid "secondary constructor must call preceding" error ).entered @@ -151,20 +153,8 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { } val paramCount = defdef.symbol.paramSymss(annotatedParamListIndex).size - val isDeferredInitial = paramCount == paramIndex && defdef.symbol.is(Deferred) - val (forwarderDefSymbol, newParamSymLists) = - if isDeferredInitial then - val existing = defdef.symbol.asTerm - existing.addAnnotation(defn.AbstractUnrollAnnot) // mark as previously abstract - existing.flags = (existing.flags &~ Deferred) // going to implement its rhs - existing -> extractParamSymss(identity) - else - initNewForwarder() - - if inferOverride then - // in this case we will not replace the source method, but we will add the override flag - defdef.symbol.flags_=(defdef.symbol.flags | Override) + val (forwarderDefSymbol, newParamSymLists) = initNewForwarder() def forwarderRhs(): tpd.Tree = { val defaultOffset = defdef.paramss @@ -233,7 +223,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { tpd.DefDef(forwarderDefSymbol, rhs = if nextParamIndex == -1 then EmptyTree else forwarderRhs()) - forwarderDef.withSpan(if isDeferredInitial then defdef.span else nextSymbol.span.shift(1)) + forwarderDef.withSpan(nextSymbol.span.shift(1)) } def generateFromProduct(startParamIndices: List[Int], paramCount: Int, defdef: DefDef)(using Context) = { @@ -268,25 +258,10 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { ).setDefTree } - def generateSyntheticDefs(tree: Tree, compute: ComputeIndicies)(using Context): (Option[Symbol], Seq[(Symbol, Tree)]) = tree match { + def generateSyntheticDefs(tree: Tree, compute: ComputeIndicies)(using Context): Option[(Symbol, Option[Symbol], Seq[DefDef])] = tree match { case defdef: DefDef if defdef.paramss.nonEmpty => import dotty.tools.dotc.core.NameOps.isConstructorName - // infer an override when we are implementing a method that matches the signature and has unroll annotations - // in the same positions - lazy val inferOverride = { - def unrollIndices(sym: Symbol): List[Int] = - sym.paramSymss.flatten.zipWithIndex.collect({ - case (p, i) if p.hasAnnotation(defn.UnrollAnnot) => i - }) - - val candidate = defdef.symbol.nextOverriddenSymbol - candidate.exists && !candidate.is(Deferred) && candidate.hasAnnotation(defn.AbstractUnrollAnnot) && { - // check unroll indices match - unrollIndices(candidate) == unrollIndices(defdef.symbol) - } - } - val isCaseCopy = defdef.name.toString == "copy" && defdef.symbol.owner.is(CaseClass) @@ -302,81 +277,54 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { else defdef.symbol compute(annotated) match { - case Nil => (None, Nil) + case Nil => None case Seq((paramClauseIndex, annotationIndices)) => val paramCount = annotated.paramSymss(paramClauseIndex).size - if (isCaseFromProduct) { - val newDef = generateFromProduct(annotationIndices, paramCount, defdef) - (Some(defdef.symbol), Seq(defdef.symbol -> newDef)) - } else { - if (defdef.symbol.is(Deferred)){ - ( - Some(defdef.symbol), - (-1 +: annotationIndices :+ paramCount).sliding(2).toList.foldLeft((Seq.empty[(Symbol, DefDef)], defdef.symbol))((m, v) => ((m, v): @unchecked) match { - case ((defdefs, nextSymbol), Seq(paramIndex, nextParamIndex)) => - val forwarder = generateSingleForwarder( - defdef, - defdef.symbol.info, - nextParamIndex, - paramIndex, - nextSymbol, - paramClauseIndex, - isCaseApply, - inferOverride - ) - // replacements += forwarder.symbol - ((defdef.symbol -> forwarder) +: defdefs, forwarder.symbol) - })._1 - ) - - }else{ - ( - None, - (annotationIndices :+ paramCount).sliding(2).toList.reverse.foldLeft((Seq.empty[(Symbol, DefDef)], defdef.symbol))((m, v) => ((m, v): @unchecked) match { - case ((defdefs, nextSymbol), Seq(paramIndex, nextParamIndex)) => - val forwarder = generateSingleForwarder( - defdef, - defdef.symbol.info, - paramIndex, - nextParamIndex, - nextSymbol, - paramClauseIndex, - isCaseApply, - inferOverride - ) - ((defdef.symbol -> forwarder) +: defdefs, forwarder.symbol) - })._1 - ) - } - } + if isCaseFromProduct then + Some((defdef.symbol, Some(defdef.symbol), Seq(generateFromProduct(annotationIndices, paramCount, defdef)))) + else + val (generatedDefs, _) = + val indices = (annotationIndices :+ paramCount).sliding(2).toList.reverse + indices.foldLeft((Seq.empty[DefDef], defdef.symbol)): + case ((defdefs, nextSymbol), Seq(paramIndex, nextParamIndex)) => + val forwarder = generateSingleForwarder( + defdef, + defdef.symbol.info, + paramIndex, + nextParamIndex, + nextSymbol, + paramClauseIndex, + isCaseApply + ) + (forwarder +: defdefs, forwarder.symbol) + case _ => unreachable("sliding with at least 2 elements") + Some((defdef.symbol, None, generatedDefs)) case multiple => sys.error("Cannot have multiple parameter lists containing `@unroll` annotation") } - case _ => (None, Nil) + case _ => None } def unrollTemplate(tmpl: tpd.Template, compute: ComputeIndicies)(using Context): tpd.Tree = { - val (removed0, generatedDefs0) = tmpl.body.map(generateSyntheticDefs(_, compute)).unzip - val (removedCtor, generatedConstr0) = generateSyntheticDefs(tmpl.constr, compute) - val removedSymsBody = removed0.flatten - val allRemoved = removedSymsBody ++ removedCtor - - val generatedDefOrigins = generatedDefs0.flatten - val generatedDefs = generatedDefOrigins.map(_(1)) - val generatedConstr = generatedConstr0.map(_(1)) - - val otherDecls = tmpl.body.filter(t => !removedSymsBody.contains(t.symbol)) + val generatedBody = tmpl.body.flatMap(generateSyntheticDefs(_, compute)) + val generatedConstr0 = generateSyntheticDefs(tmpl.constr, compute) + val allGenerated = generatedBody ++ generatedConstr0 + val bodySubs = generatedBody.flatMap((_, maybeSub, _) => maybeSub).toSet + val otherDecls = tmpl.body.filterNot(d => d.symbol.exists && bodySubs(d.symbol)) /** inlined from compiler/src/dotty/tools/dotc/typer/Checking.scala */ def checkClash(decl: Symbol, other: Symbol) = def staticNonStaticPair = decl.isScalaStatic != other.isScalaStatic decl.matches(other) && !staticNonStaticPair - if generatedDefOrigins.nonEmpty then + if allGenerated.nonEmpty then val byName = otherDecls.groupMap(_.symbol.name.toString)(_.symbol) - for case (src, dcl: NamedDefTree) <- generatedDefOrigins do + for + (src, _, dcls) <- allGenerated + dcl <- dcls + do val replaced = dcl.symbol byName.get(dcl.name.toString).foreach { syms => val clashes = syms.filter(checkClash(replaced, _)) @@ -387,6 +335,9 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { } end if + val generatedDefs = generatedBody.flatMap((_, _, gens) => gens) + val generatedConstr = generatedConstr0.toList.flatMap((_, _, gens) => gens) + cpy.Template(tmpl)( tmpl.constr, tmpl.parents, diff --git a/library/src/scala/annotation/internal/AbstractUnroll.scala b/library/src/scala/annotation/internal/AbstractUnroll.scala deleted file mode 100644 index 1f19081b0d84..000000000000 --- a/library/src/scala/annotation/internal/AbstractUnroll.scala +++ /dev/null @@ -1,10 +0,0 @@ -package scala.annotation.internal - -import scala.annotation.Annotation -import scala.annotation.experimental - -/** Indicates the method was abstract in source code before unrolling transformation was added. - * downstream direct overrides, with matching `@unroll` annotations will infer an override. - */ -@experimental("under review as part of SIP-61") -final class AbstractUnroll extends Annotation diff --git a/sbt-test/unroll-annot/abstractClassMethod/build.sbt b/sbt-test/unroll-annot/abstractClassMethod/build.sbt deleted file mode 100644 index 387de257af72..000000000000 --- a/sbt-test/unroll-annot/abstractClassMethod/build.sbt +++ /dev/null @@ -1,69 +0,0 @@ -lazy val utils = project.in(file("utils")) - -lazy val sharedSettings = Seq( - scalacOptions ++= Seq("-Ycheck:all", "-experimental") -) - -lazy val v1 = project.in(file("v1")) - .settings(sharedSettings) - -lazy val v1_downstream = project.in(file("v1_downstream")).dependsOn(v1) - .settings(sharedSettings) - -lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - Attributed.blank((v1 / Runtime / classDirectory).value), - Attributed.blank((v1_downstream / Runtime / classDirectory).value), - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v1 / Compile / classDirectory).value), - Attributed.blank((v1_downstream / Compile / classDirectory).value), - ), - ) - -lazy val v2 = project.in(file("v2")) - .settings(sharedSettings) - -lazy val v2_downstream = project.in(file("v2_downstream")).dependsOn(v2) - .settings(sharedSettings) - -lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - Attributed.blank((v2 / Runtime / classDirectory).value), - Attributed.blank((v2_downstream / Runtime / classDirectory).value), - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v2 / Compile / classDirectory).value), - Attributed.blank((v2_downstream / Compile / classDirectory).value) - ), - ) - -lazy val v3 = project.in(file("v3")) - .settings(sharedSettings) - -lazy val v3_downstream = project.in(file("v3_downstream")).dependsOn(v3) - .settings(sharedSettings) - -lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - // add v2_app, compiled against v2, to the classpath - Attributed.blank((v2_app / Runtime / classDirectory).value), - Attributed.blank((v3 / Runtime / classDirectory).value), - Attributed.blank((v3_downstream / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v3 / Compile / classDirectory).value), - Attributed.blank((v3_downstream / Compile / classDirectory).value) - ), - ) diff --git a/sbt-test/unroll-annot/abstractClassMethod/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/abstractClassMethod/project/DottyInjectedPlugin.scala deleted file mode 100644 index 69f15d168bfc..000000000000 --- a/sbt-test/unroll-annot/abstractClassMethod/project/DottyInjectedPlugin.scala +++ /dev/null @@ -1,12 +0,0 @@ -import sbt._ -import Keys._ - -object DottyInjectedPlugin extends AutoPlugin { - override def requires = plugins.JvmPlugin - override def trigger = allRequirements - - override val projectSettings = Seq( - scalaVersion := sys.props("plugin.scalaVersion"), - scalacOptions += "-source:3.0-migration" - ) -} diff --git a/sbt-test/unroll-annot/abstractClassMethod/test b/sbt-test/unroll-annot/abstractClassMethod/test deleted file mode 100644 index 7c0875518e21..000000000000 --- a/sbt-test/unroll-annot/abstractClassMethod/test +++ /dev/null @@ -1,17 +0,0 @@ -# compile and run a basic version of Unrolled (v1), and an app that uses it -> v1/compile -> v1_downstream/compile -> v1_app/runMain unroll.UnrollTestMainV1 -# add a field to the case class (v2), and update the app to use it, -# and ensure the old version (v1) still links -> v2/compile -> v2_downstream/compile -> v2_app/runMain unroll.UnrollTestMainV1 -> v2_app/runMain unroll.UnrollTestMainV2 -# add a field to the case class (v3), and update the app to use it, -# and ensure the old versions (v1, v2) still link -> v3/compile -> v3_downstream/compile -> v3_app/runMain unroll.UnrollTestMainV1 -> v3_app/runMain unroll.UnrollTestMainV2 -> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/abstractClassMethod/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/abstractClassMethod/utils/src/main/scala/TestUtils.scala deleted file mode 100644 index 6ac413c9fe98..000000000000 --- a/sbt-test/unroll-annot/abstractClassMethod/utils/src/main/scala/TestUtils.scala +++ /dev/null @@ -1,12 +0,0 @@ -package unroll - -object TestUtils { - def logAssertStartsWith(actual: String, expected: String): Unit = { - assert(actual.startsWith(expected)) - val suffix = { - val suffix0 = actual.stripPrefix(expected) - if (suffix0.isEmpty) "" else s""" + "$suffix0"""" - } - println(s"""Assertion passed: found "$expected"$suffix""") - } -} diff --git a/sbt-test/unroll-annot/abstractClassMethod/v1/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/abstractClassMethod/v1/src/main/scala/Unrolled.scala deleted file mode 100644 index 4ff7eac61475..000000000000 --- a/sbt-test/unroll-annot/abstractClassMethod/v1/src/main/scala/Unrolled.scala +++ /dev/null @@ -1,5 +0,0 @@ -package unroll - -abstract class Unrolled{ - def foo(s: String, n: Int = 1): String -} diff --git a/sbt-test/unroll-annot/abstractClassMethod/v1_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/abstractClassMethod/v1_app/src/main/scala/UnrollTestMain.scala deleted file mode 100644 index d069e35063b7..000000000000 --- a/sbt-test/unroll-annot/abstractClassMethod/v1_app/src/main/scala/UnrollTestMain.scala +++ /dev/null @@ -1,16 +0,0 @@ -package unroll - -import unroll.TestUtils.logAssertStartsWith - - - -object UnrollTestMainV1{ - def main(args: Array[String]): Unit = { - val unrolled = new UnrolledCls - logAssertStartsWith(unrolled.foo("cow"), "cow1") - logAssertStartsWith(unrolled.foo("cow", 2), "cow2") - - logAssertStartsWith(UnrolledObj.foo("cow"), "cow1") - logAssertStartsWith(UnrolledObj.foo("cow", 2), "cow2") - } -} diff --git a/sbt-test/unroll-annot/abstractClassMethod/v1_downstream/src/main/scala/Downstream.scala b/sbt-test/unroll-annot/abstractClassMethod/v1_downstream/src/main/scala/Downstream.scala deleted file mode 100644 index 69b33c82b1c7..000000000000 --- a/sbt-test/unroll-annot/abstractClassMethod/v1_downstream/src/main/scala/Downstream.scala +++ /dev/null @@ -1,13 +0,0 @@ -package unroll - -object UnrolledObj extends Unrolled { - def foo(s: String, n: Int = 1) = s + n -} - -class UnrolledCls extends Unrolled { - def foo(s: String, n: Int = 1) = s + n -} - -object UnrollMisc{ - def expectedLength = 4 -} \ No newline at end of file diff --git a/sbt-test/unroll-annot/abstractClassMethod/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/abstractClassMethod/v2/src/main/scala/Unrolled.scala deleted file mode 100644 index ab870c5fda3e..000000000000 --- a/sbt-test/unroll-annot/abstractClassMethod/v2/src/main/scala/Unrolled.scala +++ /dev/null @@ -1,7 +0,0 @@ -package unroll - -import scala.annotation.unroll - -abstract class Unrolled{ - def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String -} diff --git a/sbt-test/unroll-annot/abstractClassMethod/v2_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/abstractClassMethod/v2_app/src/main/scala/UnrollTestMain.scala deleted file mode 100644 index fd7a891222fc..000000000000 --- a/sbt-test/unroll-annot/abstractClassMethod/v2_app/src/main/scala/UnrollTestMain.scala +++ /dev/null @@ -1,17 +0,0 @@ -package unroll - -import unroll.TestUtils.logAssertStartsWith - - -object UnrollTestMainV2{ - def main(args: Array[String]): Unit = { - val unrolled = new UnrolledCls - logAssertStartsWith(unrolled.foo("cow"), "cow1true".take(UnrollMisc.expectedLength)) - logAssertStartsWith(unrolled.foo("cow", 2), "cow2true".take(UnrollMisc.expectedLength)) - logAssertStartsWith(unrolled.foo("cow", 2, false), "cow2fals".take(UnrollMisc.expectedLength)) - - logAssertStartsWith(UnrolledObj.foo("cow"), "cow1true".take(UnrollMisc.expectedLength)) - logAssertStartsWith(UnrolledObj.foo("cow", 2), "cow2true".take(UnrollMisc.expectedLength)) - logAssertStartsWith(UnrolledObj.foo("cow", 2, false), "cow2fals".take(UnrollMisc.expectedLength)) - } -} diff --git a/sbt-test/unroll-annot/abstractClassMethod/v2_downstream/src/main/scala/src/Downstream.scala b/sbt-test/unroll-annot/abstractClassMethod/v2_downstream/src/main/scala/src/Downstream.scala deleted file mode 100644 index e0545e97a42f..000000000000 --- a/sbt-test/unroll-annot/abstractClassMethod/v2_downstream/src/main/scala/src/Downstream.scala +++ /dev/null @@ -1,15 +0,0 @@ -package unroll - -import scala.annotation.unroll - -object UnrolledObj extends Unrolled { - def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b.toString.take(4) -} - -class UnrolledCls extends Unrolled { - def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b.toString.take(4) -} - -object UnrollMisc{ - def expectedLength = 6 -} \ No newline at end of file diff --git a/sbt-test/unroll-annot/abstractClassMethod/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/abstractClassMethod/v3/src/main/scala/Unrolled.scala deleted file mode 100644 index 991407a0da45..000000000000 --- a/sbt-test/unroll-annot/abstractClassMethod/v3/src/main/scala/Unrolled.scala +++ /dev/null @@ -1,7 +0,0 @@ -package unroll - -import scala.annotation.unroll - -abstract class Unrolled{ - def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0): String -} diff --git a/sbt-test/unroll-annot/abstractClassMethod/v3_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/abstractClassMethod/v3_app/src/main/scala/UnrollTestMain.scala deleted file mode 100644 index 904538003026..000000000000 --- a/sbt-test/unroll-annot/abstractClassMethod/v3_app/src/main/scala/UnrollTestMain.scala +++ /dev/null @@ -1,21 +0,0 @@ -package unroll - -import unroll.TestUtils.logAssertStartsWith - - -object UnrollTestMainV3{ - def main(args: Array[String]): Unit = { - UnrollTestPlatformSpecificV3() - - val unrolled = new UnrolledCls - logAssertStartsWith(unrolled.foo("cow"), "cow1true0".take(UnrollMisc.expectedLength)) - logAssertStartsWith(unrolled.foo("cow", 2), "cow2true0".take(UnrollMisc.expectedLength)) - logAssertStartsWith(unrolled.foo("cow", 2, false), "cow2fals0".take(UnrollMisc.expectedLength)) - logAssertStartsWith(unrolled.foo("cow", 2, false, 3), "cow2fals3".take(UnrollMisc.expectedLength)) - - logAssertStartsWith(UnrolledObj.foo("cow"), "cow1true0".take(UnrollMisc.expectedLength)) - logAssertStartsWith(UnrolledObj.foo("cow", 2), "cow2true0".take(UnrollMisc.expectedLength)) - logAssertStartsWith(UnrolledObj.foo("cow", 2, false), "cow2fals0".take(UnrollMisc.expectedLength)) - logAssertStartsWith(UnrolledObj.foo("cow", 2, false, 3), "cow2fals3".take(UnrollMisc.expectedLength)) - } -} diff --git a/sbt-test/unroll-annot/abstractClassMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/sbt-test/unroll-annot/abstractClassMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala deleted file mode 100644 index feef2fe677da..000000000000 --- a/sbt-test/unroll-annot/abstractClassMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala +++ /dev/null @@ -1,28 +0,0 @@ -package unroll - -object UnrollTestPlatformSpecificV3{ - def apply() = { -// val instance = new UnrolledCls {} -// val cls = classOf[UnrolledCls] -// -// cls.getMethods.filter(_.getName.contains("foo")).foreach(println) -// -// assert(scala.util.Try(cls.getMethod("foo", classOf[String])).isFailure) -// println() -// assert( -// cls.getMethod("foo", classOf[String], classOf[Int]).invoke(instance, "hello", 2: Integer) == -// "hello2true0" -// ) -// assert( -// cls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean]) -// .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE) == -// "hello2false0" -// ) -// assert( -// cls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean], classOf[Long]) -// .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE, 3: Integer) == -// "hello2false3" -// ) - - } -} \ No newline at end of file diff --git a/sbt-test/unroll-annot/abstractClassMethod/v3_downstream/src/main/scala/Downstream.scala b/sbt-test/unroll-annot/abstractClassMethod/v3_downstream/src/main/scala/Downstream.scala deleted file mode 100644 index be2a68e8efff..000000000000 --- a/sbt-test/unroll-annot/abstractClassMethod/v3_downstream/src/main/scala/Downstream.scala +++ /dev/null @@ -1,15 +0,0 @@ -package unroll - -import scala.annotation.unroll - -object UnrolledObj extends Unrolled { - def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0) = s + n + b.toString.take(4) + l -} - -class UnrolledCls extends Unrolled { - def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0) = s + n + b.toString.take(4) + l -} - -object UnrollMisc{ - def expectedLength = 9 -} \ No newline at end of file diff --git a/sbt-test/unroll-annot/abstractTraitMethod/build.sbt b/sbt-test/unroll-annot/abstractTraitMethod/build.sbt deleted file mode 100644 index 387de257af72..000000000000 --- a/sbt-test/unroll-annot/abstractTraitMethod/build.sbt +++ /dev/null @@ -1,69 +0,0 @@ -lazy val utils = project.in(file("utils")) - -lazy val sharedSettings = Seq( - scalacOptions ++= Seq("-Ycheck:all", "-experimental") -) - -lazy val v1 = project.in(file("v1")) - .settings(sharedSettings) - -lazy val v1_downstream = project.in(file("v1_downstream")).dependsOn(v1) - .settings(sharedSettings) - -lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - Attributed.blank((v1 / Runtime / classDirectory).value), - Attributed.blank((v1_downstream / Runtime / classDirectory).value), - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v1 / Compile / classDirectory).value), - Attributed.blank((v1_downstream / Compile / classDirectory).value), - ), - ) - -lazy val v2 = project.in(file("v2")) - .settings(sharedSettings) - -lazy val v2_downstream = project.in(file("v2_downstream")).dependsOn(v2) - .settings(sharedSettings) - -lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - Attributed.blank((v2 / Runtime / classDirectory).value), - Attributed.blank((v2_downstream / Runtime / classDirectory).value), - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v2 / Compile / classDirectory).value), - Attributed.blank((v2_downstream / Compile / classDirectory).value) - ), - ) - -lazy val v3 = project.in(file("v3")) - .settings(sharedSettings) - -lazy val v3_downstream = project.in(file("v3_downstream")).dependsOn(v3) - .settings(sharedSettings) - -lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - // add v2_app, compiled against v2, to the classpath - Attributed.blank((v2_app / Runtime / classDirectory).value), - Attributed.blank((v3 / Runtime / classDirectory).value), - Attributed.blank((v3_downstream / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v3 / Compile / classDirectory).value), - Attributed.blank((v3_downstream / Compile / classDirectory).value) - ), - ) diff --git a/sbt-test/unroll-annot/abstractTraitMethod/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/abstractTraitMethod/project/DottyInjectedPlugin.scala deleted file mode 100644 index 69f15d168bfc..000000000000 --- a/sbt-test/unroll-annot/abstractTraitMethod/project/DottyInjectedPlugin.scala +++ /dev/null @@ -1,12 +0,0 @@ -import sbt._ -import Keys._ - -object DottyInjectedPlugin extends AutoPlugin { - override def requires = plugins.JvmPlugin - override def trigger = allRequirements - - override val projectSettings = Seq( - scalaVersion := sys.props("plugin.scalaVersion"), - scalacOptions += "-source:3.0-migration" - ) -} diff --git a/sbt-test/unroll-annot/abstractTraitMethod/test b/sbt-test/unroll-annot/abstractTraitMethod/test deleted file mode 100644 index 7c0875518e21..000000000000 --- a/sbt-test/unroll-annot/abstractTraitMethod/test +++ /dev/null @@ -1,17 +0,0 @@ -# compile and run a basic version of Unrolled (v1), and an app that uses it -> v1/compile -> v1_downstream/compile -> v1_app/runMain unroll.UnrollTestMainV1 -# add a field to the case class (v2), and update the app to use it, -# and ensure the old version (v1) still links -> v2/compile -> v2_downstream/compile -> v2_app/runMain unroll.UnrollTestMainV1 -> v2_app/runMain unroll.UnrollTestMainV2 -# add a field to the case class (v3), and update the app to use it, -# and ensure the old versions (v1, v2) still link -> v3/compile -> v3_downstream/compile -> v3_app/runMain unroll.UnrollTestMainV1 -> v3_app/runMain unroll.UnrollTestMainV2 -> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/abstractTraitMethod/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/abstractTraitMethod/utils/src/main/scala/TestUtils.scala deleted file mode 100644 index 6ac413c9fe98..000000000000 --- a/sbt-test/unroll-annot/abstractTraitMethod/utils/src/main/scala/TestUtils.scala +++ /dev/null @@ -1,12 +0,0 @@ -package unroll - -object TestUtils { - def logAssertStartsWith(actual: String, expected: String): Unit = { - assert(actual.startsWith(expected)) - val suffix = { - val suffix0 = actual.stripPrefix(expected) - if (suffix0.isEmpty) "" else s""" + "$suffix0"""" - } - println(s"""Assertion passed: found "$expected"$suffix""") - } -} diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v1/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/abstractTraitMethod/v1/src/main/scala/Unrolled.scala deleted file mode 100644 index a80db5e1fa73..000000000000 --- a/sbt-test/unroll-annot/abstractTraitMethod/v1/src/main/scala/Unrolled.scala +++ /dev/null @@ -1,5 +0,0 @@ -package unroll - -trait Unrolled{ - def foo(s: String, n: Int = 1): String -} diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v1_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/abstractTraitMethod/v1_app/src/main/scala/UnrollTestMain.scala deleted file mode 100644 index d069e35063b7..000000000000 --- a/sbt-test/unroll-annot/abstractTraitMethod/v1_app/src/main/scala/UnrollTestMain.scala +++ /dev/null @@ -1,16 +0,0 @@ -package unroll - -import unroll.TestUtils.logAssertStartsWith - - - -object UnrollTestMainV1{ - def main(args: Array[String]): Unit = { - val unrolled = new UnrolledCls - logAssertStartsWith(unrolled.foo("cow"), "cow1") - logAssertStartsWith(unrolled.foo("cow", 2), "cow2") - - logAssertStartsWith(UnrolledObj.foo("cow"), "cow1") - logAssertStartsWith(UnrolledObj.foo("cow", 2), "cow2") - } -} diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v1_downstream/src/main/scala/Downstream.scala b/sbt-test/unroll-annot/abstractTraitMethod/v1_downstream/src/main/scala/Downstream.scala deleted file mode 100644 index 69b33c82b1c7..000000000000 --- a/sbt-test/unroll-annot/abstractTraitMethod/v1_downstream/src/main/scala/Downstream.scala +++ /dev/null @@ -1,13 +0,0 @@ -package unroll - -object UnrolledObj extends Unrolled { - def foo(s: String, n: Int = 1) = s + n -} - -class UnrolledCls extends Unrolled { - def foo(s: String, n: Int = 1) = s + n -} - -object UnrollMisc{ - def expectedLength = 4 -} \ No newline at end of file diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/abstractTraitMethod/v2/src/main/scala/Unrolled.scala deleted file mode 100644 index f701334aee60..000000000000 --- a/sbt-test/unroll-annot/abstractTraitMethod/v2/src/main/scala/Unrolled.scala +++ /dev/null @@ -1,7 +0,0 @@ -package unroll - -import scala.annotation.unroll - -trait Unrolled{ - def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String -} diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v2_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/abstractTraitMethod/v2_app/src/main/scala/UnrollTestMain.scala deleted file mode 100644 index fd7a891222fc..000000000000 --- a/sbt-test/unroll-annot/abstractTraitMethod/v2_app/src/main/scala/UnrollTestMain.scala +++ /dev/null @@ -1,17 +0,0 @@ -package unroll - -import unroll.TestUtils.logAssertStartsWith - - -object UnrollTestMainV2{ - def main(args: Array[String]): Unit = { - val unrolled = new UnrolledCls - logAssertStartsWith(unrolled.foo("cow"), "cow1true".take(UnrollMisc.expectedLength)) - logAssertStartsWith(unrolled.foo("cow", 2), "cow2true".take(UnrollMisc.expectedLength)) - logAssertStartsWith(unrolled.foo("cow", 2, false), "cow2fals".take(UnrollMisc.expectedLength)) - - logAssertStartsWith(UnrolledObj.foo("cow"), "cow1true".take(UnrollMisc.expectedLength)) - logAssertStartsWith(UnrolledObj.foo("cow", 2), "cow2true".take(UnrollMisc.expectedLength)) - logAssertStartsWith(UnrolledObj.foo("cow", 2, false), "cow2fals".take(UnrollMisc.expectedLength)) - } -} diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v2_downstream/src/main/scala/Downstream.scala b/sbt-test/unroll-annot/abstractTraitMethod/v2_downstream/src/main/scala/Downstream.scala deleted file mode 100644 index a194928948d6..000000000000 --- a/sbt-test/unroll-annot/abstractTraitMethod/v2_downstream/src/main/scala/Downstream.scala +++ /dev/null @@ -1,15 +0,0 @@ -package unroll - -import scala.annotation.unroll - -object UnrolledObj extends Unrolled { - def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b.toString.take(4) -} - -class UnrolledCls extends Unrolled { - def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b.toString.take(4) -} - -object UnrollMisc{ - def expectedLength = 6 -} diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/abstractTraitMethod/v3/src/main/scala/Unrolled.scala deleted file mode 100644 index fb26b7c114b1..000000000000 --- a/sbt-test/unroll-annot/abstractTraitMethod/v3/src/main/scala/Unrolled.scala +++ /dev/null @@ -1,7 +0,0 @@ -package unroll - -import scala.annotation.unroll - -trait Unrolled{ - def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0): String -} diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v3_app/src/main/scala/UnrollTestMain.scala b/sbt-test/unroll-annot/abstractTraitMethod/v3_app/src/main/scala/UnrollTestMain.scala deleted file mode 100644 index 904538003026..000000000000 --- a/sbt-test/unroll-annot/abstractTraitMethod/v3_app/src/main/scala/UnrollTestMain.scala +++ /dev/null @@ -1,21 +0,0 @@ -package unroll - -import unroll.TestUtils.logAssertStartsWith - - -object UnrollTestMainV3{ - def main(args: Array[String]): Unit = { - UnrollTestPlatformSpecificV3() - - val unrolled = new UnrolledCls - logAssertStartsWith(unrolled.foo("cow"), "cow1true0".take(UnrollMisc.expectedLength)) - logAssertStartsWith(unrolled.foo("cow", 2), "cow2true0".take(UnrollMisc.expectedLength)) - logAssertStartsWith(unrolled.foo("cow", 2, false), "cow2fals0".take(UnrollMisc.expectedLength)) - logAssertStartsWith(unrolled.foo("cow", 2, false, 3), "cow2fals3".take(UnrollMisc.expectedLength)) - - logAssertStartsWith(UnrolledObj.foo("cow"), "cow1true0".take(UnrollMisc.expectedLength)) - logAssertStartsWith(UnrolledObj.foo("cow", 2), "cow2true0".take(UnrollMisc.expectedLength)) - logAssertStartsWith(UnrolledObj.foo("cow", 2, false), "cow2fals0".take(UnrollMisc.expectedLength)) - logAssertStartsWith(UnrolledObj.foo("cow", 2, false, 3), "cow2fals3".take(UnrollMisc.expectedLength)) - } -} diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/sbt-test/unroll-annot/abstractTraitMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala deleted file mode 100644 index feef2fe677da..000000000000 --- a/sbt-test/unroll-annot/abstractTraitMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala +++ /dev/null @@ -1,28 +0,0 @@ -package unroll - -object UnrollTestPlatformSpecificV3{ - def apply() = { -// val instance = new UnrolledCls {} -// val cls = classOf[UnrolledCls] -// -// cls.getMethods.filter(_.getName.contains("foo")).foreach(println) -// -// assert(scala.util.Try(cls.getMethod("foo", classOf[String])).isFailure) -// println() -// assert( -// cls.getMethod("foo", classOf[String], classOf[Int]).invoke(instance, "hello", 2: Integer) == -// "hello2true0" -// ) -// assert( -// cls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean]) -// .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE) == -// "hello2false0" -// ) -// assert( -// cls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean], classOf[Long]) -// .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE, 3: Integer) == -// "hello2false3" -// ) - - } -} \ No newline at end of file diff --git a/sbt-test/unroll-annot/abstractTraitMethod/v3_downstream/src/main/scala/Downstream.scala b/sbt-test/unroll-annot/abstractTraitMethod/v3_downstream/src/main/scala/Downstream.scala deleted file mode 100644 index be2a68e8efff..000000000000 --- a/sbt-test/unroll-annot/abstractTraitMethod/v3_downstream/src/main/scala/Downstream.scala +++ /dev/null @@ -1,15 +0,0 @@ -package unroll - -import scala.annotation.unroll - -object UnrolledObj extends Unrolled { - def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0) = s + n + b.toString.take(4) + l -} - -class UnrolledCls extends Unrolled { - def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0) = s + n + b.toString.take(4) + l -} - -object UnrollMisc{ - def expectedLength = 9 -} \ No newline at end of file diff --git a/sbt-test/unroll-annot/classMethod/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/classMethod/v2/src/main/scala/Unrolled.scala index e46d36c230c0..6d756148a098 100644 --- a/sbt-test/unroll-annot/classMethod/v2/src/main/scala/Unrolled.scala +++ b/sbt-test/unroll-annot/classMethod/v2/src/main/scala/Unrolled.scala @@ -3,5 +3,5 @@ package unroll import scala.annotation.unroll class Unrolled{ - def foo(s: String, @unroll n: Int = 1, b: Boolean = true) = s + n + b + final def foo(s: String, @unroll n: Int = 1, b: Boolean = true) = s + n + b } diff --git a/sbt-test/unroll-annot/classMethod/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/classMethod/v3/src/main/scala/Unrolled.scala index 5f0b76a799d4..684c3906750e 100644 --- a/sbt-test/unroll-annot/classMethod/v3/src/main/scala/Unrolled.scala +++ b/sbt-test/unroll-annot/classMethod/v3/src/main/scala/Unrolled.scala @@ -3,5 +3,5 @@ package unroll import scala.annotation.unroll class Unrolled{ - def foo(s: String, @unroll n: Int = 1, b: Boolean = true, @unroll l: Long = 0) = s + n + b + l + final def foo(s: String, @unroll n: Int = 1, b: Boolean = true, @unroll l: Long = 0) = s + n + b + l } diff --git a/sbt-test/unroll-annot/curriedMethod/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/curriedMethod/v2/src/main/scala/Unrolled.scala index 843dbaa73e16..595c06d6dc50 100644 --- a/sbt-test/unroll-annot/curriedMethod/v2/src/main/scala/Unrolled.scala +++ b/sbt-test/unroll-annot/curriedMethod/v2/src/main/scala/Unrolled.scala @@ -3,5 +3,5 @@ package unroll import scala.annotation.unroll class Unrolled{ - def foo(s: String, @unroll n: Int = 1, b: Boolean = true)(f: String => String) = f(s + n + b) + final def foo(s: String, @unroll n: Int = 1, b: Boolean = true)(f: String => String) = f(s + n + b) } diff --git a/sbt-test/unroll-annot/curriedMethod/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/curriedMethod/v3/src/main/scala/Unrolled.scala index 01c34e9548cf..f34c9b071edd 100644 --- a/sbt-test/unroll-annot/curriedMethod/v3/src/main/scala/Unrolled.scala +++ b/sbt-test/unroll-annot/curriedMethod/v3/src/main/scala/Unrolled.scala @@ -3,5 +3,5 @@ package unroll import scala.annotation.unroll class Unrolled{ - def foo(s: String, @unroll n: Int = 1, b: Boolean = true, @unroll l: Long = 0)(f: String => String) = f(s + n + b + l) + final def foo(s: String, @unroll n: Int = 1, b: Boolean = true, @unroll l: Long = 0)(f: String => String) = f(s + n + b + l) } diff --git a/sbt-test/unroll-annot/genericMethod/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/genericMethod/v2/src/main/scala/Unrolled.scala index 9b5566ef6b27..191a4df184ef 100644 --- a/sbt-test/unroll-annot/genericMethod/v2/src/main/scala/Unrolled.scala +++ b/sbt-test/unroll-annot/genericMethod/v2/src/main/scala/Unrolled.scala @@ -3,5 +3,5 @@ package unroll import scala.annotation.unroll class Unrolled{ - def foo[T](s: T, @unroll n: Int = 1, b: Boolean = true) = s.toString + n + b + final def foo[T](s: T, @unroll n: Int = 1, b: Boolean = true) = s.toString + n + b } diff --git a/sbt-test/unroll-annot/genericMethod/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/genericMethod/v3/src/main/scala/Unrolled.scala index 29b1948cffbd..0c4909948a24 100644 --- a/sbt-test/unroll-annot/genericMethod/v3/src/main/scala/Unrolled.scala +++ b/sbt-test/unroll-annot/genericMethod/v3/src/main/scala/Unrolled.scala @@ -3,5 +3,5 @@ package unroll import scala.annotation.unroll class Unrolled{ - def foo[T](s: T, @unroll n: Int = 1, b: Boolean = true, @unroll l: Long = 0) = s.toString + n + b + l + final def foo[T](s: T, @unroll n: Int = 1, b: Boolean = true, @unroll l: Long = 0) = s.toString + n + b + l } diff --git a/sbt-test/unroll-annot/methodWithImplicit/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/methodWithImplicit/v2/src/main/scala/Unrolled.scala index fa98747d28d8..b2bf28bfb1db 100644 --- a/sbt-test/unroll-annot/methodWithImplicit/v2/src/main/scala/Unrolled.scala +++ b/sbt-test/unroll-annot/methodWithImplicit/v2/src/main/scala/Unrolled.scala @@ -3,5 +3,5 @@ package unroll import scala.annotation.unroll class Unrolled{ - def foo(s: String, @unroll n: Int = 1, b: Boolean = true)(implicit f: String => String) = f(s + n + b) + final def foo(s: String, @unroll n: Int = 1, b: Boolean = true)(implicit f: String => String) = f(s + n + b) } diff --git a/sbt-test/unroll-annot/methodWithImplicit/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/methodWithImplicit/v3/src/main/scala/Unrolled.scala index 58e88f581b4c..fee831cef37f 100644 --- a/sbt-test/unroll-annot/methodWithImplicit/v3/src/main/scala/Unrolled.scala +++ b/sbt-test/unroll-annot/methodWithImplicit/v3/src/main/scala/Unrolled.scala @@ -3,5 +3,5 @@ package unroll import scala.annotation.unroll class Unrolled{ - def foo(s: String, @unroll n: Int = 1, b: Boolean = true, @unroll l: Long = 0)(implicit f: String => String) = f(s + n + b + l) + final def foo(s: String, @unroll n: Int = 1, b: Boolean = true, @unroll l: Long = 0)(implicit f: String => String) = f(s + n + b + l) } diff --git a/sbt-test/unroll-annot/objectMethod/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/objectMethod/v2/src/main/scala/Unrolled.scala index 53d777884939..c72097c2288c 100644 --- a/sbt-test/unroll-annot/objectMethod/v2/src/main/scala/Unrolled.scala +++ b/sbt-test/unroll-annot/objectMethod/v2/src/main/scala/Unrolled.scala @@ -3,5 +3,5 @@ package unroll import scala.annotation.unroll object Unrolled{ - def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b + final def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b } diff --git a/sbt-test/unroll-annot/objectMethod/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/objectMethod/v3/src/main/scala/Unrolled.scala index 5e3464c184a4..3491b42e95ef 100644 --- a/sbt-test/unroll-annot/objectMethod/v3/src/main/scala/Unrolled.scala +++ b/sbt-test/unroll-annot/objectMethod/v3/src/main/scala/Unrolled.scala @@ -3,5 +3,5 @@ package unroll import scala.annotation.unroll object Unrolled{ - def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0) = s + n + b + l + final def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0) = s + n + b + l } diff --git a/sbt-test/unroll-annot/primaryConstructor/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/primaryConstructor/v2/src/main/scala/Unrolled.scala index dfe3c2f0e46d..40bf19a16901 100644 --- a/sbt-test/unroll-annot/primaryConstructor/v2/src/main/scala/Unrolled.scala +++ b/sbt-test/unroll-annot/primaryConstructor/v2/src/main/scala/Unrolled.scala @@ -3,5 +3,5 @@ package unroll import scala.annotation.unroll class Unrolled(s: String, n: Int = 1, @unroll b: Boolean = true){ - def foo = s + n + b + final def foo = s + n + b } diff --git a/sbt-test/unroll-annot/secondParameterList/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/secondParameterList/v2/src/main/scala/Unrolled.scala index 81d481deff38..73f635b24545 100644 --- a/sbt-test/unroll-annot/secondParameterList/v2/src/main/scala/Unrolled.scala +++ b/sbt-test/unroll-annot/secondParameterList/v2/src/main/scala/Unrolled.scala @@ -3,5 +3,5 @@ package unroll import scala.annotation.unroll class Unrolled{ - def foo(f: String => String)(s: String, @unroll n: Int = 1, b: Boolean = true) = f(s + n + b) + final def foo(f: String => String)(s: String, @unroll n: Int = 1, b: Boolean = true) = f(s + n + b) } diff --git a/sbt-test/unroll-annot/secondParameterList/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/secondParameterList/v3/src/main/scala/Unrolled.scala index 35803d5220a8..a903fbed4d93 100644 --- a/sbt-test/unroll-annot/secondParameterList/v3/src/main/scala/Unrolled.scala +++ b/sbt-test/unroll-annot/secondParameterList/v3/src/main/scala/Unrolled.scala @@ -3,5 +3,5 @@ package unroll import scala.annotation.unroll class Unrolled{ - def foo(f: String => String)(s: String, @unroll n: Int = 1, b: Boolean = true, @unroll l: Long = 0) = f(s + n + b + l) + final def foo(f: String => String)(s: String, @unroll n: Int = 1, b: Boolean = true, @unroll l: Long = 0) = f(s + n + b + l) } diff --git a/sbt-test/unroll-annot/traitMethod/v2/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/traitMethod/v2/src/main/scala/Unrolled.scala index 7d40ff17b846..d0bc2f595020 100644 --- a/sbt-test/unroll-annot/traitMethod/v2/src/main/scala/Unrolled.scala +++ b/sbt-test/unroll-annot/traitMethod/v2/src/main/scala/Unrolled.scala @@ -3,7 +3,7 @@ package unroll import scala.annotation.unroll trait Unrolled{ - def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b + final def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b } -object Unrolled extends Unrolled \ No newline at end of file +object Unrolled extends Unrolled diff --git a/sbt-test/unroll-annot/traitMethod/v3/src/main/scala/Unrolled.scala b/sbt-test/unroll-annot/traitMethod/v3/src/main/scala/Unrolled.scala index 085ecea52adf..4478d57a60f8 100644 --- a/sbt-test/unroll-annot/traitMethod/v3/src/main/scala/Unrolled.scala +++ b/sbt-test/unroll-annot/traitMethod/v3/src/main/scala/Unrolled.scala @@ -3,7 +3,7 @@ package unroll import scala.annotation.unroll trait Unrolled{ - def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0) = s + n + b + l + final def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0) = s + n + b + l } -object Unrolled extends Unrolled \ No newline at end of file +object Unrolled extends Unrolled diff --git a/tests/neg/unroll-abstractMethod.check b/tests/neg/unroll-abstractMethod.check new file mode 100644 index 000000000000..53f6be8b5092 --- /dev/null +++ b/tests/neg/unroll-abstractMethod.check @@ -0,0 +1,8 @@ +-- Error: tests/neg/unroll-abstractMethod.scala:6:6 -------------------------------------------------------------------- +6 | def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String // error + | ^ + | Unrolled method must be final and concrete +-- Error: tests/neg/unroll-abstractMethod.scala:10:6 ------------------------------------------------------------------- +10 | def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String // error + | ^ + | Unrolled method must be final and concrete diff --git a/tests/run/unroll-abstractMethod2/Unrolled_1.scala b/tests/neg/unroll-abstractMethod.scala similarity index 53% rename from tests/run/unroll-abstractMethod2/Unrolled_1.scala rename to tests/neg/unroll-abstractMethod.scala index a1f10681f75c..8f30fb63b20f 100644 --- a/tests/run/unroll-abstractMethod2/Unrolled_1.scala +++ b/tests/neg/unroll-abstractMethod.scala @@ -1,12 +1,11 @@ //> using options -experimental + import scala.annotation.unroll trait Unrolled { - def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String + def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String // error } -object UnrolledImpl { - val impl: Unrolled = new Unrolled { - def foo(s: String, n: Int, @unroll b: Boolean) = s + n + b - } +abstract class UnrolledBase { + def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String // error } diff --git a/tests/neg/unroll-clash.check b/tests/neg/unroll-clash.check index 9483e4611e7c..709575b41c03 100644 --- a/tests/neg/unroll-clash.check +++ b/tests/neg/unroll-clash.check @@ -3,4 +3,4 @@ | ^ | Unrolled method foo clashes with existing declaration. | Please remove the clashing definition, or the @unroll annotation. - | Unrolled from def foo(x: Int, y: Int): Int in class Foo at line 6 + | Unrolled from final def foo(x: Int, y: Int): Int in class Foo at line 6 diff --git a/tests/neg/unroll-clash.scala b/tests/neg/unroll-clash.scala index b6d146f75d76..0ed60483afef 100644 --- a/tests/neg/unroll-clash.scala +++ b/tests/neg/unroll-clash.scala @@ -3,7 +3,7 @@ import scala.annotation.unroll class Foo { - def foo(x: Int, @unroll y: Int = 0) = x + y + final def foo(x: Int, @unroll y: Int = 0) = x + y def foo(x: Int) = { // error println("Not binary compatible!") -1 diff --git a/tests/neg/unroll-no-default.check b/tests/neg/unroll-no-default.check index a077e7558271..d16a94ce0527 100644 --- a/tests/neg/unroll-no-default.check +++ b/tests/neg/unroll-no-default.check @@ -1,4 +1,4 @@ --- Error: tests/neg/unroll-no-default.scala:6:26 ----------------------------------------------------------------------- -6 | def foo(x: Int, @unroll y: Int) = x + y // error - | ^^^^^^^^^^^^^^ - | Cannot unroll method foo in class Foo at line 6 because parameter y needs a default value +-- Error: tests/neg/unroll-no-default.scala:6:32 ----------------------------------------------------------------------- +6 | final def foo(x: Int, @unroll y: Int) = x + y // error + | ^^^^^^^^^^^^^^ + | Cannot unroll method foo in class Foo at line 6 because parameter y needs a default value diff --git a/tests/neg/unroll-no-default.scala b/tests/neg/unroll-no-default.scala index dfb8ea70d931..1058f34087e3 100644 --- a/tests/neg/unroll-no-default.scala +++ b/tests/neg/unroll-no-default.scala @@ -3,5 +3,5 @@ import scala.annotation.unroll class Foo { - def foo(x: Int, @unroll y: Int) = x + y // error + final def foo(x: Int, @unroll y: Int) = x + y // error } diff --git a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala index ab24d4e2ea5f..0443fd8e1c28 100644 --- a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala @@ -96,7 +96,7 @@ val experimentalDefinitionInLibrary = Set( "scala.Predef$.runtimeChecked", "scala.annotation.internal.RuntimeChecked", // New feature: SIP 61 - @unroll annotation - "scala.annotation.unroll", "scala.annotation.internal.UnrollForwarder", "scala.annotation.internal.AbstractUnroll", + "scala.annotation.unroll", "scala.annotation.internal.UnrollForwarder" ) diff --git a/tests/run/unroll-abstractMethod.scala b/tests/run/unroll-abstractMethod.scala deleted file mode 100644 index f767530b895d..000000000000 --- a/tests/run/unroll-abstractMethod.scala +++ /dev/null @@ -1,28 +0,0 @@ -//> using options -experimental - -import scala.annotation.unroll - -trait Unrolled { - def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String -} - -abstract class UnrolledBase { - def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String -} - -class UnrolledCls extends Unrolled { - /** infers `override` even though Unrolled.foo is actually implemented. */ - def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b -} - -object UnrolledObject extends UnrolledBase { - /** infers `override` even though Unrolled.foo is actually implemented. */ - def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b -} - -@main def Test: Unit = { - // val cls = new UnrolledCls - // assert(cls.foo("foo") == "foo1true") - // assert(UnrolledObject.foo("foo") == "foo1true") - () -} diff --git a/tests/run/unroll-abstractMethod2/Test_2.scala b/tests/run/unroll-traitMethod/Test_2.scala similarity index 100% rename from tests/run/unroll-abstractMethod2/Test_2.scala rename to tests/run/unroll-traitMethod/Test_2.scala diff --git a/tests/run/unroll-traitMethod/Unrolled_1.scala b/tests/run/unroll-traitMethod/Unrolled_1.scala new file mode 100644 index 000000000000..fa4c78708f4b --- /dev/null +++ b/tests/run/unroll-traitMethod/Unrolled_1.scala @@ -0,0 +1,10 @@ +//> using options -experimental +import scala.annotation.unroll + +trait Unrolled { + final def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String = s + n + b +} + +object UnrolledImpl { + val impl: Unrolled = new Unrolled {} +} From ea080c531768d0458ee71ac429d89df06c8059d2 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 3 Oct 2024 13:20:06 +0200 Subject: [PATCH 16/35] include constructor in unroll clash, loosen final check for objects --- .../tools/dotc/transform/PostTyper.scala | 2 +- .../dotc/transform/UnrollDefinitions.scala | 19 +++++++++++-------- tests/neg/unroll-clash.check | 6 ++++++ tests/neg/unroll-clash.scala | 7 +++++++ tests/run/unroll-inferredFinal.scala | 17 +++++++++++++++++ 5 files changed, 42 insertions(+), 9 deletions(-) create mode 100644 tests/run/unroll-inferredFinal.scala diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 2f980cf4f0da..ed3920e49fd2 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -137,7 +137,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => if method.is(Deferred) then report.error("Unrolled method must be final and concrete", method.srcPos) res = false - if !method.isConstructor && !method.is(Final) then + if !(method.isConstructor || method.is(Final) || method.owner.is(ModuleClass)) then report.error("Unrolled method must be final", method.srcPos) res = false res diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala index a3153ea40ed8..6ca7467336d1 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala @@ -25,6 +25,8 @@ import dotty.tools.unreachable /**Implementation of SIP-61. * Runs when `@unroll` annotations are found in a compilation unit, installing new definitions + * + * Note that it only generates `Invisible` methods, so no interactions with Zinc/SemanticDB */ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { self => @@ -63,17 +65,18 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { def computeIndices(annotated: Symbol)(using Context): ComputedIndicies = unrolledDefs.getOrElseUpdate(annotated, { - annotated + val indices = annotated .paramSymss .zipWithIndex - .flatMap { (paramClause, paramClauseIndex) => + .flatMap: (paramClause, paramClauseIndex) => val annotationIndices = findUnrollAnnotations(paramClause) if (annotationIndices.isEmpty) None - else - require(annotated.is(Final, butNot = Deferred) || annotated.isConstructor, - i"${annotated} is not final&concrete, or a constructor") - Some((paramClauseIndex, annotationIndices)) - } + else Some((paramClauseIndex, annotationIndices)) + if indices.nonEmpty then + // pre-validation should have occurred in posttyper + assert(annotated.is(Final, butNot = Deferred) || annotated.isConstructor || annotated.owner.is(ModuleClass), + i"$annotated is not final&concrete, or a constructor") + indices }) end computeIndices @@ -320,7 +323,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { decl.matches(other) && !staticNonStaticPair if allGenerated.nonEmpty then - val byName = otherDecls.groupMap(_.symbol.name.toString)(_.symbol) + val byName = (tmpl.constr :: otherDecls).groupMap(_.symbol.name.toString)(_.symbol) for (src, _, dcls) <- allGenerated dcl <- dcls diff --git a/tests/neg/unroll-clash.check b/tests/neg/unroll-clash.check index 709575b41c03..a398c550c461 100644 --- a/tests/neg/unroll-clash.check +++ b/tests/neg/unroll-clash.check @@ -4,3 +4,9 @@ | Unrolled method foo clashes with existing declaration. | Please remove the clashing definition, or the @unroll annotation. | Unrolled from final def foo(x: Int, y: Int): Int in class Foo at line 6 +-- Error: tests/neg/unroll-clash.scala:13:20 --------------------------------------------------------------------------- +13 |class UnrolledClass2(s: String) { // error + | ^ + | Unrolled constructor UnrolledClass2 clashes with existing declaration. + | Please remove the clashing definition, or the @unroll annotation. + | Unrolled from def (s: String, y: Boolean): UnrolledClass2 in class UnrolledClass2 at line 15 diff --git a/tests/neg/unroll-clash.scala b/tests/neg/unroll-clash.scala index 0ed60483afef..79a75c2ba785 100644 --- a/tests/neg/unroll-clash.scala +++ b/tests/neg/unroll-clash.scala @@ -9,3 +9,10 @@ class Foo { -1 } } + +class UnrolledClass2(s: String) { // error + + def this(s: String, @unroll y: Boolean = true) = this(s + y) + + override def toString = s"UnrolledClass2($s)" +} diff --git a/tests/run/unroll-inferredFinal.scala b/tests/run/unroll-inferredFinal.scala new file mode 100644 index 000000000000..b4e1ccd9f011 --- /dev/null +++ b/tests/run/unroll-inferredFinal.scala @@ -0,0 +1,17 @@ +//> using options -experimental + +import scala.annotation.unroll + +object UnrolledObj { + // final is not needed because objects can't be extended + def foo(s: String, @unroll y: Boolean = true): String = s + y +} + +// final inferred for constructor +class UnrolledClass(s: String, @unroll y: Boolean = true): + override def toString = s"UnrolledClass($s,$y)" + + +@main def Test: Unit = + assert(UnrolledObj.foo("foo") == "footrue") + assert(new UnrolledClass("foo").toString == "UnrolledClass(foo,true)") From 44b1b6f79db75704a62ce258fecb76ff7f522837 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 3 Oct 2024 17:19:11 +0200 Subject: [PATCH 17/35] fix order of printing in test --- tests/run/unroll-caseclass.check | 2 +- tests/run/unroll-caseclass/unrolledV3_3.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/run/unroll-caseclass.check b/tests/run/unroll-caseclass.check index 3d0ebd182b9f..ad59ed9b4dde 100644 --- a/tests/run/unroll-caseclass.check +++ b/tests/run/unroll-caseclass.check @@ -20,9 +20,9 @@ Assertion passed: found "hello31337false" + "0" === Unrolled Test V3 === Assertion passed: found "hello31337false12345" as expected, no constructor for Unrolled(s: String) -public example.Unrolled(java.lang.String,int,boolean,long) public example.Unrolled(java.lang.String,int) public example.Unrolled(java.lang.String,int,boolean) +public example.Unrolled(java.lang.String,int,boolean,long) Assertion passed: found "cow1true0" Assertion passed: found "cow2true0" Assertion passed: found "cow2false0" diff --git a/tests/run/unroll-caseclass/unrolledV3_3.scala b/tests/run/unroll-caseclass/unrolledV3_3.scala index 5892e416ce5c..24f8b59ac78e 100644 --- a/tests/run/unroll-caseclass/unrolledV3_3.scala +++ b/tests/run/unroll-caseclass/unrolledV3_3.scala @@ -91,6 +91,6 @@ object UnrollTestPlatformSpecificV3 extends TestUtil { "hello2false3" ) - cls.getConstructors.foreach(println) + cls.getConstructors.sortBy(_.getParameterCount()).foreach(println) } } From 606dbf24ae543f86749116a7e43aebf1a0088807 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 3 Oct 2024 18:17:42 +0200 Subject: [PATCH 18/35] check for illegal uses of @unroll --- .../tools/dotc/typer/CrossVersionChecks.scala | 27 +++++++++++++- tests/neg/unroll-illegal.check | 36 +++++++++++++++++++ tests/neg/unroll-illegal.scala | 29 +++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 tests/neg/unroll-illegal.check create mode 100644 tests/neg/unroll-illegal.scala diff --git a/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala b/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala index 6020431672b9..03990f0e54b2 100644 --- a/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala @@ -81,18 +81,37 @@ class CrossVersionChecks extends MiniPhase: report.deprecationWarning(em"inheritance from $psym is deprecated$since$msg", parent.srcPos, origin=psym.showFullName) } + private def unrollError(pos: SrcPos)(using Context): Unit = + report.error("@unroll is only allowed on a method parameter", pos) + + private def checkUnrollAnnot(annotSym: Symbol, pos: SrcPos)(using Context): Unit = + if annotSym == defn.UnrollAnnot then + unrollError(pos) + + private def checkUnrollMemberDef(memberDef: MemberDef)(using Context): Unit = + val sym = memberDef.symbol + if + sym.hasAnnotation(defn.UnrollAnnot) + && !(sym.isTerm && sym.is(Param)) + then + val normSym = if sym.is(ModuleVal) then sym.moduleClass else sym + unrollError(normSym.srcPos) + override def transformValDef(tree: ValDef)(using Context): ValDef = + checkUnrollMemberDef(tree) checkDeprecatedOvers(tree) checkExperimentalAnnots(tree.symbol) tree override def transformDefDef(tree: DefDef)(using Context): DefDef = + checkUnrollMemberDef(tree) checkDeprecatedOvers(tree) checkExperimentalAnnots(tree.symbol) tree override def transformTypeDef(tree: TypeDef)(using Context): TypeDef = // TODO do we need to check checkDeprecatedOvers(tree)? + checkUnrollMemberDef(tree) checkExperimentalAnnots(tree.symbol) tree @@ -126,6 +145,8 @@ class CrossVersionChecks extends MiniPhase: if tree.span.isSourceDerived then checkDeprecatedRef(sym, tree.srcPos) checkExperimentalRef(sym, tree.srcPos) + case AnnotatedType(_, annot) => + checkUnrollAnnot(annot.symbol, tree.srcPos) case _ => } tree @@ -140,7 +161,11 @@ class CrossVersionChecks extends MiniPhase: case tree: TypeTree => transformTypeTree(tree) case _ => } - tree + tree match + case Annotated(_, annot) => + checkUnrollAnnot(annot.tpe.typeSymbol, tree.srcPos) + tree + case tree => tree end CrossVersionChecks diff --git a/tests/neg/unroll-illegal.check b/tests/neg/unroll-illegal.check new file mode 100644 index 000000000000..c1f57847e3a6 --- /dev/null +++ b/tests/neg/unroll-illegal.check @@ -0,0 +1,36 @@ +-- Error: tests/neg/unroll-illegal.scala:6:6 --------------------------------------------------------------------------- +6 |class UnrollClass // error + | ^ + | @unroll is only allowed on a method parameter +-- Error: tests/neg/unroll-illegal.scala:9:6 --------------------------------------------------------------------------- +9 |trait UnrollTrait // error + | ^ + | @unroll is only allowed on a method parameter +-- Error: tests/neg/unroll-illegal.scala:12:7 -------------------------------------------------------------------------- +12 |object UnrollObject // error + | ^ + | @unroll is only allowed on a method parameter +-- Error: tests/neg/unroll-illegal.scala:18:5 -------------------------------------------------------------------------- +18 |enum UnrollEnum { case X } // error + | ^ + | @unroll is only allowed on a method parameter +-- Error: tests/neg/unroll-illegal.scala:21:25 ------------------------------------------------------------------------- +21 | val annotExpr: Int = 23: @unroll // error + | ^ + | @unroll is only allowed on a method parameter +-- Error: tests/neg/unroll-illegal.scala:22:19 ------------------------------------------------------------------------- +22 | type annotType = Int @unroll // error + | ^^^^^^^^^^^ + | @unroll is only allowed on a method parameter +-- Error: tests/neg/unroll-illegal.scala:25:6 -------------------------------------------------------------------------- +25 | val unrollVal: Int = 23 // error + | ^ + | @unroll is only allowed on a method parameter +-- Error: tests/neg/unroll-illegal.scala:28:6 -------------------------------------------------------------------------- +28 | def unrollDef: Int = 23 // error + | ^ + | @unroll is only allowed on a method parameter +-- Error: tests/neg/unroll-illegal.scala:15:5 -------------------------------------------------------------------------- +15 |type UnrollType = Int // error + | ^ + | @unroll is only allowed on a method parameter diff --git a/tests/neg/unroll-illegal.scala b/tests/neg/unroll-illegal.scala new file mode 100644 index 000000000000..0b40a1c33c03 --- /dev/null +++ b/tests/neg/unroll-illegal.scala @@ -0,0 +1,29 @@ +//> using options -experimental + +import scala.annotation.unroll + +@unroll +class UnrollClass // error + +@unroll +trait UnrollTrait // error + +@unroll +object UnrollObject // error + +@unroll +type UnrollType = Int // error + +@unroll +enum UnrollEnum { case X } // error + +object wrap { + val annotExpr: Int = 23: @unroll // error + type annotType = Int @unroll // error + + @unroll + val unrollVal: Int = 23 // error + + @unroll + def unrollDef: Int = 23 // error +} From dbac4ffd4043a20ef6e281fcca86b154caeca337 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 3 Oct 2024 20:13:44 +0200 Subject: [PATCH 19/35] error when unrolling trait constructors Traits are forbidden in source code from having secondary constructors, which is what the current transform would generate. Trait parameters are encoded in concrete implementing classes as getter methods, perhaps unroll could provide default implementations, but this is unexplored. --- compiler/src/dotty/tools/dotc/transform/PostTyper.scala | 6 +++++- tests/neg/unroll-traitConstructor.check | 4 ++++ tests/neg/unroll-traitConstructor.scala | 8 ++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/neg/unroll-traitConstructor.check create mode 100644 tests/neg/unroll-traitConstructor.scala diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index ed3920e49fd2..a182ef08072f 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -137,7 +137,11 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => if method.is(Deferred) then report.error("Unrolled method must be final and concrete", method.srcPos) res = false - if !(method.isConstructor || method.is(Final) || method.owner.is(ModuleClass)) then + val isCtor = method.isConstructor + if isCtor && method.owner.is(Trait) then + report.error("implementation restriction: Unrolled method cannot be a trait constructor", method.srcPos) + res = false + if !(isCtor || method.is(Final) || method.owner.is(ModuleClass)) then report.error("Unrolled method must be final", method.srcPos) res = false res diff --git a/tests/neg/unroll-traitConstructor.check b/tests/neg/unroll-traitConstructor.check new file mode 100644 index 000000000000..74e3eff5999b --- /dev/null +++ b/tests/neg/unroll-traitConstructor.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/unroll-traitConstructor.scala:5:12 ----------------------------------------------------------------- +5 |trait Unroll(a: String, @unroll b: Boolean = true): // error + | ^ + | implementation restriction: Unrolled method cannot be a trait constructor diff --git a/tests/neg/unroll-traitConstructor.scala b/tests/neg/unroll-traitConstructor.scala new file mode 100644 index 000000000000..3c48852d8303 --- /dev/null +++ b/tests/neg/unroll-traitConstructor.scala @@ -0,0 +1,8 @@ +//> using options -experimental + +import scala.annotation.unroll + +trait Unroll(a: String, @unroll b: Boolean = true): // error + def show: String = a + b + +class Bar(arg: String, bool: Boolean) extends Unroll(arg, bool) From ce4f9b4ea7c4e2ff1d9be36d139578de7fa6d939 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 3 Oct 2024 21:08:46 +0200 Subject: [PATCH 20/35] better error when multiple parameter lists with unroll --- .../tools/dotc/transform/PostTyper.scala | 28 +++++++++------- .../dotc/transform/UnrollDefinitions.scala | 33 +++++++++++-------- tests/neg/unroll-multipleParams.check | 4 +++ tests/neg/unroll-multipleParams.scala | 7 ++++ 4 files changed, 46 insertions(+), 26 deletions(-) create mode 100644 tests/neg/unroll-multipleParams.check create mode 100644 tests/neg/unroll-multipleParams.scala diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index a182ef08072f..f40b749604d9 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -21,6 +21,7 @@ import reporting.* import NameKinds.WildcardParamName import cc.* import dotty.tools.dotc.transform.MacroAnnotations.hasMacroAnnotation +import dotty.tools.dotc.core.NameKinds.DefaultGetterName object PostTyper { val name: String = "posttyper" @@ -133,18 +134,21 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => else local seenMethods.getOrElseUpdate(method, { - var res = true - if method.is(Deferred) then - report.error("Unrolled method must be final and concrete", method.srcPos) - res = false - val isCtor = method.isConstructor - if isCtor && method.owner.is(Trait) then - report.error("implementation restriction: Unrolled method cannot be a trait constructor", method.srcPos) - res = false - if !(isCtor || method.is(Final) || method.owner.is(ModuleClass)) then - report.error("Unrolled method must be final", method.srcPos) - res = false - res + if method.name.is(DefaultGetterName) then + false // not an error, but not an expandable unrolled method + else + var res = true + if method.is(Deferred) then + report.error("Unrolled method must be final and concrete", method.srcPos) + res = false + val isCtor = method.isConstructor + if isCtor && method.owner.is(Trait) then + report.error("implementation restriction: Unrolled method cannot be a trait constructor", method.srcPos) + res = false + if !(isCtor || method.is(Final) || method.owner.is(ModuleClass)) then + report.error(s"Unrolled method ${method} must be final", method.srcPos) + res = false + res }) def withNoCheckNews[T](ts: List[New])(op: => T): T = { diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala index 6ca7467336d1..0b817d492263 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala @@ -65,18 +65,21 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { def computeIndices(annotated: Symbol)(using Context): ComputedIndicies = unrolledDefs.getOrElseUpdate(annotated, { - val indices = annotated - .paramSymss - .zipWithIndex - .flatMap: (paramClause, paramClauseIndex) => - val annotationIndices = findUnrollAnnotations(paramClause) - if (annotationIndices.isEmpty) None - else Some((paramClauseIndex, annotationIndices)) - if indices.nonEmpty then - // pre-validation should have occurred in posttyper - assert(annotated.is(Final, butNot = Deferred) || annotated.isConstructor || annotated.owner.is(ModuleClass), - i"$annotated is not final&concrete, or a constructor") - indices + if annotated.name.is(DefaultGetterName) then + Nil // happens in curried methods where more than one parameter list has @unroll + else + val indices = annotated + .paramSymss + .zipWithIndex + .flatMap: (paramClause, paramClauseIndex) => + val annotationIndices = findUnrollAnnotations(paramClause) + if (annotationIndices.isEmpty) None + else Some((paramClauseIndex, annotationIndices)) + if indices.nonEmpty then + // pre-validation should have occurred in posttyper + assert(annotated.is(Final, butNot = Deferred) || annotated.isConstructor || annotated.owner.is(ModuleClass) || annotated.name.is(DefaultGetterName), + i"$annotated is not final&concrete, or a constructor") + indices }) end computeIndices @@ -103,7 +106,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { params .zipWithIndex .collect { - case (v, i) if v.annotations.exists(_.symbol.fullName.toString == "scala.annotation.unroll") => + case (v, i) if v.hasAnnotation(defn.UnrollAnnot) => i } } @@ -303,7 +306,9 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { case _ => unreachable("sliding with at least 2 elements") Some((defdef.symbol, None, generatedDefs)) - case multiple => sys.error("Cannot have multiple parameter lists containing `@unroll` annotation") + case multiple => + report.error("Cannot have multiple parameter lists containing `@unroll` annotation", defdef.srcPos) + None } case _ => None diff --git a/tests/neg/unroll-multipleParams.check b/tests/neg/unroll-multipleParams.check new file mode 100644 index 000000000000..f8f1bc22510c --- /dev/null +++ b/tests/neg/unroll-multipleParams.check @@ -0,0 +1,4 @@ +-- Error: tests/neg/unroll-multipleParams.scala:6:12 ------------------------------------------------------------------- +6 | final def foo(x: Int, @unroll y: Int = 0)(@unroll z: Int = 0) = x + y + z // error + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | Cannot have multiple parameter lists containing `@unroll` annotation diff --git a/tests/neg/unroll-multipleParams.scala b/tests/neg/unroll-multipleParams.scala new file mode 100644 index 000000000000..80371fec74c4 --- /dev/null +++ b/tests/neg/unroll-multipleParams.scala @@ -0,0 +1,7 @@ +//> using options -experimental + +import scala.annotation.unroll + +class Foo { + final def foo(x: Int, @unroll y: Int = 0)(@unroll z: Int = 0) = x + y + z // error +} From b3cd7293efb454847b5032fb500f18a8917395b4 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Sat, 5 Oct 2024 03:23:35 +0200 Subject: [PATCH 21/35] SIP 61 - add documentation, and regression test for local defs --- .../tools/dotc/transform/PostTyper.scala | 2 +- .../reference/experimental/unrolled-defs.md | 156 ++++++++++++++++++ docs/sidebar.yml | 1 + tests/neg/unroll-illegal2.check | 4 + tests/neg/unroll-illegal2.scala | 9 + tests/neg/unroll-illegal3.check | 12 ++ tests/neg/unroll-illegal3.scala | 17 ++ 7 files changed, 200 insertions(+), 1 deletion(-) create mode 100644 docs/_docs/reference/experimental/unrolled-defs.md create mode 100644 tests/neg/unroll-illegal2.check create mode 100644 tests/neg/unroll-illegal2.scala create mode 100644 tests/neg/unroll-illegal3.check create mode 100644 tests/neg/unroll-illegal3.scala diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index f40b749604d9..bdfe0f114a59 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -146,7 +146,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => report.error("implementation restriction: Unrolled method cannot be a trait constructor", method.srcPos) res = false if !(isCtor || method.is(Final) || method.owner.is(ModuleClass)) then - report.error(s"Unrolled method ${method} must be final", method.srcPos) + report.error(s"Unrolled method ${method.name} must be final", method.srcPos) res = false res }) diff --git a/docs/_docs/reference/experimental/unrolled-defs.md b/docs/_docs/reference/experimental/unrolled-defs.md new file mode 100644 index 000000000000..f2e09e82bc18 --- /dev/null +++ b/docs/_docs/reference/experimental/unrolled-defs.md @@ -0,0 +1,156 @@ +--- +layout: doc-page +title: "Automatic Parameter Unrolling" +nightlyOf: https://docs.scala-lang.org/scala3/reference/experimental/unrolled-defs.html +--- + +Parameter unrolling enables new parameters to be added to methods and classes, +while still preserving backwards binary compatibility. An `@unroll` annotation, on a parameter with default value, will generate backwards compatible forwarders to a method or constructor. + +## Example +```scala +// V1 +final def foo( + s: String, + i: Int +): String = s + i +``` + +In the example above, assume version `V1` of a library defines the method `foo` with two parameters: `s` and `i`. +Assume a client library or application `C1` compiles against `V1` of `foo`. + +```scala +// V2 +final def foo( + s: String, + i: Int, + @unroll b: Boolean = true, + l: Long = 0L +): String = s + i + b + l + +// Generated automatically +`` final def foo( + s: String, + i: Int +) = foo(s, i, true, 0L) +``` + +In version `V2`, the library adds the `b` and `l` parameters to `foo`, along with default values. +To preserve compatibility with `V1`, `b` is annotated with `@unroll`, generating a forwarder with only the parameters that come before, i.e. it has the same signature as `foo` in `V1`. + +A client `C2` compiling against `V2` will only see `foo` with four parameters in the public API. +The generated forwarder is hidden from those clients. +However, `C1` remains compatible with `V2` of the library, and does not need to be recompiled. +At runtime, it will continue to link against the signature of the old `foo` method, and call the generated forwarder which is accessible in the binary API. + +## Specification + +### `@unroll` annotation + +The `scala.annotation.unroll` annotation can be applied to any term parameter of an effectively-final method: +- `def` in an `object` (i.e. `final` may be omitted) +- `final def` in a `class` or `trait` +- `class` parameters (i.e. primary constructors) +- `def this` in a `class` (i.e. secondary constructors) + +### Restrictions + +It is illegal for `@unroll` to be applied to any other definition (including `trait` parameters and local methods), or to annotate a type. + +`@unroll` may be applied to more than one parameter per method, but all occurrences must appear in the same parameter clause. + +The annotated parameter, and any parameters to the right in the same parameter clause, must have a default value. + +It is a compile-time error if any generated forwarder matches the signature of another declaration in the same class. + +## Code generation + +Expansion of `@unroll` parameters is performed before TASTy generation, so generated code will appear in TASTy. + +Below specifies the transformations that occur: + +For each method `m` of a template, there is a target method `t` which is checked for `@unroll`: +- for `fromProduct`, `copy`, and `apply` of the companion of case class `C`, then `t` is the primary constructor of `C`. +- otherwise `m` is `t`. + +if `t` has a single parameter list with `@unroll` annotations, then `m` is subject to code generation. There are two +possible transformations: +1. Forwarder generation +2. Reimplementation: for `fromProduct` of a case class companion + +### (1) Forwarder generation + +In a method `foo` with unrolled parameters in parameter list `i`: +each parameter `p` with an `@unroll` annotation causes the generation of exactly one forwarder method `f_p`. + +for a given method with generic signature + +```scala +final def foo[T](ps0...)(psX..., @unroll p, psY...)(psN...): T = + ... +``` +then `f_p` will take the form + +```scala +`` final def foo[T](ps0...)(psX...)(psN...): T = + foo(ps0...)(psX..., p_D, psY_D...)(psN...) +``` + +i.e. result type is preserved, parameter lists before and after `i` are unchanged, and within `i`: +- the parameters `psX...` to the left of `p` are preserved, +- the parameters `p` and `psY...` are dropped. + +In the body of `f_p`, parameters are passed positionally to the original `foo`, except for the dropped parameters, which are replaced by default arguments for those parameters (`p_D` for `p`, and `psY_D...` for `psY...`). + +Forwarders are generated after type checking, before pickling, and with the `Invisible` flag. +This means that while present in TASTy, they can not be resolved from other top-level classes. + +Forwarder method parameters do not have default values, and are never annotated with `@unroll`. + +### (2) Method reimplementation + +To preserve semantic compatibility of `fromProduct`, its body is replaced with a pattern match over the `productArity` of the parameter. +For each forwarder generated for the case class primary constructor, an equivalent case is generated in the pattern match. + +e.g. for a forwarder +```scala +`` def this(ps...) = this(ps..., ds...) +``` +then the following case is generated: +```scala +case n => new C(...p.productElement(n - 1), ds...) +``` +where `n` is an integer matching the number of parameters in `ps`. + +The pattern match will have a default wildcard case, which has the same body as the original `fromProduct` method. + +In all the complete transformation: + +```scala +case class C(ps0...) // ps0 has z parameters + +object C: + def fromProduct(p: Product): C = + p.productArity match + case ... => ... + case n => new C(...p.productElement(n - 1), ds...) + case _ => new C(...p.productElement(z - 1)) +``` + + +## Background Motivation + +The Scala language library ecosystem is based upon compatability of API's represented via both the TASTy format (TASTy compatibility), and the Java class file format (binary compatibility). + +Adding a parameter to a method or constructor is a binary backwards incompatible change: +clients compiled against the previous version will expect the old signature to exist, and cause a `LinkageError` to be thrown at runtime. +The correct solution to this problem, to preserve compatibility, is to duplicate the method before adding the new parameter. + +In practice, Scala users developed various techniques and disciplines for mitigating this problem when evolving APIs. +Either by forbidding certain features, such as case classes, or various code generation frameworks. Here are some well-known examples: + +1. [data-class](https://index.scala-lang.org/alexarchambault/data-class) +2. [SBT Contraband](https://www.scala-sbt.org/contraband/) +3. [Structural Data Structures](https://github.com/scala/docs.scala-lang/pull/2662) + +The `@unroll` annotation was proposed as an alternative to these disciplines that not not require learning a new meta-language on top of Scala. The standard data modelling techniques of `def`, `case class`, `enum`, `class` and `trait` are preserved, and the mistake-prone boilerplate is automated. diff --git a/docs/sidebar.yml b/docs/sidebar.yml index a306d8bdf274..8cae4e95725a 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -163,6 +163,7 @@ subsection: - page: reference/experimental/typeclasses.md - page: reference/experimental/runtimeChecked.md - page: reference/experimental/better-fors.md + - page: reference/experimental/unrolled-defs.md - page: reference/syntax.md - title: Language Versions index: reference/language-versions/language-versions.md diff --git a/tests/neg/unroll-illegal2.check b/tests/neg/unroll-illegal2.check new file mode 100644 index 000000000000..e2ead2ee3fe7 --- /dev/null +++ b/tests/neg/unroll-illegal2.check @@ -0,0 +1,4 @@ +-- [E200] Syntax Error: tests/neg/unroll-illegal2.scala:7:10 ----------------------------------------------------------- +7 | final def foo(s: String, @unroll y: Boolean) = s + y // error + | ^^^ + | The final modifier is not allowed on local definitions diff --git a/tests/neg/unroll-illegal2.scala b/tests/neg/unroll-illegal2.scala new file mode 100644 index 000000000000..ad7284506bbf --- /dev/null +++ b/tests/neg/unroll-illegal2.scala @@ -0,0 +1,9 @@ +//> using options -experimental + +import scala.annotation.unroll + +class wrap { + locally { + final def foo(s: String, @unroll y: Boolean) = s + y // error + } +} diff --git a/tests/neg/unroll-illegal3.check b/tests/neg/unroll-illegal3.check new file mode 100644 index 000000000000..20917e857ee7 --- /dev/null +++ b/tests/neg/unroll-illegal3.check @@ -0,0 +1,12 @@ +-- Error: tests/neg/unroll-illegal3.scala:7:8 -------------------------------------------------------------------------- +7 | def foo(s: String, @unroll y: Boolean) = s + y // error + | ^ + | Unrolled method foo must be final +-- Error: tests/neg/unroll-illegal3.scala:12:6 ------------------------------------------------------------------------- +12 | def foo(s: String, @unroll y: Boolean) = s + y // error + | ^ + | Unrolled method foo must be final +-- Error: tests/neg/unroll-illegal3.scala:16:6 ------------------------------------------------------------------------- +16 | def foo(s: String, @unroll y: Boolean): String // error + | ^ + | Unrolled method must be final and concrete diff --git a/tests/neg/unroll-illegal3.scala b/tests/neg/unroll-illegal3.scala new file mode 100644 index 000000000000..22a53bd04de6 --- /dev/null +++ b/tests/neg/unroll-illegal3.scala @@ -0,0 +1,17 @@ +//> using options -experimental + +import scala.annotation.unroll + +object wrap { + locally { + def foo(s: String, @unroll y: Boolean) = s + y // error + } +} + +class UnrolledCls { + def foo(s: String, @unroll y: Boolean) = s + y // error +} + +trait UnrolledTrait { + def foo(s: String, @unroll y: Boolean): String // error +} From d14448bcbab55c8ac789c444a3ee618aa18f0eda Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Sat, 5 Oct 2024 21:53:20 +0200 Subject: [PATCH 22/35] SIP 61 - remove tasty hack by not "telescoping" forwarders Now, all forwarders directly call the unrolled method. This means that there is no need to resolve an invisible method, so the "hack" using TERMREFdirect is no longer needed. This increases bytecode size but reduces stack depth. Also removed scala.annotation.internal.UnrolledForwarder, as it is only needed for the TASTy hack. --- .../dotty/tools/dotc/core/Definitions.scala | 1 - .../tools/dotc/core/tasty/TreePickler.scala | 3 - .../tools/dotc/core/tasty/TreeUnpickler.scala | 7 +- .../dotc/transform/UnrollDefinitions.scala | 18 ++--- .../annotation/internal/UnrollForwarder.scala | 9 --- .../stdlibExperimentalDefinitions.scala | 2 +- tests/run/unroll-multiple.scala | 72 +++++++++++++++++++ 7 files changed, 81 insertions(+), 31 deletions(-) delete mode 100644 library/src/scala/annotation/internal/UnrollForwarder.scala create mode 100644 tests/run/unroll-multiple.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 5b3b2fc47050..4a136b4cea95 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -1037,7 +1037,6 @@ class Definitions { @tu lazy val NowarnAnnot: ClassSymbol = requiredClass("scala.annotation.nowarn") @tu lazy val UnusedAnnot: ClassSymbol = requiredClass("scala.annotation.unused") @tu lazy val UnrollAnnot: ClassSymbol = requiredClass("scala.annotation.unroll") - @tu lazy val UnrollForwarderAnnot: ClassSymbol = requiredClass("scala.annotation.internal.UnrollForwarder") @tu lazy val TransparentTraitAnnot: ClassSymbol = requiredClass("scala.annotation.transparentTrait") @tu lazy val NativeAnnot: ClassSymbol = requiredClass("scala.native") @tu lazy val RepeatedAnnot: ClassSymbol = requiredClass("scala.annotation.internal.Repeated") diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index d222f828ba9b..6822fcc8e548 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -490,9 +490,6 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) { writeByte(if name.isTypeName then SELECTtpt else SELECT) pickleNameAndSig(name, sig, ename) pickleTree(qual) - else if sym.is(Invisible) && qual.isInstanceOf[This] && sym.hasAnnotation(defn.UnrollForwarderAnnot) then - writeByte(TERMREFdirect) - pickleSymRef(sym) // SIP-61 HACK: resolution from Signature filters out Invisible symbols else // select from owner writeByte(SELECTin) withLength { diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 1cd82a27d7b9..d9ae4ddb6006 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1260,12 +1260,7 @@ class TreeUnpickler(reader: TastyReader, goto(start) readType() match { case path: TypeRef => TypeTree(path) - case path: TermRef => - val sym = path.symbol - if sym.is(Invisible) && sym.hasAnnotation(defn.UnrollForwarderAnnot) then - This(sym.owner.asClass).select(sym) - else - ref(path) + case path: TermRef => ref(path) case path: ThisType => untpd.This(untpd.EmptyTypeIdent).withType(path) case path: ConstantType => Literal(path.value) case path: ErrorType if isBestEffortTasty => TypeTree(path) diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala index 0b817d492263..9cbaf7738533 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala @@ -116,6 +116,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { def generateSingleForwarder(defdef: DefDef, prevMethodType: Type, paramIndex: Int, + paramCount: Int, nextParamIndex: Int, nextSymbol: Symbol, annotatedParamListIndex: Int, @@ -125,18 +126,12 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { val forwarderDefSymbol0 = Symbols.newSymbol( defdef.symbol.owner, defdef.name, - (defdef.symbol.flags &~ - HasDefaultParams &~ - (if nextParamIndex == -1 then EmptyFlags else Deferred)) | + defdef.symbol.flags &~ HasDefaultParams | Invisible | Synthetic, NoType, // fill in later coord = nextSymbol.span.shift(1) // shift by 1 to avoid "secondary constructor must call preceding" error ).entered - // we need this such that when unpickling a TERMREFdirect, if we see this annotation, - // we restore the tree to a Select - forwarderDefSymbol0.addAnnotation(defn.UnrollForwarderAnnot) - val newParamSymMappings = extractParamSymss(copyParamSym(_, forwarderDefSymbol0)) val (oldParams, newParams) = newParamSymMappings.flatten.unzip @@ -170,7 +165,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { .map(_.size) .sum - val defaultCalls = Range(paramIndex, nextParamIndex).map(n => + val defaultCalls = Range(paramIndex, paramCount).map(n => def makeSelect(refTree: Tree, name: TermName): Tree = val sym = refTree.symbol @@ -204,7 +199,8 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { else Apply(lhs, newParams) ) - val forwarderInner: Tree = This(defdef.symbol.owner.asClass).select(nextSymbol) + val forwarderInner: Tree = + This(defdef.symbol.owner.asClass).select(defdef.symbol) val forwarderCallArgs = newParamSymLists.zipWithIndex.map{case (ps, i) => @@ -226,8 +222,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { } val forwarderDef = - tpd.DefDef(forwarderDefSymbol, - rhs = if nextParamIndex == -1 then EmptyTree else forwarderRhs()) + tpd.DefDef(forwarderDefSymbol, rhs = forwarderRhs()) forwarderDef.withSpan(nextSymbol.span.shift(1)) } @@ -297,6 +292,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { defdef, defdef.symbol.info, paramIndex, + paramCount, nextParamIndex, nextSymbol, paramClauseIndex, diff --git a/library/src/scala/annotation/internal/UnrollForwarder.scala b/library/src/scala/annotation/internal/UnrollForwarder.scala deleted file mode 100644 index cfa9c3534737..000000000000 --- a/library/src/scala/annotation/internal/UnrollForwarder.scala +++ /dev/null @@ -1,9 +0,0 @@ -package scala.annotation.internal - -import scala.annotation.Annotation -import scala.annotation.experimental - -/** This method was generated via `@unroll` annotation. - */ -@experimental("under review as part of SIP-61") -final class UnrollForwarder extends Annotation diff --git a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala index 0443fd8e1c28..2c4ee5401f92 100644 --- a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala @@ -96,7 +96,7 @@ val experimentalDefinitionInLibrary = Set( "scala.Predef$.runtimeChecked", "scala.annotation.internal.RuntimeChecked", // New feature: SIP 61 - @unroll annotation - "scala.annotation.unroll", "scala.annotation.internal.UnrollForwarder" + "scala.annotation.unroll" ) diff --git a/tests/run/unroll-multiple.scala b/tests/run/unroll-multiple.scala new file mode 100644 index 000000000000..e1790be26395 --- /dev/null +++ b/tests/run/unroll-multiple.scala @@ -0,0 +1,72 @@ +//> using options -experimental + +import scala.annotation.unroll +import scala.deriving.Mirror + +class Unrolled { + final def foo( + s: String, + @unroll y: Boolean = true, + @unroll i: Int = 0, + @unroll c: Char = '?'): String = s + y + i + c +} + +class Outer { + class Unrolled { + final def bar( + s: String, + @unroll y: Boolean = true, + @unroll i: Int = 0, + @unroll c: Char = '?'): String = s + y + i + c + } + + case class UnrolledCase( + s: String, + @unroll y: Boolean = true, + @unroll i: Int = 0, + @unroll c: Char = '?') { + def baz: String = s + y + i + c + } + + class UnrolledSecondary { + private var msg = "" + + def qux: String = msg + + def this( + s: String, + @unroll y: Boolean = true, + @unroll i: Int = 0, + @unroll c: Char = '?') = { + this() + msg = s + y + i + c + } + } +} + +@main def Test: Unit = + assert(Unrolled().foo("foo") == "footrue0?") + assert(Unrolled().foo("foo", false) == "foofalse0?") + assert(Unrolled().foo("foo", false, 1) == "foofalse1?") + assert(Unrolled().foo("foo", false, 1, '@') == "foofalse1@") + val outer = new Outer() + assert(new outer.Unrolled().bar("bar") == "bartrue0?") + assert(new outer.Unrolled().bar("bar", false) == "barfalse0?") + assert(new outer.Unrolled().bar("bar", false, 1) == "barfalse1?") + assert(new outer.Unrolled().bar("bar", false, 1, '@') == "barfalse1@") + assert(outer.UnrolledCase.apply("baz").baz == "baztrue0?") + assert(outer.UnrolledCase.apply("baz", false).baz == "bazfalse0?") + assert(outer.UnrolledCase.apply("baz", false, 1).baz == "bazfalse1?") + assert(outer.UnrolledCase.apply("baz", false, 1, '@').baz == "bazfalse1@") + assert((new outer.UnrolledCase("baz")).baz == "baztrue0?") + assert((new outer.UnrolledCase("baz", false)).baz == "bazfalse0?") + assert((new outer.UnrolledCase("baz", false, 1)).baz == "bazfalse1?") + assert((new outer.UnrolledCase("baz", false, 1, '@')).baz == "bazfalse1@") + assert(summon[Mirror.Of[outer.UnrolledCase]].fromProduct(Tuple("baz")).baz == "baztrue0?") + assert(summon[Mirror.Of[outer.UnrolledCase]].fromProduct(("baz", false)).baz == "bazfalse0?") + assert(summon[Mirror.Of[outer.UnrolledCase]].fromProduct(("baz", false, 1)).baz == "bazfalse1?") + assert(summon[Mirror.Of[outer.UnrolledCase]].fromProduct(("baz", false, 1, '@')).baz == "bazfalse1@") + assert(new outer.UnrolledSecondary("qux").qux == "quxtrue0?") + assert(new outer.UnrolledSecondary("qux", false).qux == "quxfalse0?") + assert(new outer.UnrolledSecondary("qux", false, 1).qux == "quxfalse1?") + assert(new outer.UnrolledSecondary("qux", false, 1, '@').qux == "quxfalse1@") From 5eea7996d40ff8761dc27f8d54a94271ae04d3df Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Sun, 6 Oct 2024 01:00:01 +0200 Subject: [PATCH 23/35] SIP 61 - add errors when unrolling apply, copy and fromProduct Also standardise error messages as Declaration Errors --- .../tools/dotc/reporting/ErrorMessageID.scala | 1 + .../dotty/tools/dotc/reporting/messages.scala | 32 ++++++++++++++++--- .../tools/dotc/transform/PostTyper.scala | 32 ++++++++++--------- .../tools/dotc/typer/CrossVersionChecks.scala | 2 +- tests/neg/unroll-abstractMethod.check | 12 +++---- tests/neg/unroll-duped.check | 12 +++++++ tests/neg/unroll-duped.scala | 27 ++++++++++++++++ tests/neg/unroll-illegal.check | 18 +++++------ tests/neg/unroll-illegal3.check | 18 +++++------ tests/neg/unroll-traitConstructor.check | 6 ++-- 10 files changed, 112 insertions(+), 48 deletions(-) create mode 100644 tests/neg/unroll-duped.check create mode 100644 tests/neg/unroll-duped.scala diff --git a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala index cc78203e873f..25f2f879077e 100644 --- a/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala +++ b/compiler/src/dotty/tools/dotc/reporting/ErrorMessageID.scala @@ -220,6 +220,7 @@ enum ErrorMessageID(val isActive: Boolean = true) extends java.lang.Enum[ErrorMe case DeprecatedInfixNamedArgumentSyntaxID // errorNumber: 204 case GivenSearchPriorityID // errorNumber: 205 case EnumMayNotBeValueClassesID // errorNumber: 206 + case IllegalUnrollPlacementID // errorNumber: 207 def errorNumber = ordinal - 1 diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index 28a2b5757a93..a733afb68f2d 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -3331,14 +3331,14 @@ final class QuotedTypeMissing(tpe: Type)(using Context) extends StagingMessage(Q private def witness = defn.QuotedTypeClass.typeRef.appliedTo(tpe) - override protected def msg(using Context): String = + override protected def msg(using Context): String = i"Reference to $tpe within quotes requires a given ${witness} in scope" override protected def explain(using Context): String = - i"""Referencing `$tpe` inside a quoted expression requires a `${witness}` to be in scope. + i"""Referencing `$tpe` inside a quoted expression requires a `${witness}` to be in scope. |Since Scala is subject to erasure at runtime, the type information will be missing during the execution of the code. - |`${witness}` is therefore needed to carry `$tpe`'s type information into the quoted code. - |Without an implicit `${witness}`, the type `$tpe` cannot be properly referenced within the expression. + |`${witness}` is therefore needed to carry `$tpe`'s type information into the quoted code. + |Without an implicit `${witness}`, the type `$tpe` cannot be properly referenced within the expression. |To resolve this, ensure that a `${witness}` is available, either through a context-bound or explicitly. |""" @@ -3350,7 +3350,6 @@ final class DeprecatedAssignmentSyntax(key: Name, value: untpd.Tree)(using Conte |not as an assignment. | |To assign a value, use curly braces: `{${key} = ${value}}`.""" - + Message.rewriteNotice("This", version = SourceVersion.`3.6-migration`) override protected def explain(using Context): String = "" @@ -3405,3 +3404,26 @@ final class EnumMayNotBeValueClasses(sym: Symbol)(using Context) extends SyntaxM def explain(using Context) = "" end EnumMayNotBeValueClasses + +class IllegalUnrollPlacement(origin: Option[Symbol])(using Context) +extends DeclarationMsg(IllegalUnrollPlacementID): + def msg(using Context) = origin match + case None => "@unroll is only allowed on a method parameter" + case Some(method) => + val isCtor = method.isConstructor + def what = if isCtor then i"a ${if method.owner.is(Trait) then "trait" else "class"} constructor" else i"method ${method.name}" + val prefix = s"Can not unroll parameters of $what" + if method.is(Deferred) then + i"$prefix: it must not be abstract" + else if isCtor && method.owner.is(Trait) then + i"implementation restriction: $prefix" + else if !(isCtor || method.is(Final) || method.owner.is(ModuleClass)) then + i"$prefix: it is not final" + else if method.owner.companionClass.is(CaseClass) then + i"$prefix of a case class companion object: please annotate the class constructor instead" + else + assert(method.owner.is(CaseClass)) + i"$prefix of a case class: please annotate the class constructor instead" + + def explain(using Context) = "" +end IllegalUnrollPlacement diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index bdfe0f114a59..cc7595442ddb 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -124,7 +124,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => private var noCheckNews: Set[New] = Set() - def isValidUnrolledMethod(method: Symbol)(using Context): Boolean = + def isValidUnrolledMethod(method: Symbol, origin: SrcPos)(using Context): Boolean = val seenMethods = val local = seenUnrolledMethods if local == null then @@ -134,21 +134,23 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => else local seenMethods.getOrElseUpdate(method, { - if method.name.is(DefaultGetterName) then + val isCtor = method.isConstructor + if + method.name.is(DefaultGetterName) + then false // not an error, but not an expandable unrolled method + else if + method.is(Deferred) + || isCtor && method.owner.is(Trait) + || !(isCtor || method.is(Final) || method.owner.is(ModuleClass)) + || method.owner.companionClass.is(CaseClass) + && (method.name == nme.apply || method.name == nme.fromProduct) + || method.owner.is(CaseClass) && method.name == nme.copy + then + report.error(IllegalUnrollPlacement(Some(method)), origin) + false else - var res = true - if method.is(Deferred) then - report.error("Unrolled method must be final and concrete", method.srcPos) - res = false - val isCtor = method.isConstructor - if isCtor && method.owner.is(Trait) then - report.error("implementation restriction: Unrolled method cannot be a trait constructor", method.srcPos) - res = false - if !(isCtor || method.is(Final) || method.owner.is(ModuleClass)) then - report.error(s"Unrolled method ${method.name} must be final", method.srcPos) - res = false - res + true }) def withNoCheckNews[T](ts: List[New])(op: => T): T = { @@ -230,7 +232,7 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => } private def registerIfUnrolledParam(sym: Symbol)(using Context): Unit = - if sym.hasAnnotation(defn.UnrollAnnot) && isValidUnrolledMethod(sym.owner) then + if sym.hasAnnotation(defn.UnrollAnnot) && isValidUnrolledMethod(sym.owner, sym.sourcePos) then val cls = sym.enclosingClass val classes = ctx.compilationUnit.unrolledClasses val additions = Array(cls, cls.linkedClass).filter(_ != NoSymbol) diff --git a/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala b/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala index 03990f0e54b2..8f8a68aa5735 100644 --- a/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala +++ b/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala @@ -82,7 +82,7 @@ class CrossVersionChecks extends MiniPhase: } private def unrollError(pos: SrcPos)(using Context): Unit = - report.error("@unroll is only allowed on a method parameter", pos) + report.error(IllegalUnrollPlacement(None), pos) private def checkUnrollAnnot(annotSym: Symbol, pos: SrcPos)(using Context): Unit = if annotSym == defn.UnrollAnnot then diff --git a/tests/neg/unroll-abstractMethod.check b/tests/neg/unroll-abstractMethod.check index 53f6be8b5092..948c7d1a7862 100644 --- a/tests/neg/unroll-abstractMethod.check +++ b/tests/neg/unroll-abstractMethod.check @@ -1,8 +1,8 @@ --- Error: tests/neg/unroll-abstractMethod.scala:6:6 -------------------------------------------------------------------- +-- [E207] Declaration Error: tests/neg/unroll-abstractMethod.scala:6:41 ------------------------------------------------ 6 | def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String // error - | ^ - | Unrolled method must be final and concrete --- Error: tests/neg/unroll-abstractMethod.scala:10:6 ------------------------------------------------------------------- + | ^ + | Can not unroll parameters of method foo: it must not be abstract +-- [E207] Declaration Error: tests/neg/unroll-abstractMethod.scala:10:41 ----------------------------------------------- 10 | def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String // error - | ^ - | Unrolled method must be final and concrete + | ^ + | Can not unroll parameters of method foo: it must not be abstract diff --git a/tests/neg/unroll-duped.check b/tests/neg/unroll-duped.check new file mode 100644 index 000000000000..2c1cc80cfee7 --- /dev/null +++ b/tests/neg/unroll-duped.check @@ -0,0 +1,12 @@ +-- [E207] Declaration Error: tests/neg/unroll-duped.scala:11:45 -------------------------------------------------------- +11 | final def copy(s: String = this.s, @unroll y: Boolean = this.y): UnrolledCase = // error + | ^ + | Can not unroll parameters of method copy of a case class: please annotate the class constructor instead +-- [E207] Declaration Error: tests/neg/unroll-duped.scala:18:12 -------------------------------------------------------- +18 | @unroll y: Boolean = true // error + | ^ + |Can not unroll parameters of method apply of a case class companion object: please annotate the class constructor instead +-- [E207] Declaration Error: tests/neg/unroll-duped.scala:22:26 -------------------------------------------------------- +22 | def fromProduct(@unroll p: Product = EmptyTuple): UnrolledCase = { // error + | ^ + |Can not unroll parameters of method fromProduct of a case class companion object: please annotate the class constructor instead diff --git a/tests/neg/unroll-duped.scala b/tests/neg/unroll-duped.scala new file mode 100644 index 000000000000..a578fc837628 --- /dev/null +++ b/tests/neg/unroll-duped.scala @@ -0,0 +1,27 @@ +//> using options -experimental + +import scala.annotation.unroll + +case class UnrolledCase( + s: String, + y: Boolean = true, +) { + def foo: String = s + y + + final def copy(s: String = this.s, @unroll y: Boolean = this.y): UnrolledCase = // error + new UnrolledCase(s, y) +} + +object UnrolledCase { + def apply( + s: String, + @unroll y: Boolean = true // error + ): UnrolledCase = + new UnrolledCase(s, y) + + def fromProduct(@unroll p: Product = EmptyTuple): UnrolledCase = { // error + val s = p.productElement(0).asInstanceOf[String] + val y = p.productElement(1).asInstanceOf[Boolean] + UnrolledCase(s, y) + } +} diff --git a/tests/neg/unroll-illegal.check b/tests/neg/unroll-illegal.check index c1f57847e3a6..96d7528ac338 100644 --- a/tests/neg/unroll-illegal.check +++ b/tests/neg/unroll-illegal.check @@ -1,36 +1,36 @@ --- Error: tests/neg/unroll-illegal.scala:6:6 --------------------------------------------------------------------------- +-- [E207] Declaration Error: tests/neg/unroll-illegal.scala:6:6 -------------------------------------------------------- 6 |class UnrollClass // error | ^ | @unroll is only allowed on a method parameter --- Error: tests/neg/unroll-illegal.scala:9:6 --------------------------------------------------------------------------- +-- [E207] Declaration Error: tests/neg/unroll-illegal.scala:9:6 -------------------------------------------------------- 9 |trait UnrollTrait // error | ^ | @unroll is only allowed on a method parameter --- Error: tests/neg/unroll-illegal.scala:12:7 -------------------------------------------------------------------------- +-- [E207] Declaration Error: tests/neg/unroll-illegal.scala:12:7 ------------------------------------------------------- 12 |object UnrollObject // error | ^ | @unroll is only allowed on a method parameter --- Error: tests/neg/unroll-illegal.scala:18:5 -------------------------------------------------------------------------- +-- [E207] Declaration Error: tests/neg/unroll-illegal.scala:18:5 ------------------------------------------------------- 18 |enum UnrollEnum { case X } // error | ^ | @unroll is only allowed on a method parameter --- Error: tests/neg/unroll-illegal.scala:21:25 ------------------------------------------------------------------------- +-- [E207] Declaration Error: tests/neg/unroll-illegal.scala:21:25 ------------------------------------------------------ 21 | val annotExpr: Int = 23: @unroll // error | ^ | @unroll is only allowed on a method parameter --- Error: tests/neg/unroll-illegal.scala:22:19 ------------------------------------------------------------------------- +-- [E207] Declaration Error: tests/neg/unroll-illegal.scala:22:19 ------------------------------------------------------ 22 | type annotType = Int @unroll // error | ^^^^^^^^^^^ | @unroll is only allowed on a method parameter --- Error: tests/neg/unroll-illegal.scala:25:6 -------------------------------------------------------------------------- +-- [E207] Declaration Error: tests/neg/unroll-illegal.scala:25:6 ------------------------------------------------------- 25 | val unrollVal: Int = 23 // error | ^ | @unroll is only allowed on a method parameter --- Error: tests/neg/unroll-illegal.scala:28:6 -------------------------------------------------------------------------- +-- [E207] Declaration Error: tests/neg/unroll-illegal.scala:28:6 ------------------------------------------------------- 28 | def unrollDef: Int = 23 // error | ^ | @unroll is only allowed on a method parameter --- Error: tests/neg/unroll-illegal.scala:15:5 -------------------------------------------------------------------------- +-- [E207] Declaration Error: tests/neg/unroll-illegal.scala:15:5 ------------------------------------------------------- 15 |type UnrollType = Int // error | ^ | @unroll is only allowed on a method parameter diff --git a/tests/neg/unroll-illegal3.check b/tests/neg/unroll-illegal3.check index 20917e857ee7..8502b76b50e2 100644 --- a/tests/neg/unroll-illegal3.check +++ b/tests/neg/unroll-illegal3.check @@ -1,12 +1,12 @@ --- Error: tests/neg/unroll-illegal3.scala:7:8 -------------------------------------------------------------------------- +-- [E207] Declaration Error: tests/neg/unroll-illegal3.scala:7:31 ------------------------------------------------------ 7 | def foo(s: String, @unroll y: Boolean) = s + y // error - | ^ - | Unrolled method foo must be final --- Error: tests/neg/unroll-illegal3.scala:12:6 ------------------------------------------------------------------------- + | ^ + | Can not unroll parameters of method foo: it is not final +-- [E207] Declaration Error: tests/neg/unroll-illegal3.scala:12:29 ----------------------------------------------------- 12 | def foo(s: String, @unroll y: Boolean) = s + y // error - | ^ - | Unrolled method foo must be final --- Error: tests/neg/unroll-illegal3.scala:16:6 ------------------------------------------------------------------------- + | ^ + | Can not unroll parameters of method foo: it is not final +-- [E207] Declaration Error: tests/neg/unroll-illegal3.scala:16:29 ----------------------------------------------------- 16 | def foo(s: String, @unroll y: Boolean): String // error - | ^ - | Unrolled method must be final and concrete + | ^ + | Can not unroll parameters of method foo: it must not be abstract diff --git a/tests/neg/unroll-traitConstructor.check b/tests/neg/unroll-traitConstructor.check index 74e3eff5999b..0a5570667196 100644 --- a/tests/neg/unroll-traitConstructor.check +++ b/tests/neg/unroll-traitConstructor.check @@ -1,4 +1,4 @@ --- Error: tests/neg/unroll-traitConstructor.scala:5:12 ----------------------------------------------------------------- +-- [E207] Declaration Error: tests/neg/unroll-traitConstructor.scala:5:32 ---------------------------------------------- 5 |trait Unroll(a: String, @unroll b: Boolean = true): // error - | ^ - | implementation restriction: Unrolled method cannot be a trait constructor + | ^ + | implementation restriction: Can not unroll parameters of a trait constructor From 983a05a18fee12bec0227d10ca1a6fd29fefcfc4 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Mon, 7 Oct 2024 17:51:32 +0200 Subject: [PATCH 24/35] add failing test when inline method calls forwarder --- .../add-param-unroll/a-changes/A.scala | 9 +++++ .../tasty-compat/add-param-unroll/a/A.scala | 9 +++++ .../tasty-compat/add-param-unroll/b/B.scala | 7 ++++ .../tasty-compat/add-param-unroll/build.sbt | 36 +++++++++++++++++++ .../tasty-compat/add-param-unroll/c/C.scala | 5 +++ .../project/DottyInjectedPlugin.scala | 11 ++++++ sbt-test/tasty-compat/add-param-unroll/test | 8 +++++ 7 files changed, 85 insertions(+) create mode 100644 sbt-test/tasty-compat/add-param-unroll/a-changes/A.scala create mode 100644 sbt-test/tasty-compat/add-param-unroll/a/A.scala create mode 100644 sbt-test/tasty-compat/add-param-unroll/b/B.scala create mode 100644 sbt-test/tasty-compat/add-param-unroll/build.sbt create mode 100644 sbt-test/tasty-compat/add-param-unroll/c/C.scala create mode 100644 sbt-test/tasty-compat/add-param-unroll/project/DottyInjectedPlugin.scala create mode 100644 sbt-test/tasty-compat/add-param-unroll/test diff --git a/sbt-test/tasty-compat/add-param-unroll/a-changes/A.scala b/sbt-test/tasty-compat/add-param-unroll/a-changes/A.scala new file mode 100644 index 000000000000..9cbb82b5dc30 --- /dev/null +++ b/sbt-test/tasty-compat/add-param-unroll/a-changes/A.scala @@ -0,0 +1,9 @@ +package a + +import scala.annotation.unroll + +object A { + + def foo(s: String, x: Int, @unroll b: Boolean = true): String = s + x + b + +} diff --git a/sbt-test/tasty-compat/add-param-unroll/a/A.scala b/sbt-test/tasty-compat/add-param-unroll/a/A.scala new file mode 100644 index 000000000000..b2b74e32a922 --- /dev/null +++ b/sbt-test/tasty-compat/add-param-unroll/a/A.scala @@ -0,0 +1,9 @@ +package a + +import scala.annotation.unroll + +object A { + + def foo(s: String, x: Int): String = s + x + +} diff --git a/sbt-test/tasty-compat/add-param-unroll/b/B.scala b/sbt-test/tasty-compat/add-param-unroll/b/B.scala new file mode 100644 index 000000000000..6afa8e347c54 --- /dev/null +++ b/sbt-test/tasty-compat/add-param-unroll/b/B.scala @@ -0,0 +1,7 @@ +package b + +import a.* + +object B { + transparent inline def caller = A.foo("abc", 2) +} diff --git a/sbt-test/tasty-compat/add-param-unroll/build.sbt b/sbt-test/tasty-compat/add-param-unroll/build.sbt new file mode 100644 index 000000000000..7ea07075632f --- /dev/null +++ b/sbt-test/tasty-compat/add-param-unroll/build.sbt @@ -0,0 +1,36 @@ +lazy val commonSettings = Seq( + scalacOptions += "-experimental", +) + +lazy val printSettings = Seq( + scalacOptions += "-Yprint-tasty", +) + +lazy val a = project.in(file("a")) + .settings(commonSettings) + .settings( + Compile / classDirectory := (ThisBuild / baseDirectory).value / "b-input" + ) + +lazy val b = project.in(file("b")) + .settings(commonSettings) + .settings( + Compile / unmanagedClasspath += (ThisBuild / baseDirectory).value / "b-input", + Compile / classDirectory := (ThisBuild / baseDirectory).value / "c-input" + ) + +lazy val `a-changes` = project.in(file("a-changes")) + .settings(commonSettings) + .settings( + Compile / classDirectory := (ThisBuild / baseDirectory).value / "c-input" + ) + +lazy val c = project.in(file("c")) + .settings(commonSettings) + .settings(printSettings) + .settings( + // scalacOptions ++= Seq("-from-tasty", "-Ycheck:readTasty", "-Xfatal-warnings", "-Xprint:readTasty", "-Xprint-types"), + // Compile / sources := Seq(new java.io.File("c-input/B.tasty")), + Compile / unmanagedClasspath += (ThisBuild / baseDirectory).value / "c-input", + Compile / classDirectory := (ThisBuild / baseDirectory).value / "c-output" + ) diff --git a/sbt-test/tasty-compat/add-param-unroll/c/C.scala b/sbt-test/tasty-compat/add-param-unroll/c/C.scala new file mode 100644 index 000000000000..6ce872a8730c --- /dev/null +++ b/sbt-test/tasty-compat/add-param-unroll/c/C.scala @@ -0,0 +1,5 @@ +import b.* + +object C { + val res = B.caller +} diff --git a/sbt-test/tasty-compat/add-param-unroll/project/DottyInjectedPlugin.scala b/sbt-test/tasty-compat/add-param-unroll/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..fb946c4b8c61 --- /dev/null +++ b/sbt-test/tasty-compat/add-param-unroll/project/DottyInjectedPlugin.scala @@ -0,0 +1,11 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion") + ) +} diff --git a/sbt-test/tasty-compat/add-param-unroll/test b/sbt-test/tasty-compat/add-param-unroll/test new file mode 100644 index 000000000000..3fa9e731fe40 --- /dev/null +++ b/sbt-test/tasty-compat/add-param-unroll/test @@ -0,0 +1,8 @@ +# compile library A +> a/compile +# compile library B, from source, against A +> b/compile +# add a new parameter in method to library A', using @unroll to generate a forwarder +> a-changes/compile +# compile B, from tasty, against A', it should still compile: the generated forwarder is resolved. +> c/compile From ae835501f0ad08b408ee1305c49c4e4b20acb5e8 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Mon, 7 Oct 2024 18:04:41 +0200 Subject: [PATCH 25/35] Experiment: add new SourceInvisble TASTy flag --- .../dotty/tools/dotc/core/Denotations.scala | 2 +- .../src/dotty/tools/dotc/core/Flags.scala | 7 +++++-- compiler/src/dotty/tools/dotc/core/Mode.scala | 5 ++++- .../src/dotty/tools/dotc/core/NamerOps.scala | 2 +- .../tools/dotc/core/SymDenotations.scala | 2 +- .../tools/dotc/core/tasty/TreePickler.scala | 1 + .../tools/dotc/core/tasty/TreeUnpickler.scala | 20 +++++++++++++++++-- .../dotc/transform/UnrollDefinitions.scala | 4 ++-- .../quoted/runtime/impl/QuotesImpl.scala | 1 + library/src/scala/quoted/Quotes.scala | 4 ++++ tasty/src/dotty/tools/tasty/TastyFormat.scala | 4 ++++ .../stdlibExperimentalDefinitions.scala | 2 +- 12 files changed, 43 insertions(+), 11 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 85ff51bc19de..cefc6c4d4cb3 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -1072,7 +1072,7 @@ object Denotations { def filterDisjoint(denots: PreDenotation)(using Context): SingleDenotation = if (denots.exists && denots.matches(this)) NoDenotation else this def filterWithFlags(required: FlagSet, excluded: FlagSet)(using Context): SingleDenotation = - val realExcluded = if ctx.isAfterTyper then excluded else excluded | Invisible + val realExcluded = if ctx.isAfterTyper then excluded else excluded | Invisible | (if ctx.mode.is(Mode.ResolveFromTASTy) then EmptyFlags else SourceInvisible) def symd: SymDenotation = this match case symd: SymDenotation => symd case _ => symbol.denot diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 0775b3caaf0c..9981db87bc96 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -380,6 +380,9 @@ object Flags { /** Tracked modifier for class parameter / a class with some tracked parameters */ val (Tracked @ _, _, Dependent @ _) = newFlags(46, "tracked") + /** Symbol can not be resolved from source during typer. PROVISIONAL (possibly replace with `Invisible` with new semantics) */ + val (SourceInvisible @ _, _, _) = newFlags(47, "") + // ------------ Flags following this one are not pickled ---------------------------------- /** Symbol is not a member of its owner */ @@ -471,7 +474,7 @@ object Flags { Scala2SpecialFlags, MutableOrOpen, Opaque, Touched, JavaStatic, OuterOrCovariant, LabelOrContravariant, CaseAccessor, Tracked, Extension, NonMember, Implicit, Given, Permanent, Synthetic, Exported, - SuperParamAliasOrScala2x, Inline, Macro, ConstructorProxy, Invisible) + SuperParamAliasOrScala2x, Inline, Macro, ConstructorProxy, Invisible, SourceInvisible) /** Flags that are not (re)set when completing the denotation, or, if symbol is * a top-level class or object, when completing the denotation once the class @@ -525,7 +528,7 @@ object Flags { val RetainedModuleValAndClassFlags: FlagSet = AccessFlags | Package | Case | Synthetic | JavaDefined | JavaStatic | Artifact | - Lifted | MixedIn | Specialized | ConstructorProxy | Invisible | Erased + Lifted | MixedIn | Specialized | ConstructorProxy | Invisible | SourceInvisible | Erased /** Flags that can apply to a module val */ val RetainedModuleValFlags: FlagSet = RetainedModuleValAndClassFlags | diff --git a/compiler/src/dotty/tools/dotc/core/Mode.scala b/compiler/src/dotty/tools/dotc/core/Mode.scala index 14d7827974c0..965c3823d610 100644 --- a/compiler/src/dotty/tools/dotc/core/Mode.scala +++ b/compiler/src/dotty/tools/dotc/core/Mode.scala @@ -105,7 +105,7 @@ object Mode { /** Use previous Scheme for implicit resolution. Currently significant * in 3.0-migration where we use Scala-2's scheme instead and in 3.5 and 3.6-migration - * where we use the previous scheme up to 3.4 for comparison with the new scheme. + * where we use the previous scheme up to 3.4 for comparison with the new scheme. */ val OldImplicitResolution: Mode = newMode(15, "OldImplicitResolution") @@ -125,6 +125,9 @@ object Mode { /** Read original positions when unpickling from TASTY */ val ReadPositions: Mode = newMode(17, "ReadPositions") + /** We are resolving a SELECT name from TASTy */ + val ResolveFromTASTy: Mode = newMode(18, "ResolveFromTASTy") + /** We are elaborating the fully qualified name of a package clause. * In this case, identifiers should never be imported. */ diff --git a/compiler/src/dotty/tools/dotc/core/NamerOps.scala b/compiler/src/dotty/tools/dotc/core/NamerOps.scala index 363a01665564..c1962d9fc557 100644 --- a/compiler/src/dotty/tools/dotc/core/NamerOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NamerOps.scala @@ -110,7 +110,7 @@ object NamerOps: else NoSymbol.assertingErrorsReported(em"no companion $name in $scope") /** If a class has one of these flags, it does not get a constructor companion */ - private val NoConstructorProxyNeededFlags = Abstract | Trait | Case | Synthetic | Module | Invisible + private val NoConstructorProxyNeededFlags = Abstract | Trait | Case | Synthetic | Module | Invisible | SourceInvisible /** The flags of a constructor companion */ private val ConstructorCompanionFlags = Synthetic | ConstructorProxy diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index be651842d9b0..2c4ee4e67005 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -617,7 +617,7 @@ object SymDenotations { case _ => // Otherwise, no completion is necessary, see the preconditions of `markAbsent()`. (myInfo `eq` NoType) - || is(Invisible) && ctx.isTyper + || (is(Invisible) || is(SourceInvisible) && !ctx.mode.is(Mode.ResolveFromTASTy)) && ctx.isTyper || is(ModuleVal, butNot = Package) && moduleClass.isAbsent(canForce) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 6822fcc8e548..8befe2bf3cf0 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -881,6 +881,7 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) { if flags.is(Transparent) then writeModTag(TRANSPARENT) if flags.is(Infix) then writeModTag(INFIX) if flags.is(Invisible) then writeModTag(INVISIBLE) + if flags.is(SourceInvisible) then writeModTag(SOURCEINVISIBLE) if (flags.is(Erased)) writeModTag(ERASED) if (flags.is(Exported)) writeModTag(EXPORTED) if (flags.is(Given)) writeModTag(GIVEN) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index d9ae4ddb6006..284e23822ce4 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -754,6 +754,7 @@ class TreeUnpickler(reader: TastyReader, case EXPORTED => addFlag(Exported) case OPEN => addFlag(Open) case INVISIBLE => addFlag(Invisible) + case SOURCEINVISIBLE => addFlag(SourceInvisible) case TRANSPARENT => addFlag(Transparent) case INFIX => addFlag(Infix) case TRACKED => addFlag(Tracked) @@ -1562,7 +1563,7 @@ class TreeUnpickler(reader: TastyReader, * - sbt-test/tasty-compat/remove-override * - sbt-test/tasty-compat/move-method */ - def lookupInSuper = + def lookupInSuper(using Context) = val cls = ownerTpe.classSymbol if cls.exists then cls.asClass.classDenot @@ -1571,7 +1572,8 @@ class TreeUnpickler(reader: TastyReader, else NoDenotation - val denot = + + def searchDenot(using Context): Denotation = if owner.is(JavaAnnotation) && name == nme.CONSTRUCTOR then // #19951 Fix up to read TASTy produced before 3.5.0 -- ignore the signature ownerTpe.nonPrivateDecl(name).asSeenFrom(prefix) @@ -1579,6 +1581,20 @@ class TreeUnpickler(reader: TastyReader, val d = ownerTpe.decl(name).atSignature(sig, target) (if !d.exists then lookupInSuper else d).asSeenFrom(prefix) + val denot0 = inContext(ctx.addMode(Mode.ResolveFromTASTy)): + searchDenot // able to resolve SourceInvisible members + + val denot = + if + denot0.symbol.exists + && denot0.symbol.is(SourceInvisible) + && denot0.symbol.isDefinedInSource + then + searchDenot // fallback + else + denot0 + + makeSelect(qual, name, denot) case REPEATED => val elemtpt = readTpt() diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala index 9cbaf7738533..e36a5e875787 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala @@ -26,7 +26,7 @@ import dotty.tools.unreachable /**Implementation of SIP-61. * Runs when `@unroll` annotations are found in a compilation unit, installing new definitions * - * Note that it only generates `Invisible` methods, so no interactions with Zinc/SemanticDB + * Note that it only generates `SourceInvisible` methods, so no interactions with Zinc/SemanticDB */ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { self => @@ -127,7 +127,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { defdef.symbol.owner, defdef.name, defdef.symbol.flags &~ HasDefaultParams | - Invisible | Synthetic, + SourceInvisible | Synthetic, NoType, // fill in later coord = nextSymbol.span.shift(1) // shift by 1 to avoid "secondary constructor must call preceding" error ).entered diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 22be293c3562..5c2d3b4035ef 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2966,6 +2966,7 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def Infix: Flags = dotc.core.Flags.Infix def Inline: Flags = dotc.core.Flags.Inline def Invisible: Flags = dotc.core.Flags.Invisible + def SourceInvisible: Flags = dotc.core.Flags.SourceInvisible def JavaDefined: Flags = dotc.core.Flags.JavaDefined def JavaStatic: Flags = dotc.core.Flags.JavaStatic def JavaAnnotation: Flags = dotc.core.Flags.JavaAnnotation diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 7a98d6f6f761..897a0f629ebf 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -4624,6 +4624,10 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Is this symbol invisible when typechecking? */ def Invisible: Flags + /** Is this symbol invisible when typechecking? (only from source) */ + @experimental + def SourceInvisible: Flags + /** Is this symbol defined in a Java class */ def JavaDefined: Flags diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index 8ff590fefec5..c49739ad3a7f 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -228,6 +228,7 @@ Standard-Section: "ASTs" TopLevelStat* EXPORTED -- An export forwarder OPEN -- an open class INVISIBLE -- invisible during typechecking + SOURCEINVISIBLE -- invisible in the source code TRACKED -- a tracked class parameter / a dependent class Annotation @@ -511,6 +512,7 @@ object TastyFormat { final val EMPTYCLAUSE = 45 final val SPLITCLAUSE = 46 final val TRACKED = 47 + final val SOURCEINVISIBLE = 48 // Tree Cat. 2: tag Nat final val firstNatTreeTag = SHAREDterm @@ -700,6 +702,7 @@ object TastyFormat { | EXPORTED | OPEN | INVISIBLE + | SOURCEINVISIBLE | ANNOTATION | PRIVATEqualified | PROTECTEDqualified @@ -764,6 +767,7 @@ object TastyFormat { case EXPORTED => "EXPORTED" case OPEN => "OPEN" case INVISIBLE => "INVISIBLE" + case SOURCEINVISIBLE => "SOURCEINVISIBLE" case PARAMalias => "PARAMalias" case EMPTYCLAUSE => "EMPTYCLAUSE" case SPLITCLAUSE => "SPLITCLAUSE" diff --git a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala index 2c4ee5401f92..9e6df7f9525c 100644 --- a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala @@ -96,7 +96,7 @@ val experimentalDefinitionInLibrary = Set( "scala.Predef$.runtimeChecked", "scala.annotation.internal.RuntimeChecked", // New feature: SIP 61 - @unroll annotation - "scala.annotation.unroll" + "scala.annotation.unroll", "scala.quoted.Quotes.reflectModule.FlagsModule.SourceInvisible" ) From d64dc228aaa14a9e0ba5fbdf52dc0021ee1a176f Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Mon, 7 Oct 2024 19:15:04 +0200 Subject: [PATCH 26/35] fix whitespace --- tests/neg/i21696.check | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/neg/i21696.check b/tests/neg/i21696.check index 9195263040b3..ce4844782107 100644 --- a/tests/neg/i21696.check +++ b/tests/neg/i21696.check @@ -5,9 +5,9 @@ |--------------------------------------------------------------------------------------------------------------------- | Explanation (enabled by `-explain`) |- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | Referencing `T` inside a quoted expression requires a `scala.quoted.Type[T]` to be in scope. + | Referencing `T` inside a quoted expression requires a `scala.quoted.Type[T]` to be in scope. | Since Scala is subject to erasure at runtime, the type information will be missing during the execution of the code. - | `scala.quoted.Type[T]` is therefore needed to carry `T`'s type information into the quoted code. - | Without an implicit `scala.quoted.Type[T]`, the type `T` cannot be properly referenced within the expression. + | `scala.quoted.Type[T]` is therefore needed to carry `T`'s type information into the quoted code. + | Without an implicit `scala.quoted.Type[T]`, the type `T` cannot be properly referenced within the expression. | To resolve this, ensure that a `scala.quoted.Type[T]` is available, either through a context-bound or explicitly. --------------------------------------------------------------------------------------------------------------------- From 168866d1f92500c1c82b6df6e7cde10610be7a15 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 8 Oct 2024 14:05:45 +0200 Subject: [PATCH 27/35] add failing test: demo transparent inline can not see forwarders at typer At typer, when transparent inline methods are expanded, unroll forwarders are not yet generated. So if we define forwarders for the first time, in the same compilation unit that inlines a call to the forwarder, then it will fail. Perhaps we can detect this and suspend the unit, otherwise it would seem unreasonable complexity to generate forwarders in typer. --- .../add-param-unroll2/a_v1/A.scala | 10 +++++++ .../add-param-unroll2/a_v2/B.scala | 8 ++++++ .../add-param-unroll2/a_v3/A.scala | 17 +++++++++++ .../tasty-compat/add-param-unroll2/build.sbt | 28 +++++++++++++++++++ .../project/DottyInjectedPlugin.scala | 11 ++++++++ sbt-test/tasty-compat/add-param-unroll2/test | 8 ++++++ 6 files changed, 82 insertions(+) create mode 100644 sbt-test/tasty-compat/add-param-unroll2/a_v1/A.scala create mode 100644 sbt-test/tasty-compat/add-param-unroll2/a_v2/B.scala create mode 100644 sbt-test/tasty-compat/add-param-unroll2/a_v3/A.scala create mode 100644 sbt-test/tasty-compat/add-param-unroll2/build.sbt create mode 100644 sbt-test/tasty-compat/add-param-unroll2/project/DottyInjectedPlugin.scala create mode 100644 sbt-test/tasty-compat/add-param-unroll2/test diff --git a/sbt-test/tasty-compat/add-param-unroll2/a_v1/A.scala b/sbt-test/tasty-compat/add-param-unroll2/a_v1/A.scala new file mode 100644 index 000000000000..7ad2eb78214c --- /dev/null +++ b/sbt-test/tasty-compat/add-param-unroll2/a_v1/A.scala @@ -0,0 +1,10 @@ +package a + +import scala.annotation.unroll + +// new project with a single file, first compile via Zinc +object A { + + def foo(s: String, x: Int): String = s + x + +} diff --git a/sbt-test/tasty-compat/add-param-unroll2/a_v2/B.scala b/sbt-test/tasty-compat/add-param-unroll2/a_v2/B.scala new file mode 100644 index 000000000000..7f9168b0798e --- /dev/null +++ b/sbt-test/tasty-compat/add-param-unroll2/a_v2/B.scala @@ -0,0 +1,8 @@ +package b + +import a.* + +// Add a separate file in the same project as A, second compile via Zinc +object B { + transparent inline def caller = A.foo("abc", 2) +} diff --git a/sbt-test/tasty-compat/add-param-unroll2/a_v3/A.scala b/sbt-test/tasty-compat/add-param-unroll2/a_v3/A.scala new file mode 100644 index 000000000000..22c93548de26 --- /dev/null +++ b/sbt-test/tasty-compat/add-param-unroll2/a_v3/A.scala @@ -0,0 +1,17 @@ +package a +import b.* + +import scala.annotation.unroll + +// modify A.scala and add a parameter to foo, and add C, third compile via Zinc +object A { + + def foo(s: String, x: Int, @unroll b: Boolean = true): String = s + x + b + +} + +// C is the same compilation unit as A, and inlines B.caller, so its TASTy will see the forwarder +// where the associated file came from source +object C { + val res = B.caller +} diff --git a/sbt-test/tasty-compat/add-param-unroll2/build.sbt b/sbt-test/tasty-compat/add-param-unroll2/build.sbt new file mode 100644 index 000000000000..4d523f7a7beb --- /dev/null +++ b/sbt-test/tasty-compat/add-param-unroll2/build.sbt @@ -0,0 +1,28 @@ +lazy val commonSettings = Seq( + scalacOptions += "-experimental", +) + +lazy val printSettings = Seq( + scalacOptions += "-Yprint-tasty", +) + +lazy val a_v1 = project.in(file("a_v1")) + .settings(commonSettings) + .settings( + Compile / classDirectory := (ThisBuild / baseDirectory).value / "v2-input" + ) + +lazy val a_v2 = project.in(file("a_v2")) + .settings(commonSettings) + .settings(printSettings) + .settings( + Compile / unmanagedClasspath += (ThisBuild / baseDirectory).value / "v2-input", + Compile / classDirectory := (ThisBuild / baseDirectory).value / "v3-input" + ) + +lazy val a_v3 = project.in(file("a_v3")) + .settings(commonSettings) + .settings( + Compile / unmanagedClasspath += (ThisBuild / baseDirectory).value / "v3-input", + Compile / classDirectory := (ThisBuild / baseDirectory).value / "v3-output" + ) diff --git a/sbt-test/tasty-compat/add-param-unroll2/project/DottyInjectedPlugin.scala b/sbt-test/tasty-compat/add-param-unroll2/project/DottyInjectedPlugin.scala new file mode 100644 index 000000000000..fb946c4b8c61 --- /dev/null +++ b/sbt-test/tasty-compat/add-param-unroll2/project/DottyInjectedPlugin.scala @@ -0,0 +1,11 @@ +import sbt._ +import Keys._ + +object DottyInjectedPlugin extends AutoPlugin { + override def requires = plugins.JvmPlugin + override def trigger = allRequirements + + override val projectSettings = Seq( + scalaVersion := sys.props("plugin.scalaVersion") + ) +} diff --git a/sbt-test/tasty-compat/add-param-unroll2/test b/sbt-test/tasty-compat/add-param-unroll2/test new file mode 100644 index 000000000000..9783ffa9bb6f --- /dev/null +++ b/sbt-test/tasty-compat/add-param-unroll2/test @@ -0,0 +1,8 @@ +# compile library A via Zinc, with an initial file A defining method foo +> a_v1/compile +# compile library A via Zinc, for the second time, adding a new file that defines an inline +# method B.caller, calling A.foo +> a_v2/compile +# compile library A via Zinc, for the third time, add a new parameter to A.foo, using @unroll to generate a forwarder +# in the same file A.scala, define object C that inlines B.caller, it should succeed and resolve the new invisible forwarder +> a_v3/compile From e3a3cb1217f79e716d3c471f96126381933bcab0 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 8 Oct 2024 16:53:53 +0200 Subject: [PATCH 28/35] change test to establish cyclic case, and solution --- sbt-test/tasty-compat/add-param-unroll2/a_v3/A.scala | 8 +++++--- sbt-test/tasty-compat/add-param-unroll2/a_v3_2/A.scala | 10 ++++++++++ sbt-test/tasty-compat/add-param-unroll2/a_v3_2/C.scala | 9 +++++++++ sbt-test/tasty-compat/add-param-unroll2/build.sbt | 10 ++++++++++ sbt-test/tasty-compat/add-param-unroll2/test | 7 +++++-- 5 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 sbt-test/tasty-compat/add-param-unroll2/a_v3_2/A.scala create mode 100644 sbt-test/tasty-compat/add-param-unroll2/a_v3_2/C.scala diff --git a/sbt-test/tasty-compat/add-param-unroll2/a_v3/A.scala b/sbt-test/tasty-compat/add-param-unroll2/a_v3/A.scala index 22c93548de26..64df683f34a9 100644 --- a/sbt-test/tasty-compat/add-param-unroll2/a_v3/A.scala +++ b/sbt-test/tasty-compat/add-param-unroll2/a_v3/A.scala @@ -10,8 +10,10 @@ object A { } -// C is the same compilation unit as A, and inlines B.caller, so its TASTy will see the forwarder -// where the associated file came from source +// C is the same compilation unit as A, and inlines B.caller, so its TASTy will attempt to +// resolve the generated forwarder A.foo, which doesn't exist yet in typer phase. +// issue a compilation error here, suggesting to move C to a separate compilation unit. +// In a_v3_2/C.scala, demonstrate fixing the error by moving C to a separate compilation unit. object C { - val res = B.caller + val res: String = B.caller } diff --git a/sbt-test/tasty-compat/add-param-unroll2/a_v3_2/A.scala b/sbt-test/tasty-compat/add-param-unroll2/a_v3_2/A.scala new file mode 100644 index 000000000000..039676557381 --- /dev/null +++ b/sbt-test/tasty-compat/add-param-unroll2/a_v3_2/A.scala @@ -0,0 +1,10 @@ +package a + +import scala.annotation.unroll + +// modify A.scala and add a parameter to foo, and add C (in another file), third compile via Zinc +object A { + + def foo(s: String, x: Int, @unroll b: Boolean = true): String = s + x + b + +} diff --git a/sbt-test/tasty-compat/add-param-unroll2/a_v3_2/C.scala b/sbt-test/tasty-compat/add-param-unroll2/a_v3_2/C.scala new file mode 100644 index 000000000000..e9238c7edbb6 --- /dev/null +++ b/sbt-test/tasty-compat/add-param-unroll2/a_v3_2/C.scala @@ -0,0 +1,9 @@ +package a +import b.* + +// C is a separate compilation unit to A, and inlines B.caller, so its TASTy will try to resolve +// the generated A.foo forwarder, which will not exist yet in typer phase. +// The unit will suspend, and in the second run, A.foo will be generated, so resolution will succeed. +object C { + val res: String = B.caller +} diff --git a/sbt-test/tasty-compat/add-param-unroll2/build.sbt b/sbt-test/tasty-compat/add-param-unroll2/build.sbt index 4d523f7a7beb..bc61bded9b73 100644 --- a/sbt-test/tasty-compat/add-param-unroll2/build.sbt +++ b/sbt-test/tasty-compat/add-param-unroll2/build.sbt @@ -4,6 +4,7 @@ lazy val commonSettings = Seq( lazy val printSettings = Seq( scalacOptions += "-Yprint-tasty", + scalacOptions += "-Ydebug-error" ) lazy val a_v1 = project.in(file("a_v1")) @@ -22,7 +23,16 @@ lazy val a_v2 = project.in(file("a_v2")) lazy val a_v3 = project.in(file("a_v3")) .settings(commonSettings) + .settings(printSettings) .settings( Compile / unmanagedClasspath += (ThisBuild / baseDirectory).value / "v3-input", Compile / classDirectory := (ThisBuild / baseDirectory).value / "v3-output" ) + +lazy val a_v3_2 = project.in(file("a_v3_2")) + .settings(commonSettings) + .settings(printSettings) + .settings( + Compile / unmanagedClasspath += (ThisBuild / baseDirectory).value / "v3-input", + Compile / classDirectory := (ThisBuild / baseDirectory).value / "v3_2-output" + ) diff --git a/sbt-test/tasty-compat/add-param-unroll2/test b/sbt-test/tasty-compat/add-param-unroll2/test index 9783ffa9bb6f..79116f6a1464 100644 --- a/sbt-test/tasty-compat/add-param-unroll2/test +++ b/sbt-test/tasty-compat/add-param-unroll2/test @@ -4,5 +4,8 @@ # method B.caller, calling A.foo > a_v2/compile # compile library A via Zinc, for the third time, add a new parameter to A.foo, using @unroll to generate a forwarder -# in the same file A.scala, define object C that inlines B.caller, it should succeed and resolve the new invisible forwarder -> a_v3/compile +# in the same file A.scala, define object C that inlines B.caller, it should fail to resolve the new invisible forwarder, as it is not visible yet +-> a_v3/compile +# compile library A via Zinc, for the third time (alternative scenario), add a new parameter to A.foo, using @unroll to generate a forwarder +# in a new file C.scala, define object C that inlines B.caller, it should suspend the unit and then resolve the new invisible forwarder +> a_v3_2/compile From 35bbd61cd0fc2fb7559f6051aa7949791ee33bc5 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Tue, 8 Oct 2024 16:58:15 +0200 Subject: [PATCH 29/35] detect changed selectIn: error when same file, or suspend otherwise --- .../dotty/tools/dotc/core/Annotations.scala | 7 ++++- .../tools/dotc/core/tasty/TreeUnpickler.scala | 30 +++++++++---------- .../dotty/tools/dotc/inlines/Inlines.scala | 15 ++++++++-- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index d7f50d4638ab..b67a194a72b6 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -177,8 +177,13 @@ object Annotations { assert(myTree != null) myTree match { case treeFn: (Context ?=> Tree) @unchecked => + var result: Tree | Null = null myTree = null - myTree = atPhaseBeforeTransforms(treeFn) + try + result = atPhaseBeforeTransforms(treeFn) + myTree = result + finally if result == null then + myTree = ctx ?=> treeFn(using ctx) // reset, if unit is suspended then it will re-enter this annotation case _ => } myTree.asInstanceOf[Tree] diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index 284e23822ce4..ce28a255c638 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -170,11 +170,13 @@ class TreeUnpickler(reader: TastyReader, case ex: Exception => fail(ex) } - class TreeReader(val reader: TastyReader) { + class TreeReader(val reader: TastyReader, inInlineBody: Boolean = false) { import reader.* - def forkAt(start: Addr): TreeReader = new TreeReader(subReader(start, endAddr)) - def fork: TreeReader = forkAt(currentAddr) + def forkAt(start: Addr, inInlineBody: Boolean = false): TreeReader = + new TreeReader(subReader(start, endAddr), inInlineBody) + + def fork: TreeReader = forkAt(currentAddr, inInlineBody) def skipParentTree(tag: Int): Unit = { if tag == SPLITCLAUSE then () @@ -694,7 +696,7 @@ class TreeUnpickler(reader: TastyReader, val ctx1 = localContext(sym)(using ctx0).addMode(Mode.ReadPositions) inContext(sourceChangeContext(Addr(0))(using ctx1)) { // avoids space leaks by not capturing the current context - forkAt(rhsStart).readTree() + forkAt(rhsStart, inInlineBody = true).readTree() } }) goto(start) @@ -1581,21 +1583,14 @@ class TreeUnpickler(reader: TastyReader, val d = ownerTpe.decl(name).atSignature(sig, target) (if !d.exists then lookupInSuper else d).asSeenFrom(prefix) - val denot0 = inContext(ctx.addMode(Mode.ResolveFromTASTy)): + val denot = inContext(ctx.addMode(Mode.ResolveFromTASTy)): searchDenot // able to resolve SourceInvisible members - val denot = - if - denot0.symbol.exists - && denot0.symbol.is(SourceInvisible) - && denot0.symbol.isDefinedInSource - then - searchDenot // fallback - else - denot0 - - makeSelect(qual, name, denot) + val sel = makeSelect(qual, name, denot) + if denot == NoDenotation && inInlineBody && sel.denot.symbol.exists && sel.symbol.isDefinedInCurrentRun then + throw new ChangedMethodDenot(sel.denot.symbol) + sel case REPEATED => val elemtpt = readTpt() SeqLiteral(until(end)(readTree()), elemtpt) @@ -1902,6 +1897,9 @@ class TreeUnpickler(reader: TastyReader, object TreeUnpickler { + /** Specifically thrown when a SELECTin was written to TASTy, i.e. is expected to resolve, and then doesn't. */ + private[dotc] final class ChangedMethodDenot(val resolved: Symbol) extends Exception + /** Define the expected format of the tasty bytes * - TopLevel: Tasty that contains a full class nested in its package * - Term: Tasty that contains only a term tree diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index aeecd9c376e3..a09874ff56ee 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -19,6 +19,7 @@ import staging.StagingLevel import collection.mutable import reporting.{NotConstant, trace} import util.Spans.Span +import dotty.tools.dotc.core.tasty.TreeUnpickler /** Support for querying inlineable methods and for inlining calls to such methods */ object Inlines: @@ -158,8 +159,18 @@ object Inlines: else if enclosingInlineds.length < ctx.settings.XmaxInlines.value && !reachedInlinedTreesLimit then val body = try bodyToInline(tree.symbol) // can typecheck the tree and thereby produce errors - catch case _: MissingInlineInfo => - throw CyclicReference(ctx.owner) + catch + case _: MissingInlineInfo => throw CyclicReference(ctx.owner) + case err: TreeUnpickler.ChangedMethodDenot => + // tested in sbt-test/tasty-compat/add-param-unroll2/a_v3/A.scala + if err.resolved.source == ctx.source then + report.error(em"""cannot inline ${tree.symbol}: + | The definition of ${err.resolved.showLocated}, defined in the current file, has changed incompatibly. + | Try inlining from a different file.""", tree.srcPos) + EmptyTree + else + // Tested in sbt-test/tasty-compat/add-param-unroll2/a_v3_2/C.scala + ctx.compilationUnit.suspend("suspending in case of possible generated methods") new InlineCall(tree).expand(body) else ctx.base.stopInlining = true From 4ddadadc00ad135d1be958558478433cf1996112 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 10 Oct 2024 22:40:36 +0200 Subject: [PATCH 30/35] Merge SourceInvisible and Invisible flags --- compiler/src/dotty/tools/dotc/core/Denotations.scala | 2 +- compiler/src/dotty/tools/dotc/core/Flags.scala | 7 ++----- compiler/src/dotty/tools/dotc/core/NamerOps.scala | 2 +- compiler/src/dotty/tools/dotc/core/SymDenotations.scala | 2 +- compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala | 1 - .../src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala | 3 +-- .../src/dotty/tools/dotc/transform/UnrollDefinitions.scala | 4 ++-- compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala | 1 - library/src/scala/quoted/Quotes.scala | 4 ---- tasty/src/dotty/tools/tasty/TastyFormat.scala | 6 +----- .../stdlibExperimentalDefinitions.scala | 2 +- 11 files changed, 10 insertions(+), 24 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index cefc6c4d4cb3..e2ef3f7eafb8 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -1072,7 +1072,7 @@ object Denotations { def filterDisjoint(denots: PreDenotation)(using Context): SingleDenotation = if (denots.exists && denots.matches(this)) NoDenotation else this def filterWithFlags(required: FlagSet, excluded: FlagSet)(using Context): SingleDenotation = - val realExcluded = if ctx.isAfterTyper then excluded else excluded | Invisible | (if ctx.mode.is(Mode.ResolveFromTASTy) then EmptyFlags else SourceInvisible) + val realExcluded = if ctx.isAfterTyper then excluded else excluded | (if ctx.mode.is(Mode.ResolveFromTASTy) then EmptyFlags else Invisible) def symd: SymDenotation = this match case symd: SymDenotation => symd case _ => symbol.denot diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 9981db87bc96..0775b3caaf0c 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -380,9 +380,6 @@ object Flags { /** Tracked modifier for class parameter / a class with some tracked parameters */ val (Tracked @ _, _, Dependent @ _) = newFlags(46, "tracked") - /** Symbol can not be resolved from source during typer. PROVISIONAL (possibly replace with `Invisible` with new semantics) */ - val (SourceInvisible @ _, _, _) = newFlags(47, "") - // ------------ Flags following this one are not pickled ---------------------------------- /** Symbol is not a member of its owner */ @@ -474,7 +471,7 @@ object Flags { Scala2SpecialFlags, MutableOrOpen, Opaque, Touched, JavaStatic, OuterOrCovariant, LabelOrContravariant, CaseAccessor, Tracked, Extension, NonMember, Implicit, Given, Permanent, Synthetic, Exported, - SuperParamAliasOrScala2x, Inline, Macro, ConstructorProxy, Invisible, SourceInvisible) + SuperParamAliasOrScala2x, Inline, Macro, ConstructorProxy, Invisible) /** Flags that are not (re)set when completing the denotation, or, if symbol is * a top-level class or object, when completing the denotation once the class @@ -528,7 +525,7 @@ object Flags { val RetainedModuleValAndClassFlags: FlagSet = AccessFlags | Package | Case | Synthetic | JavaDefined | JavaStatic | Artifact | - Lifted | MixedIn | Specialized | ConstructorProxy | Invisible | SourceInvisible | Erased + Lifted | MixedIn | Specialized | ConstructorProxy | Invisible | Erased /** Flags that can apply to a module val */ val RetainedModuleValFlags: FlagSet = RetainedModuleValAndClassFlags | diff --git a/compiler/src/dotty/tools/dotc/core/NamerOps.scala b/compiler/src/dotty/tools/dotc/core/NamerOps.scala index c1962d9fc557..363a01665564 100644 --- a/compiler/src/dotty/tools/dotc/core/NamerOps.scala +++ b/compiler/src/dotty/tools/dotc/core/NamerOps.scala @@ -110,7 +110,7 @@ object NamerOps: else NoSymbol.assertingErrorsReported(em"no companion $name in $scope") /** If a class has one of these flags, it does not get a constructor companion */ - private val NoConstructorProxyNeededFlags = Abstract | Trait | Case | Synthetic | Module | Invisible | SourceInvisible + private val NoConstructorProxyNeededFlags = Abstract | Trait | Case | Synthetic | Module | Invisible /** The flags of a constructor companion */ private val ConstructorCompanionFlags = Synthetic | ConstructorProxy diff --git a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 2c4ee4e67005..e0dfcfd78c3b 100644 --- a/compiler/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala @@ -617,7 +617,7 @@ object SymDenotations { case _ => // Otherwise, no completion is necessary, see the preconditions of `markAbsent()`. (myInfo `eq` NoType) - || (is(Invisible) || is(SourceInvisible) && !ctx.mode.is(Mode.ResolveFromTASTy)) && ctx.isTyper + || (is(Invisible) && !ctx.mode.is(Mode.ResolveFromTASTy)) && ctx.isTyper || is(ModuleVal, butNot = Package) && moduleClass.isAbsent(canForce) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 8befe2bf3cf0..6822fcc8e548 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -881,7 +881,6 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) { if flags.is(Transparent) then writeModTag(TRANSPARENT) if flags.is(Infix) then writeModTag(INFIX) if flags.is(Invisible) then writeModTag(INVISIBLE) - if flags.is(SourceInvisible) then writeModTag(SOURCEINVISIBLE) if (flags.is(Erased)) writeModTag(ERASED) if (flags.is(Exported)) writeModTag(EXPORTED) if (flags.is(Given)) writeModTag(GIVEN) diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index ce28a255c638..e5b3e012db7f 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -756,7 +756,6 @@ class TreeUnpickler(reader: TastyReader, case EXPORTED => addFlag(Exported) case OPEN => addFlag(Open) case INVISIBLE => addFlag(Invisible) - case SOURCEINVISIBLE => addFlag(SourceInvisible) case TRANSPARENT => addFlag(Transparent) case INFIX => addFlag(Infix) case TRACKED => addFlag(Tracked) @@ -1584,7 +1583,7 @@ class TreeUnpickler(reader: TastyReader, (if !d.exists then lookupInSuper else d).asSeenFrom(prefix) val denot = inContext(ctx.addMode(Mode.ResolveFromTASTy)): - searchDenot // able to resolve SourceInvisible members + searchDenot // able to resolve Invisible members val sel = makeSelect(qual, name, denot) diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala index e36a5e875787..9cbaf7738533 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala @@ -26,7 +26,7 @@ import dotty.tools.unreachable /**Implementation of SIP-61. * Runs when `@unroll` annotations are found in a compilation unit, installing new definitions * - * Note that it only generates `SourceInvisible` methods, so no interactions with Zinc/SemanticDB + * Note that it only generates `Invisible` methods, so no interactions with Zinc/SemanticDB */ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { self => @@ -127,7 +127,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { defdef.symbol.owner, defdef.name, defdef.symbol.flags &~ HasDefaultParams | - SourceInvisible | Synthetic, + Invisible | Synthetic, NoType, // fill in later coord = nextSymbol.span.shift(1) // shift by 1 to avoid "secondary constructor must call preceding" error ).entered diff --git a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala index 5c2d3b4035ef..22be293c3562 100644 --- a/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala +++ b/compiler/src/scala/quoted/runtime/impl/QuotesImpl.scala @@ -2966,7 +2966,6 @@ class QuotesImpl private (using val ctx: Context) extends Quotes, QuoteUnpickler def Infix: Flags = dotc.core.Flags.Infix def Inline: Flags = dotc.core.Flags.Inline def Invisible: Flags = dotc.core.Flags.Invisible - def SourceInvisible: Flags = dotc.core.Flags.SourceInvisible def JavaDefined: Flags = dotc.core.Flags.JavaDefined def JavaStatic: Flags = dotc.core.Flags.JavaStatic def JavaAnnotation: Flags = dotc.core.Flags.JavaAnnotation diff --git a/library/src/scala/quoted/Quotes.scala b/library/src/scala/quoted/Quotes.scala index 897a0f629ebf..7a98d6f6f761 100644 --- a/library/src/scala/quoted/Quotes.scala +++ b/library/src/scala/quoted/Quotes.scala @@ -4624,10 +4624,6 @@ trait Quotes { self: runtime.QuoteUnpickler & runtime.QuoteMatching => /** Is this symbol invisible when typechecking? */ def Invisible: Flags - /** Is this symbol invisible when typechecking? (only from source) */ - @experimental - def SourceInvisible: Flags - /** Is this symbol defined in a Java class */ def JavaDefined: Flags diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index c49739ad3a7f..de3700c667a4 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -227,8 +227,7 @@ Standard-Section: "ASTs" TopLevelStat* PARAMalias -- Parameter is alias of a superclass parameter EXPORTED -- An export forwarder OPEN -- an open class - INVISIBLE -- invisible during typechecking - SOURCEINVISIBLE -- invisible in the source code + INVISIBLE -- invisible during typechecking, except when resolving from TASTy TRACKED -- a tracked class parameter / a dependent class Annotation @@ -512,7 +511,6 @@ object TastyFormat { final val EMPTYCLAUSE = 45 final val SPLITCLAUSE = 46 final val TRACKED = 47 - final val SOURCEINVISIBLE = 48 // Tree Cat. 2: tag Nat final val firstNatTreeTag = SHAREDterm @@ -702,7 +700,6 @@ object TastyFormat { | EXPORTED | OPEN | INVISIBLE - | SOURCEINVISIBLE | ANNOTATION | PRIVATEqualified | PROTECTEDqualified @@ -767,7 +764,6 @@ object TastyFormat { case EXPORTED => "EXPORTED" case OPEN => "OPEN" case INVISIBLE => "INVISIBLE" - case SOURCEINVISIBLE => "SOURCEINVISIBLE" case PARAMalias => "PARAMalias" case EMPTYCLAUSE => "EMPTYCLAUSE" case SPLITCLAUSE => "SPLITCLAUSE" diff --git a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala index 9e6df7f9525c..2c4ee5401f92 100644 --- a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala +++ b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala @@ -96,7 +96,7 @@ val experimentalDefinitionInLibrary = Set( "scala.Predef$.runtimeChecked", "scala.annotation.internal.RuntimeChecked", // New feature: SIP 61 - @unroll annotation - "scala.annotation.unroll", "scala.quoted.Quotes.reflectModule.FlagsModule.SourceInvisible" + "scala.annotation.unroll" ) From 7b1970c2d4c2a9e5eb9e1dfc64b6509b06a01dad Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 13 Nov 2024 08:21:25 +0100 Subject: [PATCH 31/35] address review: part 1 --- .../dotty/tools/dotc/CompilationUnit.scala | 4 +- .../dotty/tools/dotc/core/Denotations.scala | 4 +- .../tools/dotc/core/tasty/TreePickler.scala | 12 +- .../dotty/tools/dotc/reporting/messages.scala | 2 +- .../tools/dotc/transform/PostTyper.scala | 18 +-- .../dotc/transform/UnrollDefinitions.scala | 123 ++++++++++-------- tests/neg/unroll-abstractMethod.check | 4 +- tests/neg/unroll-duped.check | 6 +- tests/neg/unroll-illegal3.check | 6 +- tests/neg/unroll-traitConstructor.check | 2 +- 10 files changed, 89 insertions(+), 92 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index b7171bb28853..ea7f0d669449 100644 --- a/compiler/src/dotty/tools/dotc/CompilationUnit.scala +++ b/compiler/src/dotty/tools/dotc/CompilationUnit.scala @@ -59,8 +59,8 @@ class CompilationUnit protected (val source: SourceFile, val info: CompilationUn var hasMacroAnnotations: Boolean = false - def hasUnrollDefs: Boolean = unrolledClasses != null - var unrolledClasses: Set[Symbol] | Null = null + def hasUnrollDefs: Boolean = unrolledClasses.nonEmpty + var unrolledClasses: Set[Symbol] = Set.empty /** Set to `true` if inliner added anonymous mirrors that need to be completed */ var needsMirrorSupport: Boolean = false diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index e2ef3f7eafb8..4d7ff138a2e8 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -1072,7 +1072,9 @@ object Denotations { def filterDisjoint(denots: PreDenotation)(using Context): SingleDenotation = if (denots.exists && denots.matches(this)) NoDenotation else this def filterWithFlags(required: FlagSet, excluded: FlagSet)(using Context): SingleDenotation = - val realExcluded = if ctx.isAfterTyper then excluded else excluded | (if ctx.mode.is(Mode.ResolveFromTASTy) then EmptyFlags else Invisible) + val realExcluded = + if ctx.isAfterTyper || ctx.mode.is(Mode.ResolveFromTASTy) then excluded + else excluded | Invisible def symd: SymDenotation = this match case symd: SymDenotation => symd case _ => symbol.denot diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala index 6822fcc8e548..7b80c7c80a21 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreePickler.scala @@ -20,7 +20,6 @@ import collection.mutable import reporting.{Profile, NoProfile} import dotty.tools.tasty.TastyFormat.ASTsSection import quoted.QuotePatterns -import dotty.tools.dotc.config.Feature object TreePickler: class StackSizeExceeded(val mdef: tpd.MemberDef) extends Exception @@ -475,16 +474,15 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) { case _ => if passesConditionForErroringBestEffortCode(tree.hasType) then // #19951 The signature of a constructor of a Java annotation is irrelevant - val sym = tree.symbol val sig = - if name == nme.CONSTRUCTOR && sym.exists && sym.owner.is(JavaAnnotation) then Signature.NotAMethod + if name == nme.CONSTRUCTOR && tree.symbol.exists && tree.symbol.owner.is(JavaAnnotation) then Signature.NotAMethod else tree.tpe.signature - var ename = sym.targetName + var ename = tree.symbol.targetName val selectFromQualifier = name.isTypeName || qual.isInstanceOf[Hole] // holes have no symbol || sig == Signature.NotAMethod // no overload resolution necessary - || !sym.exists // polymorphic function type + || !tree.denot.symbol.exists // polymorphic function type || tree.denot.asSingleDenotation.isRefinedMethod // refined methods have no defining class symbol if selectFromQualifier then writeByte(if name.isTypeName then SELECTtpt else SELECT) @@ -493,9 +491,9 @@ class TreePickler(pickler: TastyPickler, attributes: Attributes) { else // select from owner writeByte(SELECTin) withLength { - pickleNameAndSig(name, sym.signature, ename) + pickleNameAndSig(name, tree.symbol.signature, ename) pickleTree(qual) - pickleType(sym.owner.typeRef) + pickleType(tree.symbol.owner.typeRef) } else writeByte(if name.isTypeName then SELECTtpt else SELECT) diff --git a/compiler/src/dotty/tools/dotc/reporting/messages.scala b/compiler/src/dotty/tools/dotc/reporting/messages.scala index a733afb68f2d..fc6ca194f4c4 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -3412,7 +3412,7 @@ extends DeclarationMsg(IllegalUnrollPlacementID): case Some(method) => val isCtor = method.isConstructor def what = if isCtor then i"a ${if method.owner.is(Trait) then "trait" else "class"} constructor" else i"method ${method.name}" - val prefix = s"Can not unroll parameters of $what" + val prefix = s"Cannot unroll parameters of $what" if method.is(Deferred) then i"$prefix: it must not be abstract" else if isCtor && method.owner.is(Trait) then diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index cc7595442ddb..02f5434aa549 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -120,20 +120,12 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => private var inJavaAnnot: Boolean = false - private var seenUnrolledMethods: util.EqHashMap[Symbol, Boolean] | Null = null + private val seenUnrolledMethods: util.EqHashMap[Symbol, Boolean] = new util.EqHashMap[Symbol, Boolean] private var noCheckNews: Set[New] = Set() def isValidUnrolledMethod(method: Symbol, origin: SrcPos)(using Context): Boolean = - val seenMethods = - val local = seenUnrolledMethods - if local == null then - val map = new util.EqHashMap[Symbol, Boolean] - seenUnrolledMethods = map - map - else - local - seenMethods.getOrElseUpdate(method, { + seenUnrolledMethods.getOrElseUpdate(method, { val isCtor = method.isConstructor if method.name.is(DefaultGetterName) @@ -234,12 +226,8 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => private def registerIfUnrolledParam(sym: Symbol)(using Context): Unit = if sym.hasAnnotation(defn.UnrollAnnot) && isValidUnrolledMethod(sym.owner, sym.sourcePos) then val cls = sym.enclosingClass - val classes = ctx.compilationUnit.unrolledClasses val additions = Array(cls, cls.linkedClass).filter(_ != NoSymbol) - if classes == null then - ctx.compilationUnit.unrolledClasses = Set.from(additions) - else - ctx.compilationUnit.unrolledClasses = classes ++ additions + ctx.compilationUnit.unrolledClasses ++= additions private def processValOrDefDef(tree: Tree)(using Context): tree.type = val sym = tree.symbol diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala index 9cbaf7738533..2556dab185d1 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala @@ -22,6 +22,7 @@ import scala.collection.mutable import scala.util.boundary, boundary.break import dotty.tools.dotc.core.StdNames.nme import dotty.tools.unreachable +import dotty.tools.dotc.util.Spans.Span /**Implementation of SIP-61. * Runs when `@unroll` annotations are found in a compilation unit, installing new definitions @@ -33,16 +34,10 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { import tpd.* - private var _unrolledDefs: util.EqHashMap[Symbol, ComputedIndicies] | Null = null - private def initializeUnrolledDefs(): util.EqHashMap[Symbol, ComputedIndicies] = - val local = _unrolledDefs - if local == null then - val map = new util.EqHashMap[Symbol, ComputedIndicies] - _unrolledDefs = map - map - else - local.clear() - local + private val _unrolledDefs: util.EqHashMap[Symbol, ComputedIndices] = new util.EqHashMap[Symbol, ComputedIndices] + private def initializeUnrolledDefs(): util.EqHashMap[Symbol, ComputedIndices] = + _unrolledDefs.clear() + _unrolledDefs override def phaseName: String = UnrollDefinitions.name @@ -55,15 +50,15 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { super.run // create and run the transformer on the current compilation unit def newTransformer(using Context): Transformer = - UnrollingTransformer(ctx.compilationUnit.unrolledClasses.nn) + UnrollingTransformer(ctx.compilationUnit.unrolledClasses) - type ComputedIndicies = List[(Int, List[Int])] - type ComputeIndicies = Context ?=> Symbol => ComputedIndicies + type ComputedIndices = List[(Int, List[Int])] + type ComputeIndices = Context ?=> Symbol => ComputedIndices - private class UnrollingTransformer(classes: Set[Symbol]) extends Transformer { + private class UnrollingTransformer(unrolledClasses: Set[Symbol]) extends Transformer { private val unrolledDefs = initializeUnrolledDefs() - def computeIndices(annotated: Symbol)(using Context): ComputedIndicies = + def computeIndices(annotated: Symbol)(using Context): ComputedIndices = unrolledDefs.getOrElseUpdate(annotated, { if annotated.name.is(DefaultGetterName) then Nil // happens in curried methods where more than one parameter list has @unroll @@ -84,17 +79,17 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { end computeIndices override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match - case tree @ TypeDef(_, impl: Template) if classes(tree.symbol) => + case tree @ TypeDef(_, impl: Template) if unrolledClasses(tree.symbol) => super.transform(cpy.TypeDef(tree)(rhs = unrollTemplate(impl, computeIndices))) case tree => super.transform(tree) } - def copyParamSym(sym: Symbol, parent: Symbol)(using Context): (Symbol, Symbol) = + private def copyParamSym(sym: Symbol, parent: Symbol)(using Context): (Symbol, Symbol) = val copied = sym.copy(owner = parent, flags = (sym.flags &~ HasDefault), coord = sym.coord) sym -> copied - def symLocation(sym: Symbol)(using Context) = { + private def symLocation(sym: Symbol)(using Context) = { val lineDesc = if (sym.span.exists && sym.span != sym.owner.span) s" at line ${sym.srcPos.line + 1}" @@ -102,7 +97,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { i"in ${sym.owner}${lineDesc}" } - def findUnrollAnnotations(params: List[Symbol])(using Context): List[Int] = { + private def findUnrollAnnotations(params: List[Symbol])(using Context): List[Int] = { params .zipWithIndex .collect { @@ -111,16 +106,25 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { } } - def isTypeClause(p: ParamClause) = p.headOption.exists(_.isInstanceOf[TypeDef]) - - def generateSingleForwarder(defdef: DefDef, - prevMethodType: Type, + private def isTypeClause(p: ParamClause) = p.headOption.exists(_.isInstanceOf[TypeDef]) + + /** Generate a forwarder that calls the next one in a "chain" of forwarders + * + * @param defdef the original unrolled def that the forwarder is derived from + * @param paramIndex index of the unrolled parameter (in the parameter list) that we stop at + * @param paramCount number of parameters in the annotated parameter list + * @param nextParamIndex index of next unrolled parameter - to fetch default argument + * @param nextSpan span of next forwarder - used to ensure the span is not identical by shifting (TODO remove) + * @param annotatedParamListIndex index of the parameter list that contains unrolled parameters + * @param isCaseApply if `defdef` is a case class apply/constructor - used for selection of default arguments + */ + private def generateSingleForwarder(defdef: DefDef, paramIndex: Int, paramCount: Int, nextParamIndex: Int, - nextSymbol: Symbol, + nextSpan: Span, annotatedParamListIndex: Int, - isCaseApply: Boolean)(using Context) = { + isCaseApply: Boolean)(using Context): DefDef = { def initNewForwarder()(using Context): (TermSymbol, List[List[Symbol]]) = { val forwarderDefSymbol0 = Symbols.newSymbol( @@ -129,15 +133,15 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { defdef.symbol.flags &~ HasDefaultParams | Invisible | Synthetic, NoType, // fill in later - coord = nextSymbol.span.shift(1) // shift by 1 to avoid "secondary constructor must call preceding" error + coord = nextSpan.shift(1) // shift by 1 to avoid "secondary constructor must call preceding" error ).entered val newParamSymMappings = extractParamSymss(copyParamSym(_, forwarderDefSymbol0)) val (oldParams, newParams) = newParamSymMappings.flatten.unzip val newParamSymLists0 = - newParamSymMappings.map: pairss => - pairss.map: (oldSym, newSym) => + newParamSymMappings.map: pairs => + pairs.map: (oldSym, newSym) => newSym.info = oldSym.info.substSym(oldParams, newParams) newSym @@ -153,8 +157,6 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { else ps.map(p => onSymbol(p.symbol)) } - val paramCount = defdef.symbol.paramSymss(annotatedParamListIndex).size - val (forwarderDefSymbol, newParamSymLists) = initNewForwarder() def forwarderRhs(): tpd.Tree = { @@ -224,10 +226,10 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { val forwarderDef = tpd.DefDef(forwarderDefSymbol, rhs = forwarderRhs()) - forwarderDef.withSpan(nextSymbol.span.shift(1)) + forwarderDef.withSpan(nextSpan.shift(1)) } - def generateFromProduct(startParamIndices: List[Int], paramCount: Int, defdef: DefDef)(using Context) = { + private def generateFromProduct(startParamIndices: List[Int], paramCount: Int, defdef: DefDef)(using Context) = { cpy.DefDef(defdef)( name = defdef.name, paramss = defdef.paramss, @@ -248,28 +250,35 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { ) ) ) - } ++ Seq( - CaseDef( - Underscore(defn.IntType), - EmptyTree, - defdef.rhs - ) + } :+ CaseDef( + Underscore(defn.IntType), + EmptyTree, + defdef.rhs ) ) ).setDefTree } - def generateSyntheticDefs(tree: Tree, compute: ComputeIndicies)(using Context): Option[(Symbol, Option[Symbol], Seq[DefDef])] = tree match { + private enum Gen: + case Substitute(origin: Symbol, newDef: DefDef) + case Forwarders(origin: Symbol, forwarders: Seq[DefDef]) + + def origin: Symbol + def extras: Seq[DefDef] = this match + case Substitute(_, d) => d :: Nil + case Forwarders(_, ds) => ds + + private def generateSyntheticDefs(tree: Tree, compute: ComputeIndices)(using Context): Option[Gen] = tree match { case defdef: DefDef if defdef.paramss.nonEmpty => import dotty.tools.dotc.core.NameOps.isConstructorName val isCaseCopy = - defdef.name.toString == "copy" && defdef.symbol.owner.is(CaseClass) + defdef.name == nme.copy && defdef.symbol.owner.is(CaseClass) val isCaseApply = - defdef.name.toString == "apply" && defdef.symbol.owner.companionClass.is(CaseClass) + defdef.name == nme.apply && defdef.symbol.owner.companionClass.is(CaseClass) - val isCaseFromProduct = defdef.name.toString == "fromProduct" && defdef.symbol.owner.companionClass.is(CaseClass) + val isCaseFromProduct = defdef.name == nme.fromProduct && defdef.symbol.owner.companionClass.is(CaseClass) val annotated = if (isCaseCopy) defdef.symbol.owner.primaryConstructor @@ -282,25 +291,27 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { case Seq((paramClauseIndex, annotationIndices)) => val paramCount = annotated.paramSymss(paramClauseIndex).size if isCaseFromProduct then - Some((defdef.symbol, Some(defdef.symbol), Seq(generateFromProduct(annotationIndices, paramCount, defdef)))) + Some(Gen.Substitute( + origin = defdef.symbol, + newDef = generateFromProduct(annotationIndices, paramCount, defdef) + )) else val (generatedDefs, _) = val indices = (annotationIndices :+ paramCount).sliding(2).toList.reverse - indices.foldLeft((Seq.empty[DefDef], defdef.symbol)): - case ((defdefs, nextSymbol), Seq(paramIndex, nextParamIndex)) => + indices.foldLeft((Seq.empty[DefDef], defdef.symbol.span)): + case ((defdefs, nextSpan), Seq(paramIndex, nextParamIndex)) => val forwarder = generateSingleForwarder( defdef, - defdef.symbol.info, paramIndex, paramCount, nextParamIndex, - nextSymbol, + nextSpan, paramClauseIndex, isCaseApply ) - (forwarder +: defdefs, forwarder.symbol) + (forwarder +: defdefs, forwarder.symbol.span) case _ => unreachable("sliding with at least 2 elements") - Some((defdef.symbol, None, generatedDefs)) + Some(Gen.Forwarders(origin = defdef.symbol, forwarders = generatedDefs)) case multiple => report.error("Cannot have multiple parameter lists containing `@unroll` annotation", defdef.srcPos) @@ -310,12 +321,12 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { case _ => None } - def unrollTemplate(tmpl: tpd.Template, compute: ComputeIndicies)(using Context): tpd.Tree = { + private def unrollTemplate(tmpl: tpd.Template, compute: ComputeIndices)(using Context): tpd.Tree = { val generatedBody = tmpl.body.flatMap(generateSyntheticDefs(_, compute)) val generatedConstr0 = generateSyntheticDefs(tmpl.constr, compute) val allGenerated = generatedBody ++ generatedConstr0 - val bodySubs = generatedBody.flatMap((_, maybeSub, _) => maybeSub).toSet + val bodySubs = generatedBody.collect({ case s: Gen.Substitute => s.origin }).toSet val otherDecls = tmpl.body.filterNot(d => d.symbol.exists && bodySubs(d.symbol)) /** inlined from compiler/src/dotty/tools/dotc/typer/Checking.scala */ @@ -326,28 +337,26 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { if allGenerated.nonEmpty then val byName = (tmpl.constr :: otherDecls).groupMap(_.symbol.name.toString)(_.symbol) for - (src, _, dcls) <- allGenerated - dcl <- dcls + syntheticDefs <- allGenerated + dcl <- syntheticDefs.extras do val replaced = dcl.symbol byName.get(dcl.name.toString).foreach { syms => val clashes = syms.filter(checkClash(replaced, _)) for existing <- clashes do + val src = syntheticDefs.origin report.error(i"""Unrolled $replaced clashes with existing declaration. |Please remove the clashing definition, or the @unroll annotation. |Unrolled from ${hl(src.showDcl)} ${symLocation(src)}""".stripMargin, existing.srcPos) } end if - val generatedDefs = generatedBody.flatMap((_, _, gens) => gens) - val generatedConstr = generatedConstr0.toList.flatMap((_, _, gens) => gens) - cpy.Template(tmpl)( tmpl.constr, tmpl.parents, tmpl.derived, tmpl.self, - otherDecls ++ generatedDefs ++ generatedConstr + otherDecls ++ allGenerated.flatMap(_.extras) ) } diff --git a/tests/neg/unroll-abstractMethod.check b/tests/neg/unroll-abstractMethod.check index 948c7d1a7862..d0874c8a44d8 100644 --- a/tests/neg/unroll-abstractMethod.check +++ b/tests/neg/unroll-abstractMethod.check @@ -1,8 +1,8 @@ -- [E207] Declaration Error: tests/neg/unroll-abstractMethod.scala:6:41 ------------------------------------------------ 6 | def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String // error | ^ - | Can not unroll parameters of method foo: it must not be abstract + | Cannot unroll parameters of method foo: it must not be abstract -- [E207] Declaration Error: tests/neg/unroll-abstractMethod.scala:10:41 ----------------------------------------------- 10 | def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String // error | ^ - | Can not unroll parameters of method foo: it must not be abstract + | Cannot unroll parameters of method foo: it must not be abstract diff --git a/tests/neg/unroll-duped.check b/tests/neg/unroll-duped.check index 2c1cc80cfee7..9ec09672566b 100644 --- a/tests/neg/unroll-duped.check +++ b/tests/neg/unroll-duped.check @@ -1,12 +1,12 @@ -- [E207] Declaration Error: tests/neg/unroll-duped.scala:11:45 -------------------------------------------------------- 11 | final def copy(s: String = this.s, @unroll y: Boolean = this.y): UnrolledCase = // error | ^ - | Can not unroll parameters of method copy of a case class: please annotate the class constructor instead + | Cannot unroll parameters of method copy of a case class: please annotate the class constructor instead -- [E207] Declaration Error: tests/neg/unroll-duped.scala:18:12 -------------------------------------------------------- 18 | @unroll y: Boolean = true // error | ^ - |Can not unroll parameters of method apply of a case class companion object: please annotate the class constructor instead + |Cannot unroll parameters of method apply of a case class companion object: please annotate the class constructor instead -- [E207] Declaration Error: tests/neg/unroll-duped.scala:22:26 -------------------------------------------------------- 22 | def fromProduct(@unroll p: Product = EmptyTuple): UnrolledCase = { // error | ^ - |Can not unroll parameters of method fromProduct of a case class companion object: please annotate the class constructor instead + |Cannot unroll parameters of method fromProduct of a case class companion object: please annotate the class constructor instead diff --git a/tests/neg/unroll-illegal3.check b/tests/neg/unroll-illegal3.check index 8502b76b50e2..6201a7d815cd 100644 --- a/tests/neg/unroll-illegal3.check +++ b/tests/neg/unroll-illegal3.check @@ -1,12 +1,12 @@ -- [E207] Declaration Error: tests/neg/unroll-illegal3.scala:7:31 ------------------------------------------------------ 7 | def foo(s: String, @unroll y: Boolean) = s + y // error | ^ - | Can not unroll parameters of method foo: it is not final + | Cannot unroll parameters of method foo: it is not final -- [E207] Declaration Error: tests/neg/unroll-illegal3.scala:12:29 ----------------------------------------------------- 12 | def foo(s: String, @unroll y: Boolean) = s + y // error | ^ - | Can not unroll parameters of method foo: it is not final + | Cannot unroll parameters of method foo: it is not final -- [E207] Declaration Error: tests/neg/unroll-illegal3.scala:16:29 ----------------------------------------------------- 16 | def foo(s: String, @unroll y: Boolean): String // error | ^ - | Can not unroll parameters of method foo: it must not be abstract + | Cannot unroll parameters of method foo: it must not be abstract diff --git a/tests/neg/unroll-traitConstructor.check b/tests/neg/unroll-traitConstructor.check index 0a5570667196..9bb74d2559bf 100644 --- a/tests/neg/unroll-traitConstructor.check +++ b/tests/neg/unroll-traitConstructor.check @@ -1,4 +1,4 @@ -- [E207] Declaration Error: tests/neg/unroll-traitConstructor.scala:5:32 ---------------------------------------------- 5 |trait Unroll(a: String, @unroll b: Boolean = true): // error | ^ - | implementation restriction: Can not unroll parameters of a trait constructor + | implementation restriction: Cannot unroll parameters of a trait constructor From c5d05d70b98e120a4ca1a8410d8e84f8ac5a9c7d Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Wed, 13 Nov 2024 19:48:31 +0100 Subject: [PATCH 32/35] address review: part 2 --- .../dotc/transform/UnrollDefinitions.scala | 52 ++++++++----------- .../src/dotty/tools/dotc/typer/Checking.scala | 7 ++- .../src/dotty/tools/dotc/typer/Typer.scala | 4 +- 3 files changed, 30 insertions(+), 33 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala index 2556dab185d1..b431a81afeac 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala @@ -61,7 +61,14 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { def computeIndices(annotated: Symbol)(using Context): ComputedIndices = unrolledDefs.getOrElseUpdate(annotated, { if annotated.name.is(DefaultGetterName) then - Nil // happens in curried methods where more than one parameter list has @unroll + // happens in curried methods, where default argument occurs in parameter list + // after the unrolled parameter list. + // example: + // `final def foo(@unroll y: String = "")(x: Int = 23) = x` + // yields: + // `def foo$default$2(@unroll y: String): Int @uncheckedVariance = 23` + // Perhaps annotations should be preprocessed before they are copied? + Nil else val indices = annotated .paramSymss @@ -114,7 +121,6 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { * @param paramIndex index of the unrolled parameter (in the parameter list) that we stop at * @param paramCount number of parameters in the annotated parameter list * @param nextParamIndex index of next unrolled parameter - to fetch default argument - * @param nextSpan span of next forwarder - used to ensure the span is not identical by shifting (TODO remove) * @param annotatedParamListIndex index of the parameter list that contains unrolled parameters * @param isCaseApply if `defdef` is a case class apply/constructor - used for selection of default arguments */ @@ -122,7 +128,6 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { paramIndex: Int, paramCount: Int, nextParamIndex: Int, - nextSpan: Span, annotatedParamListIndex: Int, isCaseApply: Boolean)(using Context): DefDef = { @@ -133,7 +138,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { defdef.symbol.flags &~ HasDefaultParams | Invisible | Synthetic, NoType, // fill in later - coord = nextSpan.shift(1) // shift by 1 to avoid "secondary constructor must call preceding" error + coord = defdef.span ).entered val newParamSymMappings = extractParamSymss(copyParamSym(_, forwarderDefSymbol0)) @@ -196,9 +201,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { newParamSymLists .take(annotatedParamListIndex) .map(_.map(ref)) - .foldLeft(inner): (lhs, newParams) => - if (newParams.headOption.exists(_.isInstanceOf[TypeTree])) TypeApply(lhs, newParams) - else Apply(lhs, newParams) + .foldLeft(inner)(_.appliedToArgs(_)) ) val forwarderInner: Tree = @@ -210,11 +213,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { else ps.map(ref) } - val forwarderCall0 = forwarderCallArgs.foldLeft[Tree](forwarderInner){ - case (lhs: Tree, newParams) => - if (newParams.headOption.exists(_.isInstanceOf[TypeTree])) TypeApply(lhs, newParams) - else Apply(lhs, newParams) - } + val forwarderCall0 = forwarderCallArgs.foldLeft(forwarderInner)(_.appliedToArgs(_)) val forwarderCall = if (!defdef.symbol.isConstructor) forwarderCall0 @@ -224,9 +223,9 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { } val forwarderDef = - tpd.DefDef(forwarderDefSymbol, rhs = forwarderRhs()) + tpd.DefDef(forwarderDefSymbol, rhs = forwarderRhs()).withSpan(defdef.span) - forwarderDef.withSpan(nextSpan.shift(1)) + forwarderDef } private def generateFromProduct(startParamIndices: List[Int], paramCount: Int, defdef: DefDef)(using Context) = { @@ -261,10 +260,10 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { private enum Gen: case Substitute(origin: Symbol, newDef: DefDef) - case Forwarders(origin: Symbol, forwarders: Seq[DefDef]) + case Forwarders(origin: Symbol, forwarders: List[DefDef]) def origin: Symbol - def extras: Seq[DefDef] = this match + def extras: List[DefDef] = this match case Substitute(_, d) => d :: Nil case Forwarders(_, ds) => ds @@ -288,7 +287,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { compute(annotated) match { case Nil => None - case Seq((paramClauseIndex, annotationIndices)) => + case (paramClauseIndex, annotationIndices) :: Nil => val paramCount = annotated.paramSymss(paramClauseIndex).size if isCaseFromProduct then Some(Gen.Substitute( @@ -296,20 +295,18 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { newDef = generateFromProduct(annotationIndices, paramCount, defdef) )) else - val (generatedDefs, _) = + val generatedDefs = val indices = (annotationIndices :+ paramCount).sliding(2).toList.reverse - indices.foldLeft((Seq.empty[DefDef], defdef.symbol.span)): - case ((defdefs, nextSpan), Seq(paramIndex, nextParamIndex)) => - val forwarder = generateSingleForwarder( + indices.foldLeft(List.empty[DefDef]): + case (defdefs, paramIndex :: nextParamIndex :: Nil) => + generateSingleForwarder( defdef, paramIndex, paramCount, nextParamIndex, - nextSpan, paramClauseIndex, isCaseApply - ) - (forwarder +: defdefs, forwarder.symbol.span) + ) :: defdefs case _ => unreachable("sliding with at least 2 elements") Some(Gen.Forwarders(origin = defdef.symbol, forwarders = generatedDefs)) @@ -329,11 +326,6 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { val bodySubs = generatedBody.collect({ case s: Gen.Substitute => s.origin }).toSet val otherDecls = tmpl.body.filterNot(d => d.symbol.exists && bodySubs(d.symbol)) - /** inlined from compiler/src/dotty/tools/dotc/typer/Checking.scala */ - def checkClash(decl: Symbol, other: Symbol) = - def staticNonStaticPair = decl.isScalaStatic != other.isScalaStatic - decl.matches(other) && !staticNonStaticPair - if allGenerated.nonEmpty then val byName = (tmpl.constr :: otherDecls).groupMap(_.symbol.name.toString)(_.symbol) for @@ -342,7 +334,7 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { do val replaced = dcl.symbol byName.get(dcl.name.toString).foreach { syms => - val clashes = syms.filter(checkClash(replaced, _)) + val clashes = syms.filter(ctx.typer.matchesSameStatic(replaced, _)) for existing <- clashes do val src = syntheticDefs.origin report.error(i"""Unrolled $replaced clashes with existing declaration. diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index e870ffd0fc90..f02dd5056bcf 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -1226,6 +1226,10 @@ trait Checking { /** A hook to exclude selected symbols from double declaration check */ def excludeFromDoubleDeclCheck(sym: Symbol)(using Context): Boolean = false + def matchesSameStatic(decl: Symbol, other: Symbol)(using Context): Boolean = + def staticNonStaticPair = decl.isScalaStatic != other.isScalaStatic + decl.matches(other) && !staticNonStaticPair + /** Check that class does not declare same symbol twice */ def checkNoDoubleDeclaration(cls: Symbol)(using Context): Unit = { val seen = new mutable.HashMap[Name, List[Symbol]].withDefaultValue(Nil) @@ -1237,8 +1241,7 @@ trait Checking { def javaFieldMethodPair = decl.is(JavaDefined) && other.is(JavaDefined) && decl.is(Method) != other.is(Method) - def staticNonStaticPair = decl.isScalaStatic != other.isScalaStatic - if (decl.matches(other) && !javaFieldMethodPair && !staticNonStaticPair) { + if (matchesSameStatic(decl, other) && !javaFieldMethodPair) { def doubleDefError(decl: Symbol, other: Symbol): Unit = if (!decl.info.isErroneous && !other.info.isErroneous) report.error(DoubleDefinition(decl, other, cls), decl.srcPos) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index c941ffe74e18..f6bdc589dea0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -2950,7 +2950,9 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer def checkThisConstrCall(tree: Tree): Unit = tree match case app: Apply if untpd.isSelfConstrCall(app) => - if (sym.span.exists && app.symbol.span.exists && sym.span.start <= app.symbol.span.start) + if !sym.is(Synthetic) + && sym.span.exists && app.symbol.span.exists && sym.span.start <= app.symbol.span.start + then report.error("secondary constructor must call a preceding constructor", app.srcPos) case Block(call :: _, _) => checkThisConstrCall(call) case _ => From 78fbf043bc421f45e70b622178009a7454ceb728 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Thu, 14 Nov 2024 19:12:10 +0100 Subject: [PATCH 33/35] address review: part 3 --- .../dotty/tools/dotc/core/Annotations.scala | 7 +--- .../tools/dotc/core/tasty/TreeUnpickler.scala | 19 +++------- .../dotty/tools/dotc/inlines/Inlines.scala | 15 +------- .../add-param-unroll2/a_v1/A.scala | 10 ----- .../add-param-unroll2/a_v2/B.scala | 8 ---- .../add-param-unroll2/a_v3/A.scala | 19 ---------- .../add-param-unroll2/a_v3_2/A.scala | 10 ----- .../add-param-unroll2/a_v3_2/C.scala | 9 ----- .../tasty-compat/add-param-unroll2/build.sbt | 38 ------------------- .../project/DottyInjectedPlugin.scala | 11 ------ sbt-test/tasty-compat/add-param-unroll2/test | 11 ------ 11 files changed, 8 insertions(+), 149 deletions(-) delete mode 100644 sbt-test/tasty-compat/add-param-unroll2/a_v1/A.scala delete mode 100644 sbt-test/tasty-compat/add-param-unroll2/a_v2/B.scala delete mode 100644 sbt-test/tasty-compat/add-param-unroll2/a_v3/A.scala delete mode 100644 sbt-test/tasty-compat/add-param-unroll2/a_v3_2/A.scala delete mode 100644 sbt-test/tasty-compat/add-param-unroll2/a_v3_2/C.scala delete mode 100644 sbt-test/tasty-compat/add-param-unroll2/build.sbt delete mode 100644 sbt-test/tasty-compat/add-param-unroll2/project/DottyInjectedPlugin.scala delete mode 100644 sbt-test/tasty-compat/add-param-unroll2/test diff --git a/compiler/src/dotty/tools/dotc/core/Annotations.scala b/compiler/src/dotty/tools/dotc/core/Annotations.scala index b67a194a72b6..d7f50d4638ab 100644 --- a/compiler/src/dotty/tools/dotc/core/Annotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Annotations.scala @@ -177,13 +177,8 @@ object Annotations { assert(myTree != null) myTree match { case treeFn: (Context ?=> Tree) @unchecked => - var result: Tree | Null = null myTree = null - try - result = atPhaseBeforeTransforms(treeFn) - myTree = result - finally if result == null then - myTree = ctx ?=> treeFn(using ctx) // reset, if unit is suspended then it will re-enter this annotation + myTree = atPhaseBeforeTransforms(treeFn) case _ => } myTree.asInstanceOf[Tree] diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index e5b3e012db7f..41c8acacde92 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -170,13 +170,11 @@ class TreeUnpickler(reader: TastyReader, case ex: Exception => fail(ex) } - class TreeReader(val reader: TastyReader, inInlineBody: Boolean = false) { + class TreeReader(val reader: TastyReader) { import reader.* - def forkAt(start: Addr, inInlineBody: Boolean = false): TreeReader = - new TreeReader(subReader(start, endAddr), inInlineBody) - - def fork: TreeReader = forkAt(currentAddr, inInlineBody) + def forkAt(start: Addr): TreeReader = new TreeReader(subReader(start, endAddr)) + def fork: TreeReader = forkAt(currentAddr) def skipParentTree(tag: Int): Unit = { if tag == SPLITCLAUSE then () @@ -696,7 +694,7 @@ class TreeUnpickler(reader: TastyReader, val ctx1 = localContext(sym)(using ctx0).addMode(Mode.ReadPositions) inContext(sourceChangeContext(Addr(0))(using ctx1)) { // avoids space leaks by not capturing the current context - forkAt(rhsStart, inInlineBody = true).readTree() + forkAt(rhsStart).readTree() } }) goto(start) @@ -1585,11 +1583,7 @@ class TreeUnpickler(reader: TastyReader, val denot = inContext(ctx.addMode(Mode.ResolveFromTASTy)): searchDenot // able to resolve Invisible members - - val sel = makeSelect(qual, name, denot) - if denot == NoDenotation && inInlineBody && sel.denot.symbol.exists && sel.symbol.isDefinedInCurrentRun then - throw new ChangedMethodDenot(sel.denot.symbol) - sel + makeSelect(qual, name, denot) case REPEATED => val elemtpt = readTpt() SeqLiteral(until(end)(readTree()), elemtpt) @@ -1896,9 +1890,6 @@ class TreeUnpickler(reader: TastyReader, object TreeUnpickler { - /** Specifically thrown when a SELECTin was written to TASTy, i.e. is expected to resolve, and then doesn't. */ - private[dotc] final class ChangedMethodDenot(val resolved: Symbol) extends Exception - /** Define the expected format of the tasty bytes * - TopLevel: Tasty that contains a full class nested in its package * - Term: Tasty that contains only a term tree diff --git a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala index a09874ff56ee..aeecd9c376e3 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inlines.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inlines.scala @@ -19,7 +19,6 @@ import staging.StagingLevel import collection.mutable import reporting.{NotConstant, trace} import util.Spans.Span -import dotty.tools.dotc.core.tasty.TreeUnpickler /** Support for querying inlineable methods and for inlining calls to such methods */ object Inlines: @@ -159,18 +158,8 @@ object Inlines: else if enclosingInlineds.length < ctx.settings.XmaxInlines.value && !reachedInlinedTreesLimit then val body = try bodyToInline(tree.symbol) // can typecheck the tree and thereby produce errors - catch - case _: MissingInlineInfo => throw CyclicReference(ctx.owner) - case err: TreeUnpickler.ChangedMethodDenot => - // tested in sbt-test/tasty-compat/add-param-unroll2/a_v3/A.scala - if err.resolved.source == ctx.source then - report.error(em"""cannot inline ${tree.symbol}: - | The definition of ${err.resolved.showLocated}, defined in the current file, has changed incompatibly. - | Try inlining from a different file.""", tree.srcPos) - EmptyTree - else - // Tested in sbt-test/tasty-compat/add-param-unroll2/a_v3_2/C.scala - ctx.compilationUnit.suspend("suspending in case of possible generated methods") + catch case _: MissingInlineInfo => + throw CyclicReference(ctx.owner) new InlineCall(tree).expand(body) else ctx.base.stopInlining = true diff --git a/sbt-test/tasty-compat/add-param-unroll2/a_v1/A.scala b/sbt-test/tasty-compat/add-param-unroll2/a_v1/A.scala deleted file mode 100644 index 7ad2eb78214c..000000000000 --- a/sbt-test/tasty-compat/add-param-unroll2/a_v1/A.scala +++ /dev/null @@ -1,10 +0,0 @@ -package a - -import scala.annotation.unroll - -// new project with a single file, first compile via Zinc -object A { - - def foo(s: String, x: Int): String = s + x - -} diff --git a/sbt-test/tasty-compat/add-param-unroll2/a_v2/B.scala b/sbt-test/tasty-compat/add-param-unroll2/a_v2/B.scala deleted file mode 100644 index 7f9168b0798e..000000000000 --- a/sbt-test/tasty-compat/add-param-unroll2/a_v2/B.scala +++ /dev/null @@ -1,8 +0,0 @@ -package b - -import a.* - -// Add a separate file in the same project as A, second compile via Zinc -object B { - transparent inline def caller = A.foo("abc", 2) -} diff --git a/sbt-test/tasty-compat/add-param-unroll2/a_v3/A.scala b/sbt-test/tasty-compat/add-param-unroll2/a_v3/A.scala deleted file mode 100644 index 64df683f34a9..000000000000 --- a/sbt-test/tasty-compat/add-param-unroll2/a_v3/A.scala +++ /dev/null @@ -1,19 +0,0 @@ -package a -import b.* - -import scala.annotation.unroll - -// modify A.scala and add a parameter to foo, and add C, third compile via Zinc -object A { - - def foo(s: String, x: Int, @unroll b: Boolean = true): String = s + x + b - -} - -// C is the same compilation unit as A, and inlines B.caller, so its TASTy will attempt to -// resolve the generated forwarder A.foo, which doesn't exist yet in typer phase. -// issue a compilation error here, suggesting to move C to a separate compilation unit. -// In a_v3_2/C.scala, demonstrate fixing the error by moving C to a separate compilation unit. -object C { - val res: String = B.caller -} diff --git a/sbt-test/tasty-compat/add-param-unroll2/a_v3_2/A.scala b/sbt-test/tasty-compat/add-param-unroll2/a_v3_2/A.scala deleted file mode 100644 index 039676557381..000000000000 --- a/sbt-test/tasty-compat/add-param-unroll2/a_v3_2/A.scala +++ /dev/null @@ -1,10 +0,0 @@ -package a - -import scala.annotation.unroll - -// modify A.scala and add a parameter to foo, and add C (in another file), third compile via Zinc -object A { - - def foo(s: String, x: Int, @unroll b: Boolean = true): String = s + x + b - -} diff --git a/sbt-test/tasty-compat/add-param-unroll2/a_v3_2/C.scala b/sbt-test/tasty-compat/add-param-unroll2/a_v3_2/C.scala deleted file mode 100644 index e9238c7edbb6..000000000000 --- a/sbt-test/tasty-compat/add-param-unroll2/a_v3_2/C.scala +++ /dev/null @@ -1,9 +0,0 @@ -package a -import b.* - -// C is a separate compilation unit to A, and inlines B.caller, so its TASTy will try to resolve -// the generated A.foo forwarder, which will not exist yet in typer phase. -// The unit will suspend, and in the second run, A.foo will be generated, so resolution will succeed. -object C { - val res: String = B.caller -} diff --git a/sbt-test/tasty-compat/add-param-unroll2/build.sbt b/sbt-test/tasty-compat/add-param-unroll2/build.sbt deleted file mode 100644 index bc61bded9b73..000000000000 --- a/sbt-test/tasty-compat/add-param-unroll2/build.sbt +++ /dev/null @@ -1,38 +0,0 @@ -lazy val commonSettings = Seq( - scalacOptions += "-experimental", -) - -lazy val printSettings = Seq( - scalacOptions += "-Yprint-tasty", - scalacOptions += "-Ydebug-error" -) - -lazy val a_v1 = project.in(file("a_v1")) - .settings(commonSettings) - .settings( - Compile / classDirectory := (ThisBuild / baseDirectory).value / "v2-input" - ) - -lazy val a_v2 = project.in(file("a_v2")) - .settings(commonSettings) - .settings(printSettings) - .settings( - Compile / unmanagedClasspath += (ThisBuild / baseDirectory).value / "v2-input", - Compile / classDirectory := (ThisBuild / baseDirectory).value / "v3-input" - ) - -lazy val a_v3 = project.in(file("a_v3")) - .settings(commonSettings) - .settings(printSettings) - .settings( - Compile / unmanagedClasspath += (ThisBuild / baseDirectory).value / "v3-input", - Compile / classDirectory := (ThisBuild / baseDirectory).value / "v3-output" - ) - -lazy val a_v3_2 = project.in(file("a_v3_2")) - .settings(commonSettings) - .settings(printSettings) - .settings( - Compile / unmanagedClasspath += (ThisBuild / baseDirectory).value / "v3-input", - Compile / classDirectory := (ThisBuild / baseDirectory).value / "v3_2-output" - ) diff --git a/sbt-test/tasty-compat/add-param-unroll2/project/DottyInjectedPlugin.scala b/sbt-test/tasty-compat/add-param-unroll2/project/DottyInjectedPlugin.scala deleted file mode 100644 index fb946c4b8c61..000000000000 --- a/sbt-test/tasty-compat/add-param-unroll2/project/DottyInjectedPlugin.scala +++ /dev/null @@ -1,11 +0,0 @@ -import sbt._ -import Keys._ - -object DottyInjectedPlugin extends AutoPlugin { - override def requires = plugins.JvmPlugin - override def trigger = allRequirements - - override val projectSettings = Seq( - scalaVersion := sys.props("plugin.scalaVersion") - ) -} diff --git a/sbt-test/tasty-compat/add-param-unroll2/test b/sbt-test/tasty-compat/add-param-unroll2/test deleted file mode 100644 index 79116f6a1464..000000000000 --- a/sbt-test/tasty-compat/add-param-unroll2/test +++ /dev/null @@ -1,11 +0,0 @@ -# compile library A via Zinc, with an initial file A defining method foo -> a_v1/compile -# compile library A via Zinc, for the second time, adding a new file that defines an inline -# method B.caller, calling A.foo -> a_v2/compile -# compile library A via Zinc, for the third time, add a new parameter to A.foo, using @unroll to generate a forwarder -# in the same file A.scala, define object C that inlines B.caller, it should fail to resolve the new invisible forwarder, as it is not visible yet --> a_v3/compile -# compile library A via Zinc, for the third time (alternative scenario), add a new parameter to A.foo, using @unroll to generate a forwarder -# in a new file C.scala, define object C that inlines B.caller, it should suspend the unit and then resolve the new invisible forwarder -> a_v3_2/compile From f3375078521ee374c9c3608cc1a65ae96cb36039 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 15 Nov 2024 19:50:53 +0100 Subject: [PATCH 34/35] test clause interleaving and value class --- tests/neg/unroll-clause-interleaving.check | 7 +++++++ tests/neg/unroll-clause-interleaving.scala | 10 ++++++++++ .../unroll-clause-interleaving/Test_4.scala | 7 +++++++ .../Unrolled_1.scala | 14 ++++++++++++++ .../Unrolled_2.scala | 16 ++++++++++++++++ .../Unrolled_3.scala | 18 ++++++++++++++++++ tests/run/unroll-value-class/Test_4.scala | 7 +++++++ tests/run/unroll-value-class/Unrolled_1.scala | 13 +++++++++++++ tests/run/unroll-value-class/Unrolled_2.scala | 15 +++++++++++++++ tests/run/unroll-value-class/Unrolled_3.scala | 17 +++++++++++++++++ 10 files changed, 124 insertions(+) create mode 100644 tests/neg/unroll-clause-interleaving.check create mode 100644 tests/neg/unroll-clause-interleaving.scala create mode 100644 tests/run/unroll-clause-interleaving/Test_4.scala create mode 100644 tests/run/unroll-clause-interleaving/Unrolled_1.scala create mode 100644 tests/run/unroll-clause-interleaving/Unrolled_2.scala create mode 100644 tests/run/unroll-clause-interleaving/Unrolled_3.scala create mode 100644 tests/run/unroll-value-class/Test_4.scala create mode 100644 tests/run/unroll-value-class/Unrolled_1.scala create mode 100644 tests/run/unroll-value-class/Unrolled_2.scala create mode 100644 tests/run/unroll-value-class/Unrolled_3.scala diff --git a/tests/neg/unroll-clause-interleaving.check b/tests/neg/unroll-clause-interleaving.check new file mode 100644 index 000000000000..eea8a7383e09 --- /dev/null +++ b/tests/neg/unroll-clause-interleaving.check @@ -0,0 +1,7 @@ +-- Error: tests/neg/unroll-clause-interleaving.scala:6:12 -------------------------------------------------------------- +6 | final def foo(@unroll x: Int = 0)[T](// error + | ^ + | Cannot have multiple parameter lists containing `@unroll` annotation +7 | s: T, +8 | @unroll y: Boolean = true, +9 | ): String = "" + x + s + y diff --git a/tests/neg/unroll-clause-interleaving.scala b/tests/neg/unroll-clause-interleaving.scala new file mode 100644 index 000000000000..c40941320db1 --- /dev/null +++ b/tests/neg/unroll-clause-interleaving.scala @@ -0,0 +1,10 @@ +//> using options -experimental + +import scala.annotation.unroll + +class Unrolled { + final def foo(@unroll x: Int = 0)[T](// error + s: T, + @unroll y: Boolean = true, + ): String = "" + x + s + y +} diff --git a/tests/run/unroll-clause-interleaving/Test_4.scala b/tests/run/unroll-clause-interleaving/Test_4.scala new file mode 100644 index 000000000000..4090469bb716 --- /dev/null +++ b/tests/run/unroll-clause-interleaving/Test_4.scala @@ -0,0 +1,7 @@ +//> using options -experimental + +@main def Test: Unit = + val u = Unrolled() + TestV1().test(u) + TestV2().test(u) + TestV3().test(u) diff --git a/tests/run/unroll-clause-interleaving/Unrolled_1.scala b/tests/run/unroll-clause-interleaving/Unrolled_1.scala new file mode 100644 index 000000000000..7954acc15680 --- /dev/null +++ b/tests/run/unroll-clause-interleaving/Unrolled_1.scala @@ -0,0 +1,14 @@ +//> using options -experimental + +import scala.annotation.unroll + +class Unrolled { + final def foo(x: Int)[T]( + s: T, + ): String = "" + x + s +} + +class TestV1 { + def test(u: Unrolled): Unit = + assert(u.foo(0)("foo").startsWith("0foo")) +} diff --git a/tests/run/unroll-clause-interleaving/Unrolled_2.scala b/tests/run/unroll-clause-interleaving/Unrolled_2.scala new file mode 100644 index 000000000000..5adc2bc924ec --- /dev/null +++ b/tests/run/unroll-clause-interleaving/Unrolled_2.scala @@ -0,0 +1,16 @@ +//> using options -experimental + +import scala.annotation.unroll + +class Unrolled { + final def foo(x: Int)[T]( + s: T, + @unroll y: Boolean = true, + ): String = "" + x + s + y +} + +class TestV2 { + def test(u: Unrolled): Unit = + assert(u.foo(0)("foo").startsWith("0footrue")) + assert(u.foo(0)("foo", false).startsWith("0foofalse")) +} diff --git a/tests/run/unroll-clause-interleaving/Unrolled_3.scala b/tests/run/unroll-clause-interleaving/Unrolled_3.scala new file mode 100644 index 000000000000..e23b9d12843a --- /dev/null +++ b/tests/run/unroll-clause-interleaving/Unrolled_3.scala @@ -0,0 +1,18 @@ +//> using options -experimental + +import scala.annotation.unroll + +class Unrolled { + final def foo(x: Int)[T]( + s: T, + @unroll y: Boolean = true, + @unroll i: Int = 0, + ): String = "" + x + s + y + i +} + +class TestV3 { + def test(u: Unrolled): Unit = + assert(u.foo(0)("foo").startsWith("0footrue0")) + assert(u.foo(0)("foo", false).startsWith("0foofalse0")) + assert(u.foo(0)("foo", false, 1).startsWith("0foofalse1")) +} diff --git a/tests/run/unroll-value-class/Test_4.scala b/tests/run/unroll-value-class/Test_4.scala new file mode 100644 index 000000000000..98d7ff7e3f64 --- /dev/null +++ b/tests/run/unroll-value-class/Test_4.scala @@ -0,0 +1,7 @@ +//> using options -experimental + +@main def Test: Unit = + val u = Unrolled(0) + TestV1().test(u) + TestV2().test(u) + TestV3().test(u) diff --git a/tests/run/unroll-value-class/Unrolled_1.scala b/tests/run/unroll-value-class/Unrolled_1.scala new file mode 100644 index 000000000000..86d12831b78f --- /dev/null +++ b/tests/run/unroll-value-class/Unrolled_1.scala @@ -0,0 +1,13 @@ +//> using options -experimental + +import scala.annotation.unroll + +class Unrolled(val x: Int) extends AnyVal { + final def foo( + s: String, + ): String = "" + x + s +} + +class TestV1: + def test(u: Unrolled): Unit = + assert(u.foo("foo").startsWith("0foo")) diff --git a/tests/run/unroll-value-class/Unrolled_2.scala b/tests/run/unroll-value-class/Unrolled_2.scala new file mode 100644 index 000000000000..1be3f4ee38b6 --- /dev/null +++ b/tests/run/unroll-value-class/Unrolled_2.scala @@ -0,0 +1,15 @@ +//> using options -experimental + +import scala.annotation.unroll + +class Unrolled(val x: Int) extends AnyVal { + final def foo( + s: String, + @unroll y: Boolean = true, + ): String = "" + x + s + y +} + +class TestV2: + def test(u: Unrolled): Unit = + assert(u.foo("foo").startsWith("0footrue")) + assert(u.foo("foo", false).startsWith("0foofalse")) diff --git a/tests/run/unroll-value-class/Unrolled_3.scala b/tests/run/unroll-value-class/Unrolled_3.scala new file mode 100644 index 000000000000..05e5399bbe53 --- /dev/null +++ b/tests/run/unroll-value-class/Unrolled_3.scala @@ -0,0 +1,17 @@ +//> using options -experimental + +import scala.annotation.unroll + +class Unrolled(val x: Int) extends AnyVal { + final def foo( + s: String, + @unroll y: Boolean = true, + @unroll i: Int = 0 + ): String = "" + x + s + y + i +} + +class TestV3: + def test(u: Unrolled): Unit = + assert(u.foo("foo").startsWith("0footrue0")) + assert(u.foo("foo", false).startsWith("0foofalse0")) + assert(u.foo("foo", false, 1).startsWith("0foofalse1")) From ddb9c5a2da7872649ae1c494a54c31fd11d731e7 Mon Sep 17 00:00:00 2001 From: Jamie Thompson Date: Fri, 15 Nov 2024 20:55:10 +0100 Subject: [PATCH 35/35] mechanically copy all sbt-test for unroll still leave tasty-compat/add-param-unroll, as that specifically tests reading from tasty skip reflection in scalajs --- sbt-test/unroll-annot/caseclass/build.sbt | 56 ------------------- .../project/DottyInjectedPlugin.scala | 12 ---- sbt-test/unroll-annot/caseclass/test | 14 ----- sbt-test/unroll-annot/classMethod/build.sbt | 56 ------------------- .../project/DottyInjectedPlugin.scala | 12 ---- sbt-test/unroll-annot/classMethod/test | 14 ----- sbt-test/unroll-annot/curriedMethod/build.sbt | 56 ------------------- .../project/DottyInjectedPlugin.scala | 12 ---- sbt-test/unroll-annot/curriedMethod/test | 14 ----- sbt-test/unroll-annot/genericMethod/build.sbt | 56 ------------------- .../project/DottyInjectedPlugin.scala | 12 ---- sbt-test/unroll-annot/genericMethod/test | 14 ----- .../unroll-annot/methodWithImplicit/build.sbt | 56 ------------------- .../project/DottyInjectedPlugin.scala | 12 ---- sbt-test/unroll-annot/methodWithImplicit/test | 14 ----- .../utils/src/main/scala/TestUtils.scala | 12 ---- sbt-test/unroll-annot/objectMethod/build.sbt | 56 ------------------- .../project/DottyInjectedPlugin.scala | 12 ---- sbt-test/unroll-annot/objectMethod/test | 14 ----- .../utils/src/main/scala/TestUtils.scala | 12 ---- .../unroll-annot/primaryConstructor/build.sbt | 56 ------------------- .../project/DottyInjectedPlugin.scala | 12 ---- sbt-test/unroll-annot/primaryConstructor/test | 14 ----- .../utils/src/main/scala/TestUtils.scala | 12 ---- .../secondParameterList/build.sbt | 56 ------------------- .../project/DottyInjectedPlugin.scala | 12 ---- .../unroll-annot/secondParameterList/test | 14 ----- .../utils/src/main/scala/TestUtils.scala | 12 ---- .../secondaryConstructor/build.sbt | 56 ------------------- .../project/DottyInjectedPlugin.scala | 12 ---- .../unroll-annot/secondaryConstructor/test | 14 ----- .../utils/src/main/scala/TestUtils.scala | 12 ---- sbt-test/unroll-annot/traitMethod/build.sbt | 56 ------------------- .../project/DottyInjectedPlugin.scala | 12 ---- sbt-test/unroll-annot/traitMethod/test | 14 ----- .../utils/src/main/scala/TestUtils.scala | 12 ---- .../TestUtils_1.scala | 1 + .../unroll-caseclass-integration/Test_4.scala | 8 +++ .../UnrollTestMain_1.scala | 1 + .../UnrollTestMain_2.scala | 1 + .../UnrollTestMain_3.scala | 1 + .../UnrollTestPlatformSpecific_3.scala | 4 +- .../UnrollTestScalaSpecific_1.scala | 3 +- .../UnrollTestScalaSpecific_2.scala | 3 +- .../UnrollTestScalaSpecific_3.scala | 3 +- .../Unrolled_1.scala | 1 + .../Unrolled_2.scala | 1 + .../Unrolled_3.scala | 1 + .../TestUtils_1.scala | 1 + .../Test_4.scala | 8 +++ .../UnrollTestMain_1.scala | 1 + .../UnrollTestMain_2.scala | 1 + .../UnrollTestMain_3.scala | 1 + .../UnrollTestPlatformSpecific_3.scala | 4 +- .../Unrolled_1.scala | 1 + .../Unrolled_2.scala | 1 + .../Unrolled_3.scala | 1 + .../TestUtils_1.scala | 1 + .../Test_4.scala | 8 +++ .../UnrollTestMain_1.scala | 1 + .../UnrollTestMain_2.scala | 1 + .../UnrollTestMain_3.scala | 1 + .../UnrollTestPlatformSpecific_3.scala | 4 +- .../Unrolled_1.scala | 1 + .../Unrolled_2.scala | 1 + .../Unrolled_3.scala | 1 + .../TestUtils_1.scala | 1 + .../Test_4.scala | 8 +++ .../UnrollTestMain_1.scala | 1 + .../UnrollTestMain_2.scala | 1 + .../UnrollTestMain_3.scala | 1 + .../UnrollTestPlatformSpecific_3.scala | 4 +- .../Unrolled_1.scala | 1 + .../Unrolled_2.scala | 1 + .../Unrolled_3.scala | 1 + .../TestUtils_1.scala | 13 +++++ .../Test_4.scala | 8 +++ .../UnrollTestMain_1.scala | 1 + .../UnrollTestMain_2.scala | 1 + .../UnrollTestMain_3.scala | 1 + .../UnrollTestPlatformSpecific_3.scala | 6 +- .../Unrolled_1.scala | 1 + .../Unrolled_2.scala | 1 + .../Unrolled_3.scala | 1 + .../TestUtils_1.scala | 13 +++++ .../Test_4.scala | 8 +++ .../UnrollTestMain_1.scala | 1 + .../UnrollTestMain_2.scala | 1 + .../UnrollTestMain_3.scala | 1 + .../UnrollTestPlatformSpecific_3.scala | 4 +- .../Unrolled_1.scala | 1 + .../Unrolled_2.scala | 1 + .../Unrolled_3.scala | 1 + .../TestUtils_1.scala | 13 +++++ .../Test_4.scala | 8 +++ .../UnrollTestMain_1.scala | 1 + .../UnrollTestMain_2.scala | 1 + .../UnrollTestMain_3.scala | 1 + .../UnrollTestPlatformSpecific_3.scala | 4 +- .../Unrolled_1.scala | 1 + .../Unrolled_2.scala | 1 + .../Unrolled_3.scala | 1 + .../TestUtils_1.scala | 13 +++++ .../Test_4.scala | 8 +++ .../UnrollTestMain_1.scala | 1 + .../UnrollTestMain_2.scala | 1 + .../UnrollTestMain_3.scala | 1 + .../UnrollTestPlatformSpecific_3.scala | 4 +- .../Unrolled_1.scala | 1 + .../Unrolled_2.scala | 1 + .../Unrolled_3.scala | 1 + .../TestUtils_1.scala | 13 +++++ .../Test_4.scala | 8 +++ .../UnrollTestMain_1.scala | 1 + .../UnrollTestMain_2.scala | 1 + .../UnrollTestMain_3.scala | 1 + .../UnrollTestPlatformSpecific_3.scala | 4 +- .../Unrolled_1.scala | 1 + .../Unrolled_2.scala | 1 + .../Unrolled_3.scala | 1 + .../TestUtils_1.scala | 13 +++++ .../Test_4.scala | 8 +++ .../UnrollTestMain_1.scala | 1 + .../UnrollTestMain_2.scala | 1 + .../UnrollTestMain_3.scala | 1 + .../UnrollTestPlatformSpecific_3.scala | 4 +- .../Unrolled_1.scala | 1 + .../Unrolled_2.scala | 1 + .../Unrolled_3.scala | 1 + tests/warn/21681.check | 1 - tests/warn/21681b.check | 1 - tests/warn/21681c.check | 1 - tests/warn/21770.check | 1 - 133 files changed, 259 insertions(+), 910 deletions(-) delete mode 100644 sbt-test/unroll-annot/caseclass/build.sbt delete mode 100644 sbt-test/unroll-annot/caseclass/project/DottyInjectedPlugin.scala delete mode 100644 sbt-test/unroll-annot/caseclass/test delete mode 100644 sbt-test/unroll-annot/classMethod/build.sbt delete mode 100644 sbt-test/unroll-annot/classMethod/project/DottyInjectedPlugin.scala delete mode 100644 sbt-test/unroll-annot/classMethod/test delete mode 100644 sbt-test/unroll-annot/curriedMethod/build.sbt delete mode 100644 sbt-test/unroll-annot/curriedMethod/project/DottyInjectedPlugin.scala delete mode 100644 sbt-test/unroll-annot/curriedMethod/test delete mode 100644 sbt-test/unroll-annot/genericMethod/build.sbt delete mode 100644 sbt-test/unroll-annot/genericMethod/project/DottyInjectedPlugin.scala delete mode 100644 sbt-test/unroll-annot/genericMethod/test delete mode 100644 sbt-test/unroll-annot/methodWithImplicit/build.sbt delete mode 100644 sbt-test/unroll-annot/methodWithImplicit/project/DottyInjectedPlugin.scala delete mode 100644 sbt-test/unroll-annot/methodWithImplicit/test delete mode 100644 sbt-test/unroll-annot/methodWithImplicit/utils/src/main/scala/TestUtils.scala delete mode 100644 sbt-test/unroll-annot/objectMethod/build.sbt delete mode 100644 sbt-test/unroll-annot/objectMethod/project/DottyInjectedPlugin.scala delete mode 100644 sbt-test/unroll-annot/objectMethod/test delete mode 100644 sbt-test/unroll-annot/objectMethod/utils/src/main/scala/TestUtils.scala delete mode 100644 sbt-test/unroll-annot/primaryConstructor/build.sbt delete mode 100644 sbt-test/unroll-annot/primaryConstructor/project/DottyInjectedPlugin.scala delete mode 100644 sbt-test/unroll-annot/primaryConstructor/test delete mode 100644 sbt-test/unroll-annot/primaryConstructor/utils/src/main/scala/TestUtils.scala delete mode 100644 sbt-test/unroll-annot/secondParameterList/build.sbt delete mode 100644 sbt-test/unroll-annot/secondParameterList/project/DottyInjectedPlugin.scala delete mode 100644 sbt-test/unroll-annot/secondParameterList/test delete mode 100644 sbt-test/unroll-annot/secondParameterList/utils/src/main/scala/TestUtils.scala delete mode 100644 sbt-test/unroll-annot/secondaryConstructor/build.sbt delete mode 100644 sbt-test/unroll-annot/secondaryConstructor/project/DottyInjectedPlugin.scala delete mode 100644 sbt-test/unroll-annot/secondaryConstructor/test delete mode 100644 sbt-test/unroll-annot/secondaryConstructor/utils/src/main/scala/TestUtils.scala delete mode 100644 sbt-test/unroll-annot/traitMethod/build.sbt delete mode 100644 sbt-test/unroll-annot/traitMethod/project/DottyInjectedPlugin.scala delete mode 100644 sbt-test/unroll-annot/traitMethod/test delete mode 100644 sbt-test/unroll-annot/traitMethod/utils/src/main/scala/TestUtils.scala rename sbt-test/unroll-annot/classMethod/utils/src/main/scala/TestUtils.scala => tests/run/unroll-caseclass-integration/TestUtils_1.scala (91%) create mode 100644 tests/run/unroll-caseclass-integration/Test_4.scala rename sbt-test/unroll-annot/caseclass/v1_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-caseclass-integration/UnrollTestMain_1.scala (95%) rename sbt-test/unroll-annot/caseclass/v2_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-caseclass-integration/UnrollTestMain_2.scala (96%) rename sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-caseclass-integration/UnrollTestMain_3.scala (97%) rename sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala => tests/run/unroll-caseclass-integration/UnrollTestPlatformSpecific_3.scala (94%) rename sbt-test/unroll-annot/caseclass/v1_app/src/main/scala/UnrollTestScalaSpecific.scala => tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_1.scala (78%) rename sbt-test/unroll-annot/caseclass/v2_app/src/main/scala/UnrollTestScalaSpecific.scala => tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_2.scala (79%) rename sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestScalaSpecific.scala => tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_3.scala (80%) rename sbt-test/unroll-annot/caseclass/v1/src/main/scala/Unrolled.scala => tests/run/unroll-caseclass-integration/Unrolled_1.scala (71%) rename sbt-test/unroll-annot/caseclass/v2/src/main/scala/Unrolled.scala => tests/run/unroll-caseclass-integration/Unrolled_2.scala (81%) rename sbt-test/unroll-annot/caseclass/v3/src/main/scala/Unrolled.scala => tests/run/unroll-caseclass-integration/Unrolled_3.scala (84%) rename sbt-test/unroll-annot/curriedMethod/utils/src/main/scala/TestUtils.scala => tests/run/unroll-classMethod-integration/TestUtils_1.scala (91%) create mode 100644 tests/run/unroll-classMethod-integration/Test_4.scala rename sbt-test/unroll-annot/genericMethod/v1_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-classMethod-integration/UnrollTestMain_1.scala (85%) rename sbt-test/unroll-annot/classMethod/v2_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-classMethod-integration/UnrollTestMain_2.scala (91%) rename sbt-test/unroll-annot/genericMethod/v3_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-classMethod-integration/UnrollTestMain_3.scala (93%) rename sbt-test/unroll-annot/classMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala => tests/run/unroll-classMethod-integration/UnrollTestPlatformSpecific_3.scala (94%) rename sbt-test/unroll-annot/classMethod/v1/src/main/scala/Unrolled.scala => tests/run/unroll-classMethod-integration/Unrolled_1.scala (64%) rename sbt-test/unroll-annot/classMethod/v2/src/main/scala/Unrolled.scala => tests/run/unroll-classMethod-integration/Unrolled_2.scala (81%) rename sbt-test/unroll-annot/classMethod/v3/src/main/scala/Unrolled.scala => tests/run/unroll-classMethod-integration/Unrolled_3.scala (84%) rename sbt-test/unroll-annot/genericMethod/utils/src/main/scala/TestUtils.scala => tests/run/unroll-curriedMethod-integration/TestUtils_1.scala (91%) create mode 100644 tests/run/unroll-curriedMethod-integration/Test_4.scala rename sbt-test/unroll-annot/curriedMethod/v1_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-curriedMethod-integration/UnrollTestMain_1.scala (86%) rename sbt-test/unroll-annot/curriedMethod/v2_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-curriedMethod-integration/UnrollTestMain_2.scala (91%) rename sbt-test/unroll-annot/curriedMethod/v3_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-curriedMethod-integration/UnrollTestMain_3.scala (93%) rename sbt-test/unroll-annot/curriedMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala => tests/run/unroll-curriedMethod-integration/UnrollTestPlatformSpecific_3.scala (95%) rename sbt-test/unroll-annot/curriedMethod/v1/src/main/scala/Unrolled.scala => tests/run/unroll-curriedMethod-integration/Unrolled_1.scala (72%) rename sbt-test/unroll-annot/curriedMethod/v2/src/main/scala/Unrolled.scala => tests/run/unroll-curriedMethod-integration/Unrolled_2.scala (84%) rename sbt-test/unroll-annot/curriedMethod/v3/src/main/scala/Unrolled.scala => tests/run/unroll-curriedMethod-integration/Unrolled_3.scala (85%) rename sbt-test/unroll-annot/caseclass/utils/src/main/scala/TestUtils.scala => tests/run/unroll-genericMethod-integration/TestUtils_1.scala (91%) create mode 100644 tests/run/unroll-genericMethod-integration/Test_4.scala rename sbt-test/unroll-annot/classMethod/v1_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-genericMethod-integration/UnrollTestMain_1.scala (85%) rename sbt-test/unroll-annot/genericMethod/v2_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-genericMethod-integration/UnrollTestMain_2.scala (91%) rename sbt-test/unroll-annot/classMethod/v3_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-genericMethod-integration/UnrollTestMain_3.scala (93%) rename sbt-test/unroll-annot/genericMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala => tests/run/unroll-genericMethod-integration/UnrollTestPlatformSpecific_3.scala (93%) rename sbt-test/unroll-annot/genericMethod/v1/src/main/scala/Unrolled.scala => tests/run/unroll-genericMethod-integration/Unrolled_1.scala (67%) rename sbt-test/unroll-annot/genericMethod/v2/src/main/scala/Unrolled.scala => tests/run/unroll-genericMethod-integration/Unrolled_2.scala (82%) rename sbt-test/unroll-annot/genericMethod/v3/src/main/scala/Unrolled.scala => tests/run/unroll-genericMethod-integration/Unrolled_3.scala (84%) create mode 100644 tests/run/unroll-methodWithImplicit-integration/TestUtils_1.scala create mode 100644 tests/run/unroll-methodWithImplicit-integration/Test_4.scala rename sbt-test/unroll-annot/methodWithImplicit/v1_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_1.scala (87%) rename sbt-test/unroll-annot/methodWithImplicit/v2_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_2.scala (92%) rename sbt-test/unroll-annot/methodWithImplicit/v3_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_3.scala (93%) rename sbt-test/unroll-annot/methodWithImplicit/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala => tests/run/unroll-methodWithImplicit-integration/UnrollTestPlatformSpecific_3.scala (94%) rename sbt-test/unroll-annot/methodWithImplicit/v1/src/main/scala/Unrolled.scala => tests/run/unroll-methodWithImplicit-integration/Unrolled_1.scala (74%) rename sbt-test/unroll-annot/methodWithImplicit/v2/src/main/scala/Unrolled.scala => tests/run/unroll-methodWithImplicit-integration/Unrolled_2.scala (84%) rename sbt-test/unroll-annot/methodWithImplicit/v3/src/main/scala/Unrolled.scala => tests/run/unroll-methodWithImplicit-integration/Unrolled_3.scala (86%) create mode 100644 tests/run/unroll-objectMethod-integration/TestUtils_1.scala create mode 100644 tests/run/unroll-objectMethod-integration/Test_4.scala rename sbt-test/unroll-annot/objectMethod/v1_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-objectMethod-integration/UnrollTestMain_1.scala (88%) rename sbt-test/unroll-annot/objectMethod/v2_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-objectMethod-integration/UnrollTestMain_2.scala (90%) rename sbt-test/unroll-annot/objectMethod/v3_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-objectMethod-integration/UnrollTestMain_3.scala (93%) rename sbt-test/unroll-annot/objectMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala => tests/run/unroll-objectMethod-integration/UnrollTestPlatformSpecific_3.scala (97%) rename sbt-test/unroll-annot/objectMethod/v1/src/main/scala/Unrolled.scala => tests/run/unroll-objectMethod-integration/Unrolled_1.scala (70%) rename sbt-test/unroll-annot/objectMethod/v2/src/main/scala/Unrolled.scala => tests/run/unroll-objectMethod-integration/Unrolled_2.scala (81%) rename sbt-test/unroll-annot/objectMethod/v3/src/main/scala/Unrolled.scala => tests/run/unroll-objectMethod-integration/Unrolled_3.scala (84%) create mode 100644 tests/run/unroll-primaryConstructor-integration/TestUtils_1.scala create mode 100644 tests/run/unroll-primaryConstructor-integration/Test_4.scala rename sbt-test/unroll-annot/primaryConstructor/v1_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-primaryConstructor-integration/UnrollTestMain_1.scala (88%) rename sbt-test/unroll-annot/secondaryConstructor/v2_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-primaryConstructor-integration/UnrollTestMain_2.scala (91%) rename sbt-test/unroll-annot/secondaryConstructor/v3_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-primaryConstructor-integration/UnrollTestMain_3.scala (93%) rename sbt-test/unroll-annot/secondaryConstructor/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala => tests/run/unroll-primaryConstructor-integration/UnrollTestPlatformSpecific_3.scala (94%) rename sbt-test/unroll-annot/primaryConstructor/v1/src/main/scala/Unrolled.scala => tests/run/unroll-primaryConstructor-integration/Unrolled_1.scala (70%) rename sbt-test/unroll-annot/primaryConstructor/v2/src/main/scala/Unrolled.scala => tests/run/unroll-primaryConstructor-integration/Unrolled_2.scala (81%) rename sbt-test/unroll-annot/primaryConstructor/v3/src/main/scala/Unrolled.scala => tests/run/unroll-primaryConstructor-integration/Unrolled_3.scala (83%) create mode 100644 tests/run/unroll-secondParameterList-integration/TestUtils_1.scala create mode 100644 tests/run/unroll-secondParameterList-integration/Test_4.scala rename sbt-test/unroll-annot/secondParameterList/v1_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-secondParameterList-integration/UnrollTestMain_1.scala (86%) rename sbt-test/unroll-annot/secondParameterList/v2_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-secondParameterList-integration/UnrollTestMain_2.scala (91%) rename sbt-test/unroll-annot/secondParameterList/v3_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-secondParameterList-integration/UnrollTestMain_3.scala (93%) rename sbt-test/unroll-annot/secondParameterList/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala => tests/run/unroll-secondParameterList-integration/UnrollTestPlatformSpecific_3.scala (95%) rename sbt-test/unroll-annot/secondParameterList/v1/src/main/scala/Unrolled.scala => tests/run/unroll-secondParameterList-integration/Unrolled_1.scala (72%) rename sbt-test/unroll-annot/secondParameterList/v2/src/main/scala/Unrolled.scala => tests/run/unroll-secondParameterList-integration/Unrolled_2.scala (84%) rename sbt-test/unroll-annot/secondParameterList/v3/src/main/scala/Unrolled.scala => tests/run/unroll-secondParameterList-integration/Unrolled_3.scala (85%) create mode 100644 tests/run/unroll-secondaryConstructor-integration/TestUtils_1.scala create mode 100644 tests/run/unroll-secondaryConstructor-integration/Test_4.scala rename sbt-test/unroll-annot/secondaryConstructor/v1_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_1.scala (88%) rename sbt-test/unroll-annot/primaryConstructor/v2_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_2.scala (91%) rename sbt-test/unroll-annot/primaryConstructor/v3_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_3.scala (93%) rename sbt-test/unroll-annot/primaryConstructor/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala => tests/run/unroll-secondaryConstructor-integration/UnrollTestPlatformSpecific_3.scala (94%) rename sbt-test/unroll-annot/secondaryConstructor/v1/src/main/scala/Unrolled.scala => tests/run/unroll-secondaryConstructor-integration/Unrolled_1.scala (78%) rename sbt-test/unroll-annot/secondaryConstructor/v2/src/main/scala/Unrolled.scala => tests/run/unroll-secondaryConstructor-integration/Unrolled_2.scala (85%) rename sbt-test/unroll-annot/secondaryConstructor/v3/src/main/scala/Unrolled.scala => tests/run/unroll-secondaryConstructor-integration/Unrolled_3.scala (86%) create mode 100644 tests/run/unroll-traitMethod-integration/TestUtils_1.scala create mode 100644 tests/run/unroll-traitMethod-integration/Test_4.scala rename sbt-test/unroll-annot/traitMethod/v1_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-traitMethod-integration/UnrollTestMain_1.scala (92%) rename sbt-test/unroll-annot/traitMethod/v2_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-traitMethod-integration/UnrollTestMain_2.scala (94%) rename sbt-test/unroll-annot/traitMethod/v3_app/src/main/scala/UnrollTestMain.scala => tests/run/unroll-traitMethod-integration/UnrollTestMain_3.scala (95%) rename sbt-test/unroll-annot/traitMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala => tests/run/unroll-traitMethod-integration/UnrollTestPlatformSpecific_3.scala (94%) rename sbt-test/unroll-annot/traitMethod/v1/src/main/scala/Unrolled.scala => tests/run/unroll-traitMethod-integration/Unrolled_1.scala (77%) rename sbt-test/unroll-annot/traitMethod/v2/src/main/scala/Unrolled.scala => tests/run/unroll-traitMethod-integration/Unrolled_2.scala (84%) rename sbt-test/unroll-annot/traitMethod/v3/src/main/scala/Unrolled.scala => tests/run/unroll-traitMethod-integration/Unrolled_3.scala (86%) diff --git a/sbt-test/unroll-annot/caseclass/build.sbt b/sbt-test/unroll-annot/caseclass/build.sbt deleted file mode 100644 index fc692a0473a6..000000000000 --- a/sbt-test/unroll-annot/caseclass/build.sbt +++ /dev/null @@ -1,56 +0,0 @@ -lazy val utils = project.in(file("utils")) - -lazy val sharedSettings = Seq( - scalacOptions ++= Seq("-Ycheck:all", "-experimental") -) - -lazy val v1 = project.in(file("v1")) - .settings(sharedSettings) - -lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - Attributed.blank((v1 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v1 / Compile / classDirectory).value) - ), - ) - -lazy val v2 = project.in(file("v2")) - .settings(sharedSettings) - -lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - Attributed.blank((v2 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v2 / Compile / classDirectory).value) - ), - ) - -lazy val v3 = project.in(file("v3")) - .settings(sharedSettings) - -lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - // add v2_app, compiled against v2, to the classpath - Attributed.blank((v2_app / Runtime / classDirectory).value), - Attributed.blank((v3 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v3 / Compile / classDirectory).value) - ), - ) diff --git a/sbt-test/unroll-annot/caseclass/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/caseclass/project/DottyInjectedPlugin.scala deleted file mode 100644 index 69f15d168bfc..000000000000 --- a/sbt-test/unroll-annot/caseclass/project/DottyInjectedPlugin.scala +++ /dev/null @@ -1,12 +0,0 @@ -import sbt._ -import Keys._ - -object DottyInjectedPlugin extends AutoPlugin { - override def requires = plugins.JvmPlugin - override def trigger = allRequirements - - override val projectSettings = Seq( - scalaVersion := sys.props("plugin.scalaVersion"), - scalacOptions += "-source:3.0-migration" - ) -} diff --git a/sbt-test/unroll-annot/caseclass/test b/sbt-test/unroll-annot/caseclass/test deleted file mode 100644 index e8e500857b0e..000000000000 --- a/sbt-test/unroll-annot/caseclass/test +++ /dev/null @@ -1,14 +0,0 @@ -# compile and run a basic version of Unrolled (v1), and an app that uses it -> v1/compile -> v1_app/runMain unroll.UnrollTestMainV1 -# add a field to the case class (v2), and update the app to use it, -# and ensure the old version (v1) still links -> v2/compile -> v2_app/runMain unroll.UnrollTestMainV1 -> v2_app/runMain unroll.UnrollTestMainV2 -# add a field to the case class (v3), and update the app to use it, -# and ensure the old versions (v1, v2) still link -> v3/compile -> v3_app/runMain unroll.UnrollTestMainV1 -> v3_app/runMain unroll.UnrollTestMainV2 -> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/classMethod/build.sbt b/sbt-test/unroll-annot/classMethod/build.sbt deleted file mode 100644 index fc692a0473a6..000000000000 --- a/sbt-test/unroll-annot/classMethod/build.sbt +++ /dev/null @@ -1,56 +0,0 @@ -lazy val utils = project.in(file("utils")) - -lazy val sharedSettings = Seq( - scalacOptions ++= Seq("-Ycheck:all", "-experimental") -) - -lazy val v1 = project.in(file("v1")) - .settings(sharedSettings) - -lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - Attributed.blank((v1 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v1 / Compile / classDirectory).value) - ), - ) - -lazy val v2 = project.in(file("v2")) - .settings(sharedSettings) - -lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - Attributed.blank((v2 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v2 / Compile / classDirectory).value) - ), - ) - -lazy val v3 = project.in(file("v3")) - .settings(sharedSettings) - -lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - // add v2_app, compiled against v2, to the classpath - Attributed.blank((v2_app / Runtime / classDirectory).value), - Attributed.blank((v3 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v3 / Compile / classDirectory).value) - ), - ) diff --git a/sbt-test/unroll-annot/classMethod/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/classMethod/project/DottyInjectedPlugin.scala deleted file mode 100644 index 69f15d168bfc..000000000000 --- a/sbt-test/unroll-annot/classMethod/project/DottyInjectedPlugin.scala +++ /dev/null @@ -1,12 +0,0 @@ -import sbt._ -import Keys._ - -object DottyInjectedPlugin extends AutoPlugin { - override def requires = plugins.JvmPlugin - override def trigger = allRequirements - - override val projectSettings = Seq( - scalaVersion := sys.props("plugin.scalaVersion"), - scalacOptions += "-source:3.0-migration" - ) -} diff --git a/sbt-test/unroll-annot/classMethod/test b/sbt-test/unroll-annot/classMethod/test deleted file mode 100644 index e8e500857b0e..000000000000 --- a/sbt-test/unroll-annot/classMethod/test +++ /dev/null @@ -1,14 +0,0 @@ -# compile and run a basic version of Unrolled (v1), and an app that uses it -> v1/compile -> v1_app/runMain unroll.UnrollTestMainV1 -# add a field to the case class (v2), and update the app to use it, -# and ensure the old version (v1) still links -> v2/compile -> v2_app/runMain unroll.UnrollTestMainV1 -> v2_app/runMain unroll.UnrollTestMainV2 -# add a field to the case class (v3), and update the app to use it, -# and ensure the old versions (v1, v2) still link -> v3/compile -> v3_app/runMain unroll.UnrollTestMainV1 -> v3_app/runMain unroll.UnrollTestMainV2 -> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/curriedMethod/build.sbt b/sbt-test/unroll-annot/curriedMethod/build.sbt deleted file mode 100644 index fc692a0473a6..000000000000 --- a/sbt-test/unroll-annot/curriedMethod/build.sbt +++ /dev/null @@ -1,56 +0,0 @@ -lazy val utils = project.in(file("utils")) - -lazy val sharedSettings = Seq( - scalacOptions ++= Seq("-Ycheck:all", "-experimental") -) - -lazy val v1 = project.in(file("v1")) - .settings(sharedSettings) - -lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - Attributed.blank((v1 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v1 / Compile / classDirectory).value) - ), - ) - -lazy val v2 = project.in(file("v2")) - .settings(sharedSettings) - -lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - Attributed.blank((v2 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v2 / Compile / classDirectory).value) - ), - ) - -lazy val v3 = project.in(file("v3")) - .settings(sharedSettings) - -lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - // add v2_app, compiled against v2, to the classpath - Attributed.blank((v2_app / Runtime / classDirectory).value), - Attributed.blank((v3 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v3 / Compile / classDirectory).value) - ), - ) diff --git a/sbt-test/unroll-annot/curriedMethod/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/curriedMethod/project/DottyInjectedPlugin.scala deleted file mode 100644 index 69f15d168bfc..000000000000 --- a/sbt-test/unroll-annot/curriedMethod/project/DottyInjectedPlugin.scala +++ /dev/null @@ -1,12 +0,0 @@ -import sbt._ -import Keys._ - -object DottyInjectedPlugin extends AutoPlugin { - override def requires = plugins.JvmPlugin - override def trigger = allRequirements - - override val projectSettings = Seq( - scalaVersion := sys.props("plugin.scalaVersion"), - scalacOptions += "-source:3.0-migration" - ) -} diff --git a/sbt-test/unroll-annot/curriedMethod/test b/sbt-test/unroll-annot/curriedMethod/test deleted file mode 100644 index e8e500857b0e..000000000000 --- a/sbt-test/unroll-annot/curriedMethod/test +++ /dev/null @@ -1,14 +0,0 @@ -# compile and run a basic version of Unrolled (v1), and an app that uses it -> v1/compile -> v1_app/runMain unroll.UnrollTestMainV1 -# add a field to the case class (v2), and update the app to use it, -# and ensure the old version (v1) still links -> v2/compile -> v2_app/runMain unroll.UnrollTestMainV1 -> v2_app/runMain unroll.UnrollTestMainV2 -# add a field to the case class (v3), and update the app to use it, -# and ensure the old versions (v1, v2) still link -> v3/compile -> v3_app/runMain unroll.UnrollTestMainV1 -> v3_app/runMain unroll.UnrollTestMainV2 -> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/genericMethod/build.sbt b/sbt-test/unroll-annot/genericMethod/build.sbt deleted file mode 100644 index fc692a0473a6..000000000000 --- a/sbt-test/unroll-annot/genericMethod/build.sbt +++ /dev/null @@ -1,56 +0,0 @@ -lazy val utils = project.in(file("utils")) - -lazy val sharedSettings = Seq( - scalacOptions ++= Seq("-Ycheck:all", "-experimental") -) - -lazy val v1 = project.in(file("v1")) - .settings(sharedSettings) - -lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - Attributed.blank((v1 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v1 / Compile / classDirectory).value) - ), - ) - -lazy val v2 = project.in(file("v2")) - .settings(sharedSettings) - -lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - Attributed.blank((v2 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v2 / Compile / classDirectory).value) - ), - ) - -lazy val v3 = project.in(file("v3")) - .settings(sharedSettings) - -lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - // add v2_app, compiled against v2, to the classpath - Attributed.blank((v2_app / Runtime / classDirectory).value), - Attributed.blank((v3 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v3 / Compile / classDirectory).value) - ), - ) diff --git a/sbt-test/unroll-annot/genericMethod/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/genericMethod/project/DottyInjectedPlugin.scala deleted file mode 100644 index 69f15d168bfc..000000000000 --- a/sbt-test/unroll-annot/genericMethod/project/DottyInjectedPlugin.scala +++ /dev/null @@ -1,12 +0,0 @@ -import sbt._ -import Keys._ - -object DottyInjectedPlugin extends AutoPlugin { - override def requires = plugins.JvmPlugin - override def trigger = allRequirements - - override val projectSettings = Seq( - scalaVersion := sys.props("plugin.scalaVersion"), - scalacOptions += "-source:3.0-migration" - ) -} diff --git a/sbt-test/unroll-annot/genericMethod/test b/sbt-test/unroll-annot/genericMethod/test deleted file mode 100644 index e8e500857b0e..000000000000 --- a/sbt-test/unroll-annot/genericMethod/test +++ /dev/null @@ -1,14 +0,0 @@ -# compile and run a basic version of Unrolled (v1), and an app that uses it -> v1/compile -> v1_app/runMain unroll.UnrollTestMainV1 -# add a field to the case class (v2), and update the app to use it, -# and ensure the old version (v1) still links -> v2/compile -> v2_app/runMain unroll.UnrollTestMainV1 -> v2_app/runMain unroll.UnrollTestMainV2 -# add a field to the case class (v3), and update the app to use it, -# and ensure the old versions (v1, v2) still link -> v3/compile -> v3_app/runMain unroll.UnrollTestMainV1 -> v3_app/runMain unroll.UnrollTestMainV2 -> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/methodWithImplicit/build.sbt b/sbt-test/unroll-annot/methodWithImplicit/build.sbt deleted file mode 100644 index fc692a0473a6..000000000000 --- a/sbt-test/unroll-annot/methodWithImplicit/build.sbt +++ /dev/null @@ -1,56 +0,0 @@ -lazy val utils = project.in(file("utils")) - -lazy val sharedSettings = Seq( - scalacOptions ++= Seq("-Ycheck:all", "-experimental") -) - -lazy val v1 = project.in(file("v1")) - .settings(sharedSettings) - -lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - Attributed.blank((v1 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v1 / Compile / classDirectory).value) - ), - ) - -lazy val v2 = project.in(file("v2")) - .settings(sharedSettings) - -lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - Attributed.blank((v2 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v2 / Compile / classDirectory).value) - ), - ) - -lazy val v3 = project.in(file("v3")) - .settings(sharedSettings) - -lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - // add v2_app, compiled against v2, to the classpath - Attributed.blank((v2_app / Runtime / classDirectory).value), - Attributed.blank((v3 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v3 / Compile / classDirectory).value) - ), - ) diff --git a/sbt-test/unroll-annot/methodWithImplicit/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/methodWithImplicit/project/DottyInjectedPlugin.scala deleted file mode 100644 index 69f15d168bfc..000000000000 --- a/sbt-test/unroll-annot/methodWithImplicit/project/DottyInjectedPlugin.scala +++ /dev/null @@ -1,12 +0,0 @@ -import sbt._ -import Keys._ - -object DottyInjectedPlugin extends AutoPlugin { - override def requires = plugins.JvmPlugin - override def trigger = allRequirements - - override val projectSettings = Seq( - scalaVersion := sys.props("plugin.scalaVersion"), - scalacOptions += "-source:3.0-migration" - ) -} diff --git a/sbt-test/unroll-annot/methodWithImplicit/test b/sbt-test/unroll-annot/methodWithImplicit/test deleted file mode 100644 index e8e500857b0e..000000000000 --- a/sbt-test/unroll-annot/methodWithImplicit/test +++ /dev/null @@ -1,14 +0,0 @@ -# compile and run a basic version of Unrolled (v1), and an app that uses it -> v1/compile -> v1_app/runMain unroll.UnrollTestMainV1 -# add a field to the case class (v2), and update the app to use it, -# and ensure the old version (v1) still links -> v2/compile -> v2_app/runMain unroll.UnrollTestMainV1 -> v2_app/runMain unroll.UnrollTestMainV2 -# add a field to the case class (v3), and update the app to use it, -# and ensure the old versions (v1, v2) still link -> v3/compile -> v3_app/runMain unroll.UnrollTestMainV1 -> v3_app/runMain unroll.UnrollTestMainV2 -> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/methodWithImplicit/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/methodWithImplicit/utils/src/main/scala/TestUtils.scala deleted file mode 100644 index 6ac413c9fe98..000000000000 --- a/sbt-test/unroll-annot/methodWithImplicit/utils/src/main/scala/TestUtils.scala +++ /dev/null @@ -1,12 +0,0 @@ -package unroll - -object TestUtils { - def logAssertStartsWith(actual: String, expected: String): Unit = { - assert(actual.startsWith(expected)) - val suffix = { - val suffix0 = actual.stripPrefix(expected) - if (suffix0.isEmpty) "" else s""" + "$suffix0"""" - } - println(s"""Assertion passed: found "$expected"$suffix""") - } -} diff --git a/sbt-test/unroll-annot/objectMethod/build.sbt b/sbt-test/unroll-annot/objectMethod/build.sbt deleted file mode 100644 index fc692a0473a6..000000000000 --- a/sbt-test/unroll-annot/objectMethod/build.sbt +++ /dev/null @@ -1,56 +0,0 @@ -lazy val utils = project.in(file("utils")) - -lazy val sharedSettings = Seq( - scalacOptions ++= Seq("-Ycheck:all", "-experimental") -) - -lazy val v1 = project.in(file("v1")) - .settings(sharedSettings) - -lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - Attributed.blank((v1 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v1 / Compile / classDirectory).value) - ), - ) - -lazy val v2 = project.in(file("v2")) - .settings(sharedSettings) - -lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - Attributed.blank((v2 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v2 / Compile / classDirectory).value) - ), - ) - -lazy val v3 = project.in(file("v3")) - .settings(sharedSettings) - -lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - // add v2_app, compiled against v2, to the classpath - Attributed.blank((v2_app / Runtime / classDirectory).value), - Attributed.blank((v3 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v3 / Compile / classDirectory).value) - ), - ) diff --git a/sbt-test/unroll-annot/objectMethod/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/objectMethod/project/DottyInjectedPlugin.scala deleted file mode 100644 index 69f15d168bfc..000000000000 --- a/sbt-test/unroll-annot/objectMethod/project/DottyInjectedPlugin.scala +++ /dev/null @@ -1,12 +0,0 @@ -import sbt._ -import Keys._ - -object DottyInjectedPlugin extends AutoPlugin { - override def requires = plugins.JvmPlugin - override def trigger = allRequirements - - override val projectSettings = Seq( - scalaVersion := sys.props("plugin.scalaVersion"), - scalacOptions += "-source:3.0-migration" - ) -} diff --git a/sbt-test/unroll-annot/objectMethod/test b/sbt-test/unroll-annot/objectMethod/test deleted file mode 100644 index e8e500857b0e..000000000000 --- a/sbt-test/unroll-annot/objectMethod/test +++ /dev/null @@ -1,14 +0,0 @@ -# compile and run a basic version of Unrolled (v1), and an app that uses it -> v1/compile -> v1_app/runMain unroll.UnrollTestMainV1 -# add a field to the case class (v2), and update the app to use it, -# and ensure the old version (v1) still links -> v2/compile -> v2_app/runMain unroll.UnrollTestMainV1 -> v2_app/runMain unroll.UnrollTestMainV2 -# add a field to the case class (v3), and update the app to use it, -# and ensure the old versions (v1, v2) still link -> v3/compile -> v3_app/runMain unroll.UnrollTestMainV1 -> v3_app/runMain unroll.UnrollTestMainV2 -> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/objectMethod/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/objectMethod/utils/src/main/scala/TestUtils.scala deleted file mode 100644 index 6ac413c9fe98..000000000000 --- a/sbt-test/unroll-annot/objectMethod/utils/src/main/scala/TestUtils.scala +++ /dev/null @@ -1,12 +0,0 @@ -package unroll - -object TestUtils { - def logAssertStartsWith(actual: String, expected: String): Unit = { - assert(actual.startsWith(expected)) - val suffix = { - val suffix0 = actual.stripPrefix(expected) - if (suffix0.isEmpty) "" else s""" + "$suffix0"""" - } - println(s"""Assertion passed: found "$expected"$suffix""") - } -} diff --git a/sbt-test/unroll-annot/primaryConstructor/build.sbt b/sbt-test/unroll-annot/primaryConstructor/build.sbt deleted file mode 100644 index fc692a0473a6..000000000000 --- a/sbt-test/unroll-annot/primaryConstructor/build.sbt +++ /dev/null @@ -1,56 +0,0 @@ -lazy val utils = project.in(file("utils")) - -lazy val sharedSettings = Seq( - scalacOptions ++= Seq("-Ycheck:all", "-experimental") -) - -lazy val v1 = project.in(file("v1")) - .settings(sharedSettings) - -lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - Attributed.blank((v1 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v1 / Compile / classDirectory).value) - ), - ) - -lazy val v2 = project.in(file("v2")) - .settings(sharedSettings) - -lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - Attributed.blank((v2 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v2 / Compile / classDirectory).value) - ), - ) - -lazy val v3 = project.in(file("v3")) - .settings(sharedSettings) - -lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - // add v2_app, compiled against v2, to the classpath - Attributed.blank((v2_app / Runtime / classDirectory).value), - Attributed.blank((v3 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v3 / Compile / classDirectory).value) - ), - ) diff --git a/sbt-test/unroll-annot/primaryConstructor/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/primaryConstructor/project/DottyInjectedPlugin.scala deleted file mode 100644 index 69f15d168bfc..000000000000 --- a/sbt-test/unroll-annot/primaryConstructor/project/DottyInjectedPlugin.scala +++ /dev/null @@ -1,12 +0,0 @@ -import sbt._ -import Keys._ - -object DottyInjectedPlugin extends AutoPlugin { - override def requires = plugins.JvmPlugin - override def trigger = allRequirements - - override val projectSettings = Seq( - scalaVersion := sys.props("plugin.scalaVersion"), - scalacOptions += "-source:3.0-migration" - ) -} diff --git a/sbt-test/unroll-annot/primaryConstructor/test b/sbt-test/unroll-annot/primaryConstructor/test deleted file mode 100644 index e8e500857b0e..000000000000 --- a/sbt-test/unroll-annot/primaryConstructor/test +++ /dev/null @@ -1,14 +0,0 @@ -# compile and run a basic version of Unrolled (v1), and an app that uses it -> v1/compile -> v1_app/runMain unroll.UnrollTestMainV1 -# add a field to the case class (v2), and update the app to use it, -# and ensure the old version (v1) still links -> v2/compile -> v2_app/runMain unroll.UnrollTestMainV1 -> v2_app/runMain unroll.UnrollTestMainV2 -# add a field to the case class (v3), and update the app to use it, -# and ensure the old versions (v1, v2) still link -> v3/compile -> v3_app/runMain unroll.UnrollTestMainV1 -> v3_app/runMain unroll.UnrollTestMainV2 -> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/primaryConstructor/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/primaryConstructor/utils/src/main/scala/TestUtils.scala deleted file mode 100644 index 6ac413c9fe98..000000000000 --- a/sbt-test/unroll-annot/primaryConstructor/utils/src/main/scala/TestUtils.scala +++ /dev/null @@ -1,12 +0,0 @@ -package unroll - -object TestUtils { - def logAssertStartsWith(actual: String, expected: String): Unit = { - assert(actual.startsWith(expected)) - val suffix = { - val suffix0 = actual.stripPrefix(expected) - if (suffix0.isEmpty) "" else s""" + "$suffix0"""" - } - println(s"""Assertion passed: found "$expected"$suffix""") - } -} diff --git a/sbt-test/unroll-annot/secondParameterList/build.sbt b/sbt-test/unroll-annot/secondParameterList/build.sbt deleted file mode 100644 index fc692a0473a6..000000000000 --- a/sbt-test/unroll-annot/secondParameterList/build.sbt +++ /dev/null @@ -1,56 +0,0 @@ -lazy val utils = project.in(file("utils")) - -lazy val sharedSettings = Seq( - scalacOptions ++= Seq("-Ycheck:all", "-experimental") -) - -lazy val v1 = project.in(file("v1")) - .settings(sharedSettings) - -lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - Attributed.blank((v1 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v1 / Compile / classDirectory).value) - ), - ) - -lazy val v2 = project.in(file("v2")) - .settings(sharedSettings) - -lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - Attributed.blank((v2 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v2 / Compile / classDirectory).value) - ), - ) - -lazy val v3 = project.in(file("v3")) - .settings(sharedSettings) - -lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - // add v2_app, compiled against v2, to the classpath - Attributed.blank((v2_app / Runtime / classDirectory).value), - Attributed.blank((v3 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v3 / Compile / classDirectory).value) - ), - ) diff --git a/sbt-test/unroll-annot/secondParameterList/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/secondParameterList/project/DottyInjectedPlugin.scala deleted file mode 100644 index 69f15d168bfc..000000000000 --- a/sbt-test/unroll-annot/secondParameterList/project/DottyInjectedPlugin.scala +++ /dev/null @@ -1,12 +0,0 @@ -import sbt._ -import Keys._ - -object DottyInjectedPlugin extends AutoPlugin { - override def requires = plugins.JvmPlugin - override def trigger = allRequirements - - override val projectSettings = Seq( - scalaVersion := sys.props("plugin.scalaVersion"), - scalacOptions += "-source:3.0-migration" - ) -} diff --git a/sbt-test/unroll-annot/secondParameterList/test b/sbt-test/unroll-annot/secondParameterList/test deleted file mode 100644 index e8e500857b0e..000000000000 --- a/sbt-test/unroll-annot/secondParameterList/test +++ /dev/null @@ -1,14 +0,0 @@ -# compile and run a basic version of Unrolled (v1), and an app that uses it -> v1/compile -> v1_app/runMain unroll.UnrollTestMainV1 -# add a field to the case class (v2), and update the app to use it, -# and ensure the old version (v1) still links -> v2/compile -> v2_app/runMain unroll.UnrollTestMainV1 -> v2_app/runMain unroll.UnrollTestMainV2 -# add a field to the case class (v3), and update the app to use it, -# and ensure the old versions (v1, v2) still link -> v3/compile -> v3_app/runMain unroll.UnrollTestMainV1 -> v3_app/runMain unroll.UnrollTestMainV2 -> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/secondParameterList/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/secondParameterList/utils/src/main/scala/TestUtils.scala deleted file mode 100644 index 6ac413c9fe98..000000000000 --- a/sbt-test/unroll-annot/secondParameterList/utils/src/main/scala/TestUtils.scala +++ /dev/null @@ -1,12 +0,0 @@ -package unroll - -object TestUtils { - def logAssertStartsWith(actual: String, expected: String): Unit = { - assert(actual.startsWith(expected)) - val suffix = { - val suffix0 = actual.stripPrefix(expected) - if (suffix0.isEmpty) "" else s""" + "$suffix0"""" - } - println(s"""Assertion passed: found "$expected"$suffix""") - } -} diff --git a/sbt-test/unroll-annot/secondaryConstructor/build.sbt b/sbt-test/unroll-annot/secondaryConstructor/build.sbt deleted file mode 100644 index fc692a0473a6..000000000000 --- a/sbt-test/unroll-annot/secondaryConstructor/build.sbt +++ /dev/null @@ -1,56 +0,0 @@ -lazy val utils = project.in(file("utils")) - -lazy val sharedSettings = Seq( - scalacOptions ++= Seq("-Ycheck:all", "-experimental") -) - -lazy val v1 = project.in(file("v1")) - .settings(sharedSettings) - -lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - Attributed.blank((v1 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v1 / Compile / classDirectory).value) - ), - ) - -lazy val v2 = project.in(file("v2")) - .settings(sharedSettings) - -lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - Attributed.blank((v2 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v2 / Compile / classDirectory).value) - ), - ) - -lazy val v3 = project.in(file("v3")) - .settings(sharedSettings) - -lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - // add v2_app, compiled against v2, to the classpath - Attributed.blank((v2_app / Runtime / classDirectory).value), - Attributed.blank((v3 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v3 / Compile / classDirectory).value) - ), - ) diff --git a/sbt-test/unroll-annot/secondaryConstructor/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/secondaryConstructor/project/DottyInjectedPlugin.scala deleted file mode 100644 index 69f15d168bfc..000000000000 --- a/sbt-test/unroll-annot/secondaryConstructor/project/DottyInjectedPlugin.scala +++ /dev/null @@ -1,12 +0,0 @@ -import sbt._ -import Keys._ - -object DottyInjectedPlugin extends AutoPlugin { - override def requires = plugins.JvmPlugin - override def trigger = allRequirements - - override val projectSettings = Seq( - scalaVersion := sys.props("plugin.scalaVersion"), - scalacOptions += "-source:3.0-migration" - ) -} diff --git a/sbt-test/unroll-annot/secondaryConstructor/test b/sbt-test/unroll-annot/secondaryConstructor/test deleted file mode 100644 index e8e500857b0e..000000000000 --- a/sbt-test/unroll-annot/secondaryConstructor/test +++ /dev/null @@ -1,14 +0,0 @@ -# compile and run a basic version of Unrolled (v1), and an app that uses it -> v1/compile -> v1_app/runMain unroll.UnrollTestMainV1 -# add a field to the case class (v2), and update the app to use it, -# and ensure the old version (v1) still links -> v2/compile -> v2_app/runMain unroll.UnrollTestMainV1 -> v2_app/runMain unroll.UnrollTestMainV2 -# add a field to the case class (v3), and update the app to use it, -# and ensure the old versions (v1, v2) still link -> v3/compile -> v3_app/runMain unroll.UnrollTestMainV1 -> v3_app/runMain unroll.UnrollTestMainV2 -> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/secondaryConstructor/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/secondaryConstructor/utils/src/main/scala/TestUtils.scala deleted file mode 100644 index 6ac413c9fe98..000000000000 --- a/sbt-test/unroll-annot/secondaryConstructor/utils/src/main/scala/TestUtils.scala +++ /dev/null @@ -1,12 +0,0 @@ -package unroll - -object TestUtils { - def logAssertStartsWith(actual: String, expected: String): Unit = { - assert(actual.startsWith(expected)) - val suffix = { - val suffix0 = actual.stripPrefix(expected) - if (suffix0.isEmpty) "" else s""" + "$suffix0"""" - } - println(s"""Assertion passed: found "$expected"$suffix""") - } -} diff --git a/sbt-test/unroll-annot/traitMethod/build.sbt b/sbt-test/unroll-annot/traitMethod/build.sbt deleted file mode 100644 index fc692a0473a6..000000000000 --- a/sbt-test/unroll-annot/traitMethod/build.sbt +++ /dev/null @@ -1,56 +0,0 @@ -lazy val utils = project.in(file("utils")) - -lazy val sharedSettings = Seq( - scalacOptions ++= Seq("-Ycheck:all", "-experimental") -) - -lazy val v1 = project.in(file("v1")) - .settings(sharedSettings) - -lazy val v1_app = project.in(file("v1_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - Attributed.blank((v1 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v1 / Compile / classDirectory).value) - ), - ) - -lazy val v2 = project.in(file("v2")) - .settings(sharedSettings) - -lazy val v2_app = project.in(file("v2_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - Attributed.blank((v2 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v2 / Compile / classDirectory).value) - ), - ) - -lazy val v3 = project.in(file("v3")) - .settings(sharedSettings) - -lazy val v3_app = project.in(file("v3_app")).dependsOn(utils) - .settings(sharedSettings) - .settings( - fork := true, - Runtime / unmanagedClasspath := Seq( - // add v1_app, compiled against v1, to the classpath - Attributed.blank((v1_app / Runtime / classDirectory).value), - // add v2_app, compiled against v2, to the classpath - Attributed.blank((v2_app / Runtime / classDirectory).value), - Attributed.blank((v3 / Runtime / classDirectory).value) - ), - Compile / unmanagedClasspath := Seq( - Attributed.blank((v3 / Compile / classDirectory).value) - ), - ) diff --git a/sbt-test/unroll-annot/traitMethod/project/DottyInjectedPlugin.scala b/sbt-test/unroll-annot/traitMethod/project/DottyInjectedPlugin.scala deleted file mode 100644 index 69f15d168bfc..000000000000 --- a/sbt-test/unroll-annot/traitMethod/project/DottyInjectedPlugin.scala +++ /dev/null @@ -1,12 +0,0 @@ -import sbt._ -import Keys._ - -object DottyInjectedPlugin extends AutoPlugin { - override def requires = plugins.JvmPlugin - override def trigger = allRequirements - - override val projectSettings = Seq( - scalaVersion := sys.props("plugin.scalaVersion"), - scalacOptions += "-source:3.0-migration" - ) -} diff --git a/sbt-test/unroll-annot/traitMethod/test b/sbt-test/unroll-annot/traitMethod/test deleted file mode 100644 index e8e500857b0e..000000000000 --- a/sbt-test/unroll-annot/traitMethod/test +++ /dev/null @@ -1,14 +0,0 @@ -# compile and run a basic version of Unrolled (v1), and an app that uses it -> v1/compile -> v1_app/runMain unroll.UnrollTestMainV1 -# add a field to the case class (v2), and update the app to use it, -# and ensure the old version (v1) still links -> v2/compile -> v2_app/runMain unroll.UnrollTestMainV1 -> v2_app/runMain unroll.UnrollTestMainV2 -# add a field to the case class (v3), and update the app to use it, -# and ensure the old versions (v1, v2) still link -> v3/compile -> v3_app/runMain unroll.UnrollTestMainV1 -> v3_app/runMain unroll.UnrollTestMainV2 -> v3_app/runMain unroll.UnrollTestMainV3 diff --git a/sbt-test/unroll-annot/traitMethod/utils/src/main/scala/TestUtils.scala b/sbt-test/unroll-annot/traitMethod/utils/src/main/scala/TestUtils.scala deleted file mode 100644 index 6ac413c9fe98..000000000000 --- a/sbt-test/unroll-annot/traitMethod/utils/src/main/scala/TestUtils.scala +++ /dev/null @@ -1,12 +0,0 @@ -package unroll - -object TestUtils { - def logAssertStartsWith(actual: String, expected: String): Unit = { - assert(actual.startsWith(expected)) - val suffix = { - val suffix0 = actual.stripPrefix(expected) - if (suffix0.isEmpty) "" else s""" + "$suffix0"""" - } - println(s"""Assertion passed: found "$expected"$suffix""") - } -} diff --git a/sbt-test/unroll-annot/classMethod/utils/src/main/scala/TestUtils.scala b/tests/run/unroll-caseclass-integration/TestUtils_1.scala similarity index 91% rename from sbt-test/unroll-annot/classMethod/utils/src/main/scala/TestUtils.scala rename to tests/run/unroll-caseclass-integration/TestUtils_1.scala index 6ac413c9fe98..e639a54d924b 100644 --- a/sbt-test/unroll-annot/classMethod/utils/src/main/scala/TestUtils.scala +++ b/tests/run/unroll-caseclass-integration/TestUtils_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll object TestUtils { diff --git a/tests/run/unroll-caseclass-integration/Test_4.scala b/tests/run/unroll-caseclass-integration/Test_4.scala new file mode 100644 index 000000000000..cae956f8b1bf --- /dev/null +++ b/tests/run/unroll-caseclass-integration/Test_4.scala @@ -0,0 +1,8 @@ +//> using options -experimental +// scalajs: --skip +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/sbt-test/unroll-annot/caseclass/v1_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-caseclass-integration/UnrollTestMain_1.scala similarity index 95% rename from sbt-test/unroll-annot/caseclass/v1_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-caseclass-integration/UnrollTestMain_1.scala index e0b058ad0230..45ce6a768f2a 100644 --- a/sbt-test/unroll-annot/caseclass/v1_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-caseclass-integration/UnrollTestMain_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/caseclass/v2_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-caseclass-integration/UnrollTestMain_2.scala similarity index 96% rename from sbt-test/unroll-annot/caseclass/v2_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-caseclass-integration/UnrollTestMain_2.scala index c266a5f8c88e..f6cfbfc54a2c 100644 --- a/sbt-test/unroll-annot/caseclass/v2_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-caseclass-integration/UnrollTestMain_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-caseclass-integration/UnrollTestMain_3.scala similarity index 97% rename from sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-caseclass-integration/UnrollTestMain_3.scala index a58303a6bdad..b0e4d7a5d25a 100644 --- a/sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-caseclass-integration/UnrollTestMain_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/tests/run/unroll-caseclass-integration/UnrollTestPlatformSpecific_3.scala similarity index 94% rename from sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala rename to tests/run/unroll-caseclass-integration/UnrollTestPlatformSpecific_3.scala index 07dee69cd8a7..e06a38502c62 100644 --- a/sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala +++ b/tests/run/unroll-caseclass-integration/UnrollTestPlatformSpecific_3.scala @@ -1,3 +1,5 @@ +//> using options -experimental +// scalajs: --skip package unroll object UnrollTestPlatformSpecificV3{ @@ -30,4 +32,4 @@ object UnrollTestPlatformSpecificV3{ cls.getConstructors.foreach(println) } -} \ No newline at end of file +} diff --git a/sbt-test/unroll-annot/caseclass/v1_app/src/main/scala/UnrollTestScalaSpecific.scala b/tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_1.scala similarity index 78% rename from sbt-test/unroll-annot/caseclass/v1_app/src/main/scala/UnrollTestScalaSpecific.scala rename to tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_1.scala index 514905a741f4..28eb2815e979 100644 --- a/sbt-test/unroll-annot/caseclass/v1_app/src/main/scala/UnrollTestScalaSpecific.scala +++ b/tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_1.scala @@ -1,10 +1,11 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith object UnrollTestScalaSpecificV1{ def test() = { - val unrolled = Unrolled.fromProduct( + val unrolled = summon[scala.deriving.Mirror.Of[Unrolled]].fromProduct( new Product{ def canEqual(that: Any) = true def productArity = 2 diff --git a/sbt-test/unroll-annot/caseclass/v2_app/src/main/scala/UnrollTestScalaSpecific.scala b/tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_2.scala similarity index 79% rename from sbt-test/unroll-annot/caseclass/v2_app/src/main/scala/UnrollTestScalaSpecific.scala rename to tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_2.scala index 88ead065de6e..5d4079a093ce 100644 --- a/sbt-test/unroll-annot/caseclass/v2_app/src/main/scala/UnrollTestScalaSpecific.scala +++ b/tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_2.scala @@ -1,10 +1,11 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith object UnrollTestScalaSpecificV2{ def test() = { - val unrolled = Unrolled.fromProduct( + val unrolled = summon[scala.deriving.Mirror.Of[Unrolled]].fromProduct( new Product { def canEqual(that: Any) = true def productArity = 3 diff --git a/sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestScalaSpecific.scala b/tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_3.scala similarity index 80% rename from sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestScalaSpecific.scala rename to tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_3.scala index 13d4fffe7f62..46f84998baa0 100644 --- a/sbt-test/unroll-annot/caseclass/v3_app/src/main/scala/UnrollTestScalaSpecific.scala +++ b/tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_3.scala @@ -1,8 +1,9 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith object UnrollTestScalaSpecificV3{ def apply() = { - val unrolled = Unrolled.fromProduct( + val unrolled = summon[scala.deriving.Mirror.Of[Unrolled]].fromProduct( new Product { def canEqual(that: Any) = true def productArity = 4 diff --git a/sbt-test/unroll-annot/caseclass/v1/src/main/scala/Unrolled.scala b/tests/run/unroll-caseclass-integration/Unrolled_1.scala similarity index 71% rename from sbt-test/unroll-annot/caseclass/v1/src/main/scala/Unrolled.scala rename to tests/run/unroll-caseclass-integration/Unrolled_1.scala index 997ae2f2dc00..e3f57bddd325 100644 --- a/sbt-test/unroll-annot/caseclass/v1/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-caseclass-integration/Unrolled_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll case class Unrolled(s: String, n: Int = 1){ diff --git a/sbt-test/unroll-annot/caseclass/v2/src/main/scala/Unrolled.scala b/tests/run/unroll-caseclass-integration/Unrolled_2.scala similarity index 81% rename from sbt-test/unroll-annot/caseclass/v2/src/main/scala/Unrolled.scala rename to tests/run/unroll-caseclass-integration/Unrolled_2.scala index 916c44550a13..cb2232a57726 100644 --- a/sbt-test/unroll-annot/caseclass/v2/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-caseclass-integration/Unrolled_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/sbt-test/unroll-annot/caseclass/v3/src/main/scala/Unrolled.scala b/tests/run/unroll-caseclass-integration/Unrolled_3.scala similarity index 84% rename from sbt-test/unroll-annot/caseclass/v3/src/main/scala/Unrolled.scala rename to tests/run/unroll-caseclass-integration/Unrolled_3.scala index f1bf8c01ad2a..66b4981660df 100644 --- a/sbt-test/unroll-annot/caseclass/v3/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-caseclass-integration/Unrolled_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/sbt-test/unroll-annot/curriedMethod/utils/src/main/scala/TestUtils.scala b/tests/run/unroll-classMethod-integration/TestUtils_1.scala similarity index 91% rename from sbt-test/unroll-annot/curriedMethod/utils/src/main/scala/TestUtils.scala rename to tests/run/unroll-classMethod-integration/TestUtils_1.scala index 6ac413c9fe98..e639a54d924b 100644 --- a/sbt-test/unroll-annot/curriedMethod/utils/src/main/scala/TestUtils.scala +++ b/tests/run/unroll-classMethod-integration/TestUtils_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll object TestUtils { diff --git a/tests/run/unroll-classMethod-integration/Test_4.scala b/tests/run/unroll-classMethod-integration/Test_4.scala new file mode 100644 index 000000000000..cae956f8b1bf --- /dev/null +++ b/tests/run/unroll-classMethod-integration/Test_4.scala @@ -0,0 +1,8 @@ +//> using options -experimental +// scalajs: --skip +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/sbt-test/unroll-annot/genericMethod/v1_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-classMethod-integration/UnrollTestMain_1.scala similarity index 85% rename from sbt-test/unroll-annot/genericMethod/v1_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-classMethod-integration/UnrollTestMain_1.scala index f36e9dd07fed..62d61c81d21d 100644 --- a/sbt-test/unroll-annot/genericMethod/v1_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-classMethod-integration/UnrollTestMain_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/classMethod/v2_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-classMethod-integration/UnrollTestMain_2.scala similarity index 91% rename from sbt-test/unroll-annot/classMethod/v2_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-classMethod-integration/UnrollTestMain_2.scala index 91b54aa9742c..921ee48ff0db 100644 --- a/sbt-test/unroll-annot/classMethod/v2_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-classMethod-integration/UnrollTestMain_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/genericMethod/v3_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-classMethod-integration/UnrollTestMain_3.scala similarity index 93% rename from sbt-test/unroll-annot/genericMethod/v3_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-classMethod-integration/UnrollTestMain_3.scala index 9a6daa3a9210..bc985b58c359 100644 --- a/sbt-test/unroll-annot/genericMethod/v3_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-classMethod-integration/UnrollTestMain_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/classMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/tests/run/unroll-classMethod-integration/UnrollTestPlatformSpecific_3.scala similarity index 94% rename from sbt-test/unroll-annot/classMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala rename to tests/run/unroll-classMethod-integration/UnrollTestPlatformSpecific_3.scala index e8527808befd..3b5dc603fd3a 100644 --- a/sbt-test/unroll-annot/classMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala +++ b/tests/run/unroll-classMethod-integration/UnrollTestPlatformSpecific_3.scala @@ -1,3 +1,5 @@ +//> using options -experimental +// scalajs: --skip package unroll object UnrollTestPlatformSpecificV3{ @@ -27,4 +29,4 @@ object UnrollTestPlatformSpecificV3{ cls.getMethods.filter(_.getName.contains("foo")).foreach(println) } -} \ No newline at end of file +} diff --git a/sbt-test/unroll-annot/classMethod/v1/src/main/scala/Unrolled.scala b/tests/run/unroll-classMethod-integration/Unrolled_1.scala similarity index 64% rename from sbt-test/unroll-annot/classMethod/v1/src/main/scala/Unrolled.scala rename to tests/run/unroll-classMethod-integration/Unrolled_1.scala index 638bcfdeb96d..aa6ca65fe6a3 100644 --- a/sbt-test/unroll-annot/classMethod/v1/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-classMethod-integration/Unrolled_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll class Unrolled{ diff --git a/sbt-test/unroll-annot/classMethod/v2/src/main/scala/Unrolled.scala b/tests/run/unroll-classMethod-integration/Unrolled_2.scala similarity index 81% rename from sbt-test/unroll-annot/classMethod/v2/src/main/scala/Unrolled.scala rename to tests/run/unroll-classMethod-integration/Unrolled_2.scala index 6d756148a098..2091bb4c5a9e 100644 --- a/sbt-test/unroll-annot/classMethod/v2/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-classMethod-integration/Unrolled_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/sbt-test/unroll-annot/classMethod/v3/src/main/scala/Unrolled.scala b/tests/run/unroll-classMethod-integration/Unrolled_3.scala similarity index 84% rename from sbt-test/unroll-annot/classMethod/v3/src/main/scala/Unrolled.scala rename to tests/run/unroll-classMethod-integration/Unrolled_3.scala index 684c3906750e..8991bda3aeb7 100644 --- a/sbt-test/unroll-annot/classMethod/v3/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-classMethod-integration/Unrolled_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/sbt-test/unroll-annot/genericMethod/utils/src/main/scala/TestUtils.scala b/tests/run/unroll-curriedMethod-integration/TestUtils_1.scala similarity index 91% rename from sbt-test/unroll-annot/genericMethod/utils/src/main/scala/TestUtils.scala rename to tests/run/unroll-curriedMethod-integration/TestUtils_1.scala index 6ac413c9fe98..e639a54d924b 100644 --- a/sbt-test/unroll-annot/genericMethod/utils/src/main/scala/TestUtils.scala +++ b/tests/run/unroll-curriedMethod-integration/TestUtils_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll object TestUtils { diff --git a/tests/run/unroll-curriedMethod-integration/Test_4.scala b/tests/run/unroll-curriedMethod-integration/Test_4.scala new file mode 100644 index 000000000000..cae956f8b1bf --- /dev/null +++ b/tests/run/unroll-curriedMethod-integration/Test_4.scala @@ -0,0 +1,8 @@ +//> using options -experimental +// scalajs: --skip +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/sbt-test/unroll-annot/curriedMethod/v1_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-curriedMethod-integration/UnrollTestMain_1.scala similarity index 86% rename from sbt-test/unroll-annot/curriedMethod/v1_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-curriedMethod-integration/UnrollTestMain_1.scala index 8d1ca388477b..411aae125e20 100644 --- a/sbt-test/unroll-annot/curriedMethod/v1_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-curriedMethod-integration/UnrollTestMain_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/curriedMethod/v2_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-curriedMethod-integration/UnrollTestMain_2.scala similarity index 91% rename from sbt-test/unroll-annot/curriedMethod/v2_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-curriedMethod-integration/UnrollTestMain_2.scala index 66ad8fdf8698..9be52201ed8b 100644 --- a/sbt-test/unroll-annot/curriedMethod/v2_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-curriedMethod-integration/UnrollTestMain_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/curriedMethod/v3_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-curriedMethod-integration/UnrollTestMain_3.scala similarity index 93% rename from sbt-test/unroll-annot/curriedMethod/v3_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-curriedMethod-integration/UnrollTestMain_3.scala index 02b839fc07d2..1f281db4f497 100644 --- a/sbt-test/unroll-annot/curriedMethod/v3_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-curriedMethod-integration/UnrollTestMain_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/curriedMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/tests/run/unroll-curriedMethod-integration/UnrollTestPlatformSpecific_3.scala similarity index 95% rename from sbt-test/unroll-annot/curriedMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala rename to tests/run/unroll-curriedMethod-integration/UnrollTestPlatformSpecific_3.scala index 61eeeb8756b8..69bbaf06d6ae 100644 --- a/sbt-test/unroll-annot/curriedMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala +++ b/tests/run/unroll-curriedMethod-integration/UnrollTestPlatformSpecific_3.scala @@ -1,3 +1,5 @@ +//> using options -experimental +// scalajs: --skip package unroll object UnrollTestPlatformSpecificV3{ @@ -26,4 +28,4 @@ object UnrollTestPlatformSpecificV3{ cls.getMethods.filter(_.getName.contains("foo")).foreach(println) } -} \ No newline at end of file +} diff --git a/sbt-test/unroll-annot/curriedMethod/v1/src/main/scala/Unrolled.scala b/tests/run/unroll-curriedMethod-integration/Unrolled_1.scala similarity index 72% rename from sbt-test/unroll-annot/curriedMethod/v1/src/main/scala/Unrolled.scala rename to tests/run/unroll-curriedMethod-integration/Unrolled_1.scala index e508d4345313..d6b8b06da582 100644 --- a/sbt-test/unroll-annot/curriedMethod/v1/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-curriedMethod-integration/Unrolled_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll class Unrolled{ diff --git a/sbt-test/unroll-annot/curriedMethod/v2/src/main/scala/Unrolled.scala b/tests/run/unroll-curriedMethod-integration/Unrolled_2.scala similarity index 84% rename from sbt-test/unroll-annot/curriedMethod/v2/src/main/scala/Unrolled.scala rename to tests/run/unroll-curriedMethod-integration/Unrolled_2.scala index 595c06d6dc50..5a526bf6eeb2 100644 --- a/sbt-test/unroll-annot/curriedMethod/v2/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-curriedMethod-integration/Unrolled_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/sbt-test/unroll-annot/curriedMethod/v3/src/main/scala/Unrolled.scala b/tests/run/unroll-curriedMethod-integration/Unrolled_3.scala similarity index 85% rename from sbt-test/unroll-annot/curriedMethod/v3/src/main/scala/Unrolled.scala rename to tests/run/unroll-curriedMethod-integration/Unrolled_3.scala index f34c9b071edd..008576a9a5c3 100644 --- a/sbt-test/unroll-annot/curriedMethod/v3/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-curriedMethod-integration/Unrolled_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/sbt-test/unroll-annot/caseclass/utils/src/main/scala/TestUtils.scala b/tests/run/unroll-genericMethod-integration/TestUtils_1.scala similarity index 91% rename from sbt-test/unroll-annot/caseclass/utils/src/main/scala/TestUtils.scala rename to tests/run/unroll-genericMethod-integration/TestUtils_1.scala index 6ac413c9fe98..e639a54d924b 100644 --- a/sbt-test/unroll-annot/caseclass/utils/src/main/scala/TestUtils.scala +++ b/tests/run/unroll-genericMethod-integration/TestUtils_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll object TestUtils { diff --git a/tests/run/unroll-genericMethod-integration/Test_4.scala b/tests/run/unroll-genericMethod-integration/Test_4.scala new file mode 100644 index 000000000000..cae956f8b1bf --- /dev/null +++ b/tests/run/unroll-genericMethod-integration/Test_4.scala @@ -0,0 +1,8 @@ +//> using options -experimental +// scalajs: --skip +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/sbt-test/unroll-annot/classMethod/v1_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-genericMethod-integration/UnrollTestMain_1.scala similarity index 85% rename from sbt-test/unroll-annot/classMethod/v1_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-genericMethod-integration/UnrollTestMain_1.scala index f36e9dd07fed..62d61c81d21d 100644 --- a/sbt-test/unroll-annot/classMethod/v1_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-genericMethod-integration/UnrollTestMain_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/genericMethod/v2_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-genericMethod-integration/UnrollTestMain_2.scala similarity index 91% rename from sbt-test/unroll-annot/genericMethod/v2_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-genericMethod-integration/UnrollTestMain_2.scala index 91b54aa9742c..921ee48ff0db 100644 --- a/sbt-test/unroll-annot/genericMethod/v2_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-genericMethod-integration/UnrollTestMain_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/classMethod/v3_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-genericMethod-integration/UnrollTestMain_3.scala similarity index 93% rename from sbt-test/unroll-annot/classMethod/v3_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-genericMethod-integration/UnrollTestMain_3.scala index 9a6daa3a9210..bc985b58c359 100644 --- a/sbt-test/unroll-annot/classMethod/v3_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-genericMethod-integration/UnrollTestMain_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/genericMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/tests/run/unroll-genericMethod-integration/UnrollTestPlatformSpecific_3.scala similarity index 93% rename from sbt-test/unroll-annot/genericMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala rename to tests/run/unroll-genericMethod-integration/UnrollTestPlatformSpecific_3.scala index 7e2e913ea1f6..d5310b3e302b 100644 --- a/sbt-test/unroll-annot/genericMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala +++ b/tests/run/unroll-genericMethod-integration/UnrollTestPlatformSpecific_3.scala @@ -1,3 +1,5 @@ +//> using options -experimental +// scalajs: --skip package unroll object UnrollTestPlatformSpecificV3{ @@ -24,4 +26,4 @@ object UnrollTestPlatformSpecificV3{ cls.getMethods.filter(_.getName.contains("foo")).foreach(println) } -} \ No newline at end of file +} diff --git a/sbt-test/unroll-annot/genericMethod/v1/src/main/scala/Unrolled.scala b/tests/run/unroll-genericMethod-integration/Unrolled_1.scala similarity index 67% rename from sbt-test/unroll-annot/genericMethod/v1/src/main/scala/Unrolled.scala rename to tests/run/unroll-genericMethod-integration/Unrolled_1.scala index 3f14c4f31544..a1d69945bc7e 100644 --- a/sbt-test/unroll-annot/genericMethod/v1/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-genericMethod-integration/Unrolled_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll class Unrolled{ diff --git a/sbt-test/unroll-annot/genericMethod/v2/src/main/scala/Unrolled.scala b/tests/run/unroll-genericMethod-integration/Unrolled_2.scala similarity index 82% rename from sbt-test/unroll-annot/genericMethod/v2/src/main/scala/Unrolled.scala rename to tests/run/unroll-genericMethod-integration/Unrolled_2.scala index 191a4df184ef..e5970388fff8 100644 --- a/sbt-test/unroll-annot/genericMethod/v2/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-genericMethod-integration/Unrolled_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/sbt-test/unroll-annot/genericMethod/v3/src/main/scala/Unrolled.scala b/tests/run/unroll-genericMethod-integration/Unrolled_3.scala similarity index 84% rename from sbt-test/unroll-annot/genericMethod/v3/src/main/scala/Unrolled.scala rename to tests/run/unroll-genericMethod-integration/Unrolled_3.scala index 0c4909948a24..2ababa300ed1 100644 --- a/sbt-test/unroll-annot/genericMethod/v3/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-genericMethod-integration/Unrolled_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/tests/run/unroll-methodWithImplicit-integration/TestUtils_1.scala b/tests/run/unroll-methodWithImplicit-integration/TestUtils_1.scala new file mode 100644 index 000000000000..e639a54d924b --- /dev/null +++ b/tests/run/unroll-methodWithImplicit-integration/TestUtils_1.scala @@ -0,0 +1,13 @@ +//> using options -experimental +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/tests/run/unroll-methodWithImplicit-integration/Test_4.scala b/tests/run/unroll-methodWithImplicit-integration/Test_4.scala new file mode 100644 index 000000000000..cae956f8b1bf --- /dev/null +++ b/tests/run/unroll-methodWithImplicit-integration/Test_4.scala @@ -0,0 +1,8 @@ +//> using options -experimental +// scalajs: --skip +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/sbt-test/unroll-annot/methodWithImplicit/v1_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_1.scala similarity index 87% rename from sbt-test/unroll-annot/methodWithImplicit/v1_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_1.scala index 928a74f57d5e..e4e91e6709f3 100644 --- a/sbt-test/unroll-annot/methodWithImplicit/v1_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/methodWithImplicit/v2_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_2.scala similarity index 92% rename from sbt-test/unroll-annot/methodWithImplicit/v2_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_2.scala index de14613be3cc..69a5a0b39ab6 100644 --- a/sbt-test/unroll-annot/methodWithImplicit/v2_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/methodWithImplicit/v3_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_3.scala similarity index 93% rename from sbt-test/unroll-annot/methodWithImplicit/v3_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_3.scala index 1982d7dff344..ffd528e15f5e 100644 --- a/sbt-test/unroll-annot/methodWithImplicit/v3_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/methodWithImplicit/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/tests/run/unroll-methodWithImplicit-integration/UnrollTestPlatformSpecific_3.scala similarity index 94% rename from sbt-test/unroll-annot/methodWithImplicit/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala rename to tests/run/unroll-methodWithImplicit-integration/UnrollTestPlatformSpecific_3.scala index 3537a6373e0d..431c1e7c2d4c 100644 --- a/sbt-test/unroll-annot/methodWithImplicit/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala +++ b/tests/run/unroll-methodWithImplicit-integration/UnrollTestPlatformSpecific_3.scala @@ -1,3 +1,5 @@ +//> using options -experimental +// scalajs: --skip package unroll object UnrollTestPlatformSpecificV3{ @@ -11,7 +13,7 @@ object UnrollTestPlatformSpecificV3{ ) assert(scala.util.Try(cls.getMethod("foo", classOf[String], classOf[Int], classOf[String => String])).isFailure) - + assert( cls.getMethod("foo", classOf[String], classOf[Int], classOf[Boolean], classOf[String => String]) .invoke(instance, "hello", 2: Integer, java.lang.Boolean.FALSE, identity[String](_)) == @@ -26,4 +28,4 @@ object UnrollTestPlatformSpecificV3{ cls.getMethods.filter(_.getName.contains("foo")).foreach(println) } -} \ No newline at end of file +} diff --git a/sbt-test/unroll-annot/methodWithImplicit/v1/src/main/scala/Unrolled.scala b/tests/run/unroll-methodWithImplicit-integration/Unrolled_1.scala similarity index 74% rename from sbt-test/unroll-annot/methodWithImplicit/v1/src/main/scala/Unrolled.scala rename to tests/run/unroll-methodWithImplicit-integration/Unrolled_1.scala index 44137480e239..1de415b26952 100644 --- a/sbt-test/unroll-annot/methodWithImplicit/v1/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-methodWithImplicit-integration/Unrolled_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll class Unrolled{ diff --git a/sbt-test/unroll-annot/methodWithImplicit/v2/src/main/scala/Unrolled.scala b/tests/run/unroll-methodWithImplicit-integration/Unrolled_2.scala similarity index 84% rename from sbt-test/unroll-annot/methodWithImplicit/v2/src/main/scala/Unrolled.scala rename to tests/run/unroll-methodWithImplicit-integration/Unrolled_2.scala index b2bf28bfb1db..01a5d2fb037a 100644 --- a/sbt-test/unroll-annot/methodWithImplicit/v2/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-methodWithImplicit-integration/Unrolled_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/sbt-test/unroll-annot/methodWithImplicit/v3/src/main/scala/Unrolled.scala b/tests/run/unroll-methodWithImplicit-integration/Unrolled_3.scala similarity index 86% rename from sbt-test/unroll-annot/methodWithImplicit/v3/src/main/scala/Unrolled.scala rename to tests/run/unroll-methodWithImplicit-integration/Unrolled_3.scala index fee831cef37f..07627f604d76 100644 --- a/sbt-test/unroll-annot/methodWithImplicit/v3/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-methodWithImplicit-integration/Unrolled_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/tests/run/unroll-objectMethod-integration/TestUtils_1.scala b/tests/run/unroll-objectMethod-integration/TestUtils_1.scala new file mode 100644 index 000000000000..e639a54d924b --- /dev/null +++ b/tests/run/unroll-objectMethod-integration/TestUtils_1.scala @@ -0,0 +1,13 @@ +//> using options -experimental +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/tests/run/unroll-objectMethod-integration/Test_4.scala b/tests/run/unroll-objectMethod-integration/Test_4.scala new file mode 100644 index 000000000000..cae956f8b1bf --- /dev/null +++ b/tests/run/unroll-objectMethod-integration/Test_4.scala @@ -0,0 +1,8 @@ +//> using options -experimental +// scalajs: --skip +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/sbt-test/unroll-annot/objectMethod/v1_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-objectMethod-integration/UnrollTestMain_1.scala similarity index 88% rename from sbt-test/unroll-annot/objectMethod/v1_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-objectMethod-integration/UnrollTestMain_1.scala index 23f8e15b40cb..9cba53c444ce 100644 --- a/sbt-test/unroll-annot/objectMethod/v1_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-objectMethod-integration/UnrollTestMain_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/objectMethod/v2_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-objectMethod-integration/UnrollTestMain_2.scala similarity index 90% rename from sbt-test/unroll-annot/objectMethod/v2_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-objectMethod-integration/UnrollTestMain_2.scala index ee5337bd4689..2fe609543d51 100644 --- a/sbt-test/unroll-annot/objectMethod/v2_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-objectMethod-integration/UnrollTestMain_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/objectMethod/v3_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-objectMethod-integration/UnrollTestMain_3.scala similarity index 93% rename from sbt-test/unroll-annot/objectMethod/v3_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-objectMethod-integration/UnrollTestMain_3.scala index b1996bd6d5d7..e6d442d3bf7c 100644 --- a/sbt-test/unroll-annot/objectMethod/v3_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-objectMethod-integration/UnrollTestMain_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/objectMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/tests/run/unroll-objectMethod-integration/UnrollTestPlatformSpecific_3.scala similarity index 97% rename from sbt-test/unroll-annot/objectMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala rename to tests/run/unroll-objectMethod-integration/UnrollTestPlatformSpecific_3.scala index 2b5578dd482d..6f399649b4b8 100644 --- a/sbt-test/unroll-annot/objectMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala +++ b/tests/run/unroll-objectMethod-integration/UnrollTestPlatformSpecific_3.scala @@ -1,3 +1,5 @@ +//> using options -experimental +// scalajs: --skip package unroll object UnrollTestPlatformSpecificV3{ @@ -45,4 +47,4 @@ object UnrollTestPlatformSpecificV3{ ) } -} \ No newline at end of file +} diff --git a/sbt-test/unroll-annot/objectMethod/v1/src/main/scala/Unrolled.scala b/tests/run/unroll-objectMethod-integration/Unrolled_1.scala similarity index 70% rename from sbt-test/unroll-annot/objectMethod/v1/src/main/scala/Unrolled.scala rename to tests/run/unroll-objectMethod-integration/Unrolled_1.scala index f4559bc2a820..042ab3180cdc 100644 --- a/sbt-test/unroll-annot/objectMethod/v1/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-objectMethod-integration/Unrolled_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll object Unrolled{ diff --git a/sbt-test/unroll-annot/objectMethod/v2/src/main/scala/Unrolled.scala b/tests/run/unroll-objectMethod-integration/Unrolled_2.scala similarity index 81% rename from sbt-test/unroll-annot/objectMethod/v2/src/main/scala/Unrolled.scala rename to tests/run/unroll-objectMethod-integration/Unrolled_2.scala index c72097c2288c..bfef86beb6b2 100644 --- a/sbt-test/unroll-annot/objectMethod/v2/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-objectMethod-integration/Unrolled_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/sbt-test/unroll-annot/objectMethod/v3/src/main/scala/Unrolled.scala b/tests/run/unroll-objectMethod-integration/Unrolled_3.scala similarity index 84% rename from sbt-test/unroll-annot/objectMethod/v3/src/main/scala/Unrolled.scala rename to tests/run/unroll-objectMethod-integration/Unrolled_3.scala index 3491b42e95ef..c76521e731d8 100644 --- a/sbt-test/unroll-annot/objectMethod/v3/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-objectMethod-integration/Unrolled_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/tests/run/unroll-primaryConstructor-integration/TestUtils_1.scala b/tests/run/unroll-primaryConstructor-integration/TestUtils_1.scala new file mode 100644 index 000000000000..e639a54d924b --- /dev/null +++ b/tests/run/unroll-primaryConstructor-integration/TestUtils_1.scala @@ -0,0 +1,13 @@ +//> using options -experimental +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/tests/run/unroll-primaryConstructor-integration/Test_4.scala b/tests/run/unroll-primaryConstructor-integration/Test_4.scala new file mode 100644 index 000000000000..cae956f8b1bf --- /dev/null +++ b/tests/run/unroll-primaryConstructor-integration/Test_4.scala @@ -0,0 +1,8 @@ +//> using options -experimental +// scalajs: --skip +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/sbt-test/unroll-annot/primaryConstructor/v1_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-primaryConstructor-integration/UnrollTestMain_1.scala similarity index 88% rename from sbt-test/unroll-annot/primaryConstructor/v1_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-primaryConstructor-integration/UnrollTestMain_1.scala index 9bed955a9bc8..ac80b2a5734b 100644 --- a/sbt-test/unroll-annot/primaryConstructor/v1_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-primaryConstructor-integration/UnrollTestMain_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/secondaryConstructor/v2_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-primaryConstructor-integration/UnrollTestMain_2.scala similarity index 91% rename from sbt-test/unroll-annot/secondaryConstructor/v2_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-primaryConstructor-integration/UnrollTestMain_2.scala index 7a88d263a213..5140d999ee9b 100644 --- a/sbt-test/unroll-annot/secondaryConstructor/v2_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-primaryConstructor-integration/UnrollTestMain_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/secondaryConstructor/v3_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-primaryConstructor-integration/UnrollTestMain_3.scala similarity index 93% rename from sbt-test/unroll-annot/secondaryConstructor/v3_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-primaryConstructor-integration/UnrollTestMain_3.scala index f05ca8808c3d..c80c33672d3e 100644 --- a/sbt-test/unroll-annot/secondaryConstructor/v3_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-primaryConstructor-integration/UnrollTestMain_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/secondaryConstructor/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/tests/run/unroll-primaryConstructor-integration/UnrollTestPlatformSpecific_3.scala similarity index 94% rename from sbt-test/unroll-annot/secondaryConstructor/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala rename to tests/run/unroll-primaryConstructor-integration/UnrollTestPlatformSpecific_3.scala index 07dee69cd8a7..e06a38502c62 100644 --- a/sbt-test/unroll-annot/secondaryConstructor/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala +++ b/tests/run/unroll-primaryConstructor-integration/UnrollTestPlatformSpecific_3.scala @@ -1,3 +1,5 @@ +//> using options -experimental +// scalajs: --skip package unroll object UnrollTestPlatformSpecificV3{ @@ -30,4 +32,4 @@ object UnrollTestPlatformSpecificV3{ cls.getConstructors.foreach(println) } -} \ No newline at end of file +} diff --git a/sbt-test/unroll-annot/primaryConstructor/v1/src/main/scala/Unrolled.scala b/tests/run/unroll-primaryConstructor-integration/Unrolled_1.scala similarity index 70% rename from sbt-test/unroll-annot/primaryConstructor/v1/src/main/scala/Unrolled.scala rename to tests/run/unroll-primaryConstructor-integration/Unrolled_1.scala index c7574f4346e0..0ddd25a70127 100644 --- a/sbt-test/unroll-annot/primaryConstructor/v1/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-primaryConstructor-integration/Unrolled_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll class Unrolled(s: String, n: Int = 1){ diff --git a/sbt-test/unroll-annot/primaryConstructor/v2/src/main/scala/Unrolled.scala b/tests/run/unroll-primaryConstructor-integration/Unrolled_2.scala similarity index 81% rename from sbt-test/unroll-annot/primaryConstructor/v2/src/main/scala/Unrolled.scala rename to tests/run/unroll-primaryConstructor-integration/Unrolled_2.scala index 40bf19a16901..c8558df1af55 100644 --- a/sbt-test/unroll-annot/primaryConstructor/v2/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-primaryConstructor-integration/Unrolled_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/sbt-test/unroll-annot/primaryConstructor/v3/src/main/scala/Unrolled.scala b/tests/run/unroll-primaryConstructor-integration/Unrolled_3.scala similarity index 83% rename from sbt-test/unroll-annot/primaryConstructor/v3/src/main/scala/Unrolled.scala rename to tests/run/unroll-primaryConstructor-integration/Unrolled_3.scala index b570bcaadbb3..c6be439e1dec 100644 --- a/sbt-test/unroll-annot/primaryConstructor/v3/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-primaryConstructor-integration/Unrolled_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/tests/run/unroll-secondParameterList-integration/TestUtils_1.scala b/tests/run/unroll-secondParameterList-integration/TestUtils_1.scala new file mode 100644 index 000000000000..e639a54d924b --- /dev/null +++ b/tests/run/unroll-secondParameterList-integration/TestUtils_1.scala @@ -0,0 +1,13 @@ +//> using options -experimental +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/tests/run/unroll-secondParameterList-integration/Test_4.scala b/tests/run/unroll-secondParameterList-integration/Test_4.scala new file mode 100644 index 000000000000..cae956f8b1bf --- /dev/null +++ b/tests/run/unroll-secondParameterList-integration/Test_4.scala @@ -0,0 +1,8 @@ +//> using options -experimental +// scalajs: --skip +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/sbt-test/unroll-annot/secondParameterList/v1_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-secondParameterList-integration/UnrollTestMain_1.scala similarity index 86% rename from sbt-test/unroll-annot/secondParameterList/v1_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-secondParameterList-integration/UnrollTestMain_1.scala index ef6fd3b68102..163225fb93bf 100644 --- a/sbt-test/unroll-annot/secondParameterList/v1_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-secondParameterList-integration/UnrollTestMain_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/secondParameterList/v2_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-secondParameterList-integration/UnrollTestMain_2.scala similarity index 91% rename from sbt-test/unroll-annot/secondParameterList/v2_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-secondParameterList-integration/UnrollTestMain_2.scala index 09c06869f617..de0776c569db 100644 --- a/sbt-test/unroll-annot/secondParameterList/v2_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-secondParameterList-integration/UnrollTestMain_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/secondParameterList/v3_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-secondParameterList-integration/UnrollTestMain_3.scala similarity index 93% rename from sbt-test/unroll-annot/secondParameterList/v3_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-secondParameterList-integration/UnrollTestMain_3.scala index 468f24956d94..b18a7f18b4d0 100644 --- a/sbt-test/unroll-annot/secondParameterList/v3_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-secondParameterList-integration/UnrollTestMain_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/secondParameterList/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/tests/run/unroll-secondParameterList-integration/UnrollTestPlatformSpecific_3.scala similarity index 95% rename from sbt-test/unroll-annot/secondParameterList/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala rename to tests/run/unroll-secondParameterList-integration/UnrollTestPlatformSpecific_3.scala index 4663b2220cd7..49dff864f49b 100644 --- a/sbt-test/unroll-annot/secondParameterList/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala +++ b/tests/run/unroll-secondParameterList-integration/UnrollTestPlatformSpecific_3.scala @@ -1,3 +1,5 @@ +//> using options -experimental +// scalajs: --skip package unroll object UnrollTestPlatformSpecificV3{ @@ -27,4 +29,4 @@ object UnrollTestPlatformSpecificV3{ cls.getMethods.filter(_.getName.contains("foo")).foreach(println) } -} \ No newline at end of file +} diff --git a/sbt-test/unroll-annot/secondParameterList/v1/src/main/scala/Unrolled.scala b/tests/run/unroll-secondParameterList-integration/Unrolled_1.scala similarity index 72% rename from sbt-test/unroll-annot/secondParameterList/v1/src/main/scala/Unrolled.scala rename to tests/run/unroll-secondParameterList-integration/Unrolled_1.scala index f9ddac201c59..fbe0a58dca24 100644 --- a/sbt-test/unroll-annot/secondParameterList/v1/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-secondParameterList-integration/Unrolled_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll class Unrolled{ diff --git a/sbt-test/unroll-annot/secondParameterList/v2/src/main/scala/Unrolled.scala b/tests/run/unroll-secondParameterList-integration/Unrolled_2.scala similarity index 84% rename from sbt-test/unroll-annot/secondParameterList/v2/src/main/scala/Unrolled.scala rename to tests/run/unroll-secondParameterList-integration/Unrolled_2.scala index 73f635b24545..68c4170f6f6e 100644 --- a/sbt-test/unroll-annot/secondParameterList/v2/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-secondParameterList-integration/Unrolled_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/sbt-test/unroll-annot/secondParameterList/v3/src/main/scala/Unrolled.scala b/tests/run/unroll-secondParameterList-integration/Unrolled_3.scala similarity index 85% rename from sbt-test/unroll-annot/secondParameterList/v3/src/main/scala/Unrolled.scala rename to tests/run/unroll-secondParameterList-integration/Unrolled_3.scala index a903fbed4d93..ddbe8c4cfaf4 100644 --- a/sbt-test/unroll-annot/secondParameterList/v3/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-secondParameterList-integration/Unrolled_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/tests/run/unroll-secondaryConstructor-integration/TestUtils_1.scala b/tests/run/unroll-secondaryConstructor-integration/TestUtils_1.scala new file mode 100644 index 000000000000..e639a54d924b --- /dev/null +++ b/tests/run/unroll-secondaryConstructor-integration/TestUtils_1.scala @@ -0,0 +1,13 @@ +//> using options -experimental +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/tests/run/unroll-secondaryConstructor-integration/Test_4.scala b/tests/run/unroll-secondaryConstructor-integration/Test_4.scala new file mode 100644 index 000000000000..cae956f8b1bf --- /dev/null +++ b/tests/run/unroll-secondaryConstructor-integration/Test_4.scala @@ -0,0 +1,8 @@ +//> using options -experimental +// scalajs: --skip +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/sbt-test/unroll-annot/secondaryConstructor/v1_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_1.scala similarity index 88% rename from sbt-test/unroll-annot/secondaryConstructor/v1_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_1.scala index 9bed955a9bc8..ac80b2a5734b 100644 --- a/sbt-test/unroll-annot/secondaryConstructor/v1_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/primaryConstructor/v2_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_2.scala similarity index 91% rename from sbt-test/unroll-annot/primaryConstructor/v2_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_2.scala index 7a88d263a213..5140d999ee9b 100644 --- a/sbt-test/unroll-annot/primaryConstructor/v2_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/primaryConstructor/v3_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_3.scala similarity index 93% rename from sbt-test/unroll-annot/primaryConstructor/v3_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_3.scala index f05ca8808c3d..c80c33672d3e 100644 --- a/sbt-test/unroll-annot/primaryConstructor/v3_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/primaryConstructor/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/tests/run/unroll-secondaryConstructor-integration/UnrollTestPlatformSpecific_3.scala similarity index 94% rename from sbt-test/unroll-annot/primaryConstructor/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala rename to tests/run/unroll-secondaryConstructor-integration/UnrollTestPlatformSpecific_3.scala index 07dee69cd8a7..e06a38502c62 100644 --- a/sbt-test/unroll-annot/primaryConstructor/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala +++ b/tests/run/unroll-secondaryConstructor-integration/UnrollTestPlatformSpecific_3.scala @@ -1,3 +1,5 @@ +//> using options -experimental +// scalajs: --skip package unroll object UnrollTestPlatformSpecificV3{ @@ -30,4 +32,4 @@ object UnrollTestPlatformSpecificV3{ cls.getConstructors.foreach(println) } -} \ No newline at end of file +} diff --git a/sbt-test/unroll-annot/secondaryConstructor/v1/src/main/scala/Unrolled.scala b/tests/run/unroll-secondaryConstructor-integration/Unrolled_1.scala similarity index 78% rename from sbt-test/unroll-annot/secondaryConstructor/v1/src/main/scala/Unrolled.scala rename to tests/run/unroll-secondaryConstructor-integration/Unrolled_1.scala index 529a3fc66de3..855b20efa8f6 100644 --- a/sbt-test/unroll-annot/secondaryConstructor/v1/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-secondaryConstructor-integration/Unrolled_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll class Unrolled(){ diff --git a/sbt-test/unroll-annot/secondaryConstructor/v2/src/main/scala/Unrolled.scala b/tests/run/unroll-secondaryConstructor-integration/Unrolled_2.scala similarity index 85% rename from sbt-test/unroll-annot/secondaryConstructor/v2/src/main/scala/Unrolled.scala rename to tests/run/unroll-secondaryConstructor-integration/Unrolled_2.scala index 382066698f31..b8f1f4f28328 100644 --- a/sbt-test/unroll-annot/secondaryConstructor/v2/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-secondaryConstructor-integration/Unrolled_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/sbt-test/unroll-annot/secondaryConstructor/v3/src/main/scala/Unrolled.scala b/tests/run/unroll-secondaryConstructor-integration/Unrolled_3.scala similarity index 86% rename from sbt-test/unroll-annot/secondaryConstructor/v3/src/main/scala/Unrolled.scala rename to tests/run/unroll-secondaryConstructor-integration/Unrolled_3.scala index 89411d6576ca..1da3e0d69ec6 100644 --- a/sbt-test/unroll-annot/secondaryConstructor/v3/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-secondaryConstructor-integration/Unrolled_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/tests/run/unroll-traitMethod-integration/TestUtils_1.scala b/tests/run/unroll-traitMethod-integration/TestUtils_1.scala new file mode 100644 index 000000000000..e639a54d924b --- /dev/null +++ b/tests/run/unroll-traitMethod-integration/TestUtils_1.scala @@ -0,0 +1,13 @@ +//> using options -experimental +package unroll + +object TestUtils { + def logAssertStartsWith(actual: String, expected: String): Unit = { + assert(actual.startsWith(expected)) + val suffix = { + val suffix0 = actual.stripPrefix(expected) + if (suffix0.isEmpty) "" else s""" + "$suffix0"""" + } + println(s"""Assertion passed: found "$expected"$suffix""") + } +} diff --git a/tests/run/unroll-traitMethod-integration/Test_4.scala b/tests/run/unroll-traitMethod-integration/Test_4.scala new file mode 100644 index 000000000000..cae956f8b1bf --- /dev/null +++ b/tests/run/unroll-traitMethod-integration/Test_4.scala @@ -0,0 +1,8 @@ +//> using options -experimental +// scalajs: --skip +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/sbt-test/unroll-annot/traitMethod/v1_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-traitMethod-integration/UnrollTestMain_1.scala similarity index 92% rename from sbt-test/unroll-annot/traitMethod/v1_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-traitMethod-integration/UnrollTestMain_1.scala index f1c7c8bb88a4..b290c95e1f9c 100644 --- a/sbt-test/unroll-annot/traitMethod/v1_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-traitMethod-integration/UnrollTestMain_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/traitMethod/v2_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-traitMethod-integration/UnrollTestMain_2.scala similarity index 94% rename from sbt-test/unroll-annot/traitMethod/v2_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-traitMethod-integration/UnrollTestMain_2.scala index 30eb52263e50..6721b302e3f1 100644 --- a/sbt-test/unroll-annot/traitMethod/v2_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-traitMethod-integration/UnrollTestMain_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/traitMethod/v3_app/src/main/scala/UnrollTestMain.scala b/tests/run/unroll-traitMethod-integration/UnrollTestMain_3.scala similarity index 95% rename from sbt-test/unroll-annot/traitMethod/v3_app/src/main/scala/UnrollTestMain.scala rename to tests/run/unroll-traitMethod-integration/UnrollTestMain_3.scala index 89154b958161..d4091763a86f 100644 --- a/sbt-test/unroll-annot/traitMethod/v3_app/src/main/scala/UnrollTestMain.scala +++ b/tests/run/unroll-traitMethod-integration/UnrollTestMain_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import unroll.TestUtils.logAssertStartsWith diff --git a/sbt-test/unroll-annot/traitMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala b/tests/run/unroll-traitMethod-integration/UnrollTestPlatformSpecific_3.scala similarity index 94% rename from sbt-test/unroll-annot/traitMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala rename to tests/run/unroll-traitMethod-integration/UnrollTestPlatformSpecific_3.scala index e8367679233f..88b0e2255741 100644 --- a/sbt-test/unroll-annot/traitMethod/v3_app/src/main/scala/UnrollTestPlatformSpecific.scala +++ b/tests/run/unroll-traitMethod-integration/UnrollTestPlatformSpecific_3.scala @@ -1,3 +1,5 @@ +//> using options -experimental +// scalajs: --skip package unroll object UnrollTestPlatformSpecificV3{ @@ -25,4 +27,4 @@ object UnrollTestPlatformSpecificV3{ cls.getMethods.filter(_.getName.contains("foo")).foreach(println) } -} \ No newline at end of file +} diff --git a/sbt-test/unroll-annot/traitMethod/v1/src/main/scala/Unrolled.scala b/tests/run/unroll-traitMethod-integration/Unrolled_1.scala similarity index 77% rename from sbt-test/unroll-annot/traitMethod/v1/src/main/scala/Unrolled.scala rename to tests/run/unroll-traitMethod-integration/Unrolled_1.scala index eaadde758ac7..aa9375698103 100644 --- a/sbt-test/unroll-annot/traitMethod/v1/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-traitMethod-integration/Unrolled_1.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll trait Unrolled{ diff --git a/sbt-test/unroll-annot/traitMethod/v2/src/main/scala/Unrolled.scala b/tests/run/unroll-traitMethod-integration/Unrolled_2.scala similarity index 84% rename from sbt-test/unroll-annot/traitMethod/v2/src/main/scala/Unrolled.scala rename to tests/run/unroll-traitMethod-integration/Unrolled_2.scala index d0bc2f595020..242054d41be4 100644 --- a/sbt-test/unroll-annot/traitMethod/v2/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-traitMethod-integration/Unrolled_2.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/sbt-test/unroll-annot/traitMethod/v3/src/main/scala/Unrolled.scala b/tests/run/unroll-traitMethod-integration/Unrolled_3.scala similarity index 86% rename from sbt-test/unroll-annot/traitMethod/v3/src/main/scala/Unrolled.scala rename to tests/run/unroll-traitMethod-integration/Unrolled_3.scala index 4478d57a60f8..398ddbb4da22 100644 --- a/sbt-test/unroll-annot/traitMethod/v3/src/main/scala/Unrolled.scala +++ b/tests/run/unroll-traitMethod-integration/Unrolled_3.scala @@ -1,3 +1,4 @@ +//> using options -experimental package unroll import scala.annotation.unroll diff --git a/tests/warn/21681.check b/tests/warn/21681.check index adf3586e6e0b..5156a600d609 100644 --- a/tests/warn/21681.check +++ b/tests/warn/21681.check @@ -5,4 +5,3 @@ | not as an assignment. | | To assign a value, use curly braces: `{age = 29}`. - | This can be rewritten automatically under -rewrite -source 3.6-migration. diff --git a/tests/warn/21681b.check b/tests/warn/21681b.check index 09c007f351b4..dd28df3168ed 100644 --- a/tests/warn/21681b.check +++ b/tests/warn/21681b.check @@ -5,4 +5,3 @@ | not as an assignment. | | To assign a value, use curly braces: `{age = 29}`. - | This can be rewritten automatically under -rewrite -source 3.6-migration. diff --git a/tests/warn/21681c.check b/tests/warn/21681c.check index 20273f723384..bfb62618cbb9 100644 --- a/tests/warn/21681c.check +++ b/tests/warn/21681c.check @@ -5,4 +5,3 @@ | not as an assignment. | | To assign a value, use curly braces: `{age = 29}`. - | This can be rewritten automatically under -rewrite -source 3.6-migration. diff --git a/tests/warn/21770.check b/tests/warn/21770.check index 7853d77a423c..6c978a6078a2 100644 --- a/tests/warn/21770.check +++ b/tests/warn/21770.check @@ -5,4 +5,3 @@ | not as an assignment. | | To assign a value, use curly braces: `{cache = Some(i)}`. - | This can be rewritten automatically under -rewrite -source 3.6-migration.