diff --git a/compiler/src/dotty/tools/dotc/CompilationUnit.scala b/compiler/src/dotty/tools/dotc/CompilationUnit.scala index 0d755797d026..9c985ecd84b3 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.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/Compiler.scala b/compiler/src/dotty/tools/dotc/Compiler.scala index d8ba1ab5dc2e..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 diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 27ea5771c30b..f15595fbcdb6 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/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index 85ff51bc19de..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 | 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/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/SymDenotations.scala b/compiler/src/dotty/tools/dotc/core/SymDenotations.scala index 53df2d4aa39a..670663ff2161 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) && !ctx.mode.is(Mode.ResolveFromTASTy)) && ctx.isTyper || is(ModuleVal, butNot = Package) && moduleClass.isAbsent(canForce) } diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index c07121a52191..d6f2812dad0d 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -1563,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 @@ -1572,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) @@ -1580,6 +1581,9 @@ class TreeUnpickler(reader: TastyReader, val d = ownerTpe.decl(name).atSignature(sig, target) (if !d.exists then lookupInSuper else d).asSeenFrom(prefix) + val denot = inContext(ctx.addMode(Mode.ResolveFromTASTy)): + searchDenot // able to resolve Invisible members + makeSelect(qual, name, denot) case REPEATED => val elemtpt = readTpt() 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 ab4f40677371..20347f706502 100644 --- a/compiler/src/dotty/tools/dotc/reporting/messages.scala +++ b/compiler/src/dotty/tools/dotc/reporting/messages.scala @@ -3334,14 +3334,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. |""" @@ -3408,3 +3408,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"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 + 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 898517806e50..02f5434aa549 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" @@ -119,8 +120,31 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => private var inJavaAnnot: Boolean = false + 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 = + seenUnrolledMethods.getOrElseUpdate(method, { + 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 + true + }) + def withNoCheckNews[T](ts: List[New])(op: => T): T = { val saved = noCheckNews noCheckNews ++= ts @@ -199,6 +223,12 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => tree } + private def registerIfUnrolledParam(sym: Symbol)(using Context): Unit = + if sym.hasAnnotation(defn.UnrollAnnot) && isValidUnrolledMethod(sym.owner, sym.sourcePos) then + val cls = sym.enclosingClass + val additions = Array(cls, cls.linkedClass).filter(_ != NoSymbol) + ctx.compilationUnit.unrolledClasses ++= additions + private def processValOrDefDef(tree: Tree)(using Context): tree.type = val sym = tree.symbol tree match @@ -215,6 +245,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/UnrollDefinitions.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala new file mode 100644 index 000000000000..b431a81afeac --- /dev/null +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala @@ -0,0 +1,359 @@ +package dotty.tools.dotc +package transform + +import ast.tpd +import ast.Trees.* +import core.* +import Flags.* +import Decorators.* +import Contexts.* +import Symbols.* +import Constants.Constant +import Decorators.* +import DenotTransformers.IdentityDenotTransformer +import Names.* +import dotty.tools.dotc.core.NameKinds.DefaultGetterName + +import dotty.tools.dotc.core.Types.{MethodType, NamedType, PolyType, Type, NoPrefix, NoType} + +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 +import dotty.tools.dotc.util.Spans.Span + +/**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 => + + import tpd.* + + 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 + + override def description: String = UnrollDefinitions.description + + override def changesMembers: Boolean = true + + override def run(using Context): Unit = + if ctx.compilationUnit.hasUnrollDefs then + super.run // create and run the transformer on the current compilation unit + + def newTransformer(using Context): Transformer = + UnrollingTransformer(ctx.compilationUnit.unrolledClasses) + + type ComputedIndices = List[(Int, List[Int])] + type ComputeIndices = Context ?=> Symbol => ComputedIndices + + private class UnrollingTransformer(unrolledClasses: Set[Symbol]) extends Transformer { + private val unrolledDefs = initializeUnrolledDefs() + + def computeIndices(annotated: Symbol)(using Context): ComputedIndices = + unrolledDefs.getOrElseUpdate(annotated, { + if annotated.name.is(DefaultGetterName) then + // 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 + .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 + + override def transform(tree: tpd.Tree)(using Context): tpd.Tree = tree match + case tree @ TypeDef(_, impl: Template) if unrolledClasses(tree.symbol) => + super.transform(cpy.TypeDef(tree)(rhs = unrollTemplate(impl, computeIndices))) + case tree => + super.transform(tree) + } + + 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 + + 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}" + else "" + i"in ${sym.owner}${lineDesc}" + } + + private def findUnrollAnnotations(params: List[Symbol])(using Context): List[Int] = { + params + .zipWithIndex + .collect { + case (v, i) if v.hasAnnotation(defn.UnrollAnnot) => + i + } + } + + 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 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, + annotatedParamListIndex: Int, + isCaseApply: Boolean)(using Context): DefDef = { + + def initNewForwarder()(using Context): (TermSymbol, List[List[Symbol]]) = { + val forwarderDefSymbol0 = Symbols.newSymbol( + defdef.symbol.owner, + defdef.name, + defdef.symbol.flags &~ HasDefaultParams | + Invisible | Synthetic, + NoType, // fill in later + coord = defdef.span + ).entered + + val newParamSymMappings = extractParamSymss(copyParamSym(_, forwarderDefSymbol0)) + val (oldParams, newParams) = newParamSymMappings.flatten.unzip + + val newParamSymLists0 = + newParamSymMappings.map: pairs => + pairs.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[T](onSymbol: Symbol => T): List[List[T]] = + defdef.paramss.zipWithIndex.map{ case (ps, i) => + if (i == annotatedParamListIndex) ps.take(paramIndex).map(p => onSymbol(p.symbol)) + else ps.map(p => onSymbol(p.symbol)) + } + + val (forwarderDefSymbol, newParamSymLists) = initNewForwarder() + + def forwarderRhs(): tpd.Tree = { + val defaultOffset = defdef.paramss + .iterator + .take(annotatedParamListIndex) + .filter(!isTypeClause(_)) + .map(_.size) + .sum + + val defaultCalls = Range(paramIndex, paramCount).map(n => + + 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}" + 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 + refTree.select(name) + + val inner = if (defdef.symbol.isConstructor) { + makeSelect(ref(defdef.symbol.owner.companionModule), + DefaultGetterName(defdef.name, n + defaultOffset)) + } else if (isCaseApply) { + makeSelect(ref(defdef.symbol.owner.companionModule), + DefaultGetterName(termName(""), n + defaultOffset)) + } else { + makeSelect(This(defdef.symbol.owner.asClass), + DefaultGetterName(defdef.name, n + defaultOffset)) + } + + newParamSymLists + .take(annotatedParamListIndex) + .map(_.map(ref)) + .foldLeft(inner)(_.appliedToArgs(_)) + ) + + val forwarderInner: Tree = + This(defdef.symbol.owner.asClass).select(defdef.symbol) + + val forwarderCallArgs = + newParamSymLists.zipWithIndex.map{case (ps, i) => + if (i == annotatedParamListIndex) ps.map(ref).take(nextParamIndex) ++ defaultCalls + else ps.map(ref) + } + + val forwarderCall0 = forwarderCallArgs.foldLeft(forwarderInner)(_.appliedToArgs(_)) + + val forwarderCall = + if (!defdef.symbol.isConstructor) forwarderCall0 + else Block(List(forwarderCall0), Literal(Constant(()))) + + forwarderCall + } + + val forwarderDef = + tpd.DefDef(forwarderDefSymbol, rhs = forwarderRhs()).withSpan(defdef.span) + + forwarderDef + } + + private 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)) + ) + ) + ) + } :+ CaseDef( + Underscore(defn.IntType), + EmptyTree, + defdef.rhs + ) + ) + ).setDefTree + } + + private enum Gen: + case Substitute(origin: Symbol, newDef: DefDef) + case Forwarders(origin: Symbol, forwarders: List[DefDef]) + + def origin: Symbol + def extras: List[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 == nme.copy && defdef.symbol.owner.is(CaseClass) + + val isCaseApply = + defdef.name == nme.apply && 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 + else if (isCaseApply) defdef.symbol.owner.companionClass.primaryConstructor + else if (isCaseFromProduct) defdef.symbol.owner.companionClass.primaryConstructor + else defdef.symbol + + compute(annotated) match { + case Nil => None + case (paramClauseIndex, annotationIndices) :: Nil => + val paramCount = annotated.paramSymss(paramClauseIndex).size + if isCaseFromProduct then + Some(Gen.Substitute( + origin = defdef.symbol, + newDef = generateFromProduct(annotationIndices, paramCount, defdef) + )) + else + val generatedDefs = + val indices = (annotationIndices :+ paramCount).sliding(2).toList.reverse + indices.foldLeft(List.empty[DefDef]): + case (defdefs, paramIndex :: nextParamIndex :: Nil) => + generateSingleForwarder( + defdef, + paramIndex, + paramCount, + nextParamIndex, + paramClauseIndex, + isCaseApply + ) :: defdefs + case _ => unreachable("sliding with at least 2 elements") + Some(Gen.Forwarders(origin = defdef.symbol, forwarders = generatedDefs)) + + case multiple => + report.error("Cannot have multiple parameter lists containing `@unroll` annotation", defdef.srcPos) + None + } + + case _ => None + } + + 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.collect({ case s: Gen.Substitute => s.origin }).toSet + val otherDecls = tmpl.body.filterNot(d => d.symbol.exists && bodySubs(d.symbol)) + + if allGenerated.nonEmpty then + val byName = (tmpl.constr :: otherDecls).groupMap(_.symbol.name.toString)(_.symbol) + for + syntheticDefs <- allGenerated + dcl <- syntheticDefs.extras + do + val replaced = dcl.symbol + byName.get(dcl.name.toString).foreach { syms => + 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. + |Please remove the clashing definition, or the @unroll annotation. + |Unrolled from ${hl(src.showDcl)} ${symLocation(src)}""".stripMargin, existing.srcPos) + } + end if + + cpy.Template(tmpl)( + tmpl.constr, + tmpl.parents, + tmpl.derived, + tmpl.self, + otherDecls ++ allGenerated.flatMap(_.extras) + ) + } + +} + +object UnrollDefinitions: + val name: String = "unrollDefs" + val description: String = "generates forwarders for methods annotated with @unroll" diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 3a2f16438c88..2a40cb78f442 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/CrossVersionChecks.scala b/compiler/src/dotty/tools/dotc/typer/CrossVersionChecks.scala index 6020431672b9..8f8a68aa5735 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(IllegalUnrollPlacement(None), 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/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 9b7e4fe36668..ee22f093c08c 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 _ => diff --git a/compiler/test/dotty/tools/utils.scala b/compiler/test/dotty/tools/utils.scala index d17edbaa855e..a5ebf0d59ec0 100644 --- a/compiler/test/dotty/tools/utils.scala +++ b/compiler/test/dotty/tools/utils.scala @@ -57,25 +57,54 @@ def assertThrows[T <: Throwable: ClassTag](p: T => Boolean)(body: => Any): Unit case NonFatal(other) => throw AssertionError(s"Wrong exception: expected ${implicitly[ClassTag[T]]} but was ${other.getClass.getName}").tap(_.addSuppressed(other)) end assertThrows +enum TestPlatform: + case JVM, ScalaJS + override def toString: String = this match + case JVM => "jvm" + case ScalaJS => "scala-js" + +object TestPlatform: + def named(s: String): TestPlatform = s match + case "jvm" => TestPlatform.JVM + case "scala-js" => TestPlatform.ScalaJS + case _ => throw IllegalArgumentException(s) + /** Famous tool names in the ecosystem. Used for tool args in test files. */ enum ToolName: - case Scala, Scalac, Java, Javac, ScalaJS, Test + case Scala, Scalac, Java, Javac, ScalaJS, Test, Target object ToolName: def named(s: String): ToolName = values.find(_.toString.equalsIgnoreCase(s)).getOrElse(throw IllegalArgumentException(s)) type ToolArgs = Map[ToolName, List[String]] +type PlatformFiles = Map[TestPlatform, List[String]] /** Take a prefix of each file, extract tool args, parse, and combine. * Arg parsing respects quotation marks. Result is a map from ToolName to the combined tokens. */ def toolArgsFor(files: List[JPath], charset: Charset = UTF_8): ToolArgs = - files.foldLeft(Map.empty[ToolName, List[String]]) { (res, path) => + val (_, toolArgs) = platformAndToolArgsFor(files, charset) + toolArgs + +/** Take a prefix of each file, extract tool args, parse, and combine. + * Arg parsing respects quotation marks. Result is a map from ToolName to the combined tokens. + * If the ToolName is Target, then also accumulate the file name associated with the given platform. + */ +def platformAndToolArgsFor(files: List[JPath], charset: Charset = UTF_8): (PlatformFiles, ToolArgs) = + files.foldLeft(Map.empty[TestPlatform, List[String]] -> Map.empty[ToolName, List[String]]) { (res, path) => val toolargs = toolArgsParse(resource(Files.lines(path, charset))(_.limit(10).toScala(List)), Some(path.toString)) toolargs.foldLeft(res) { - case (acc, (tool, args)) => + case ((plat, acc), (tool, args)) => val name = ToolName.named(tool) val tokens = CommandLineParser.tokenize(args) - acc.updatedWith(name)(v0 => v0.map(_ ++ tokens).orElse(Some(tokens))) + + val plat1 = if name eq ToolName.Target then + val testPlatform = TestPlatform.named(tokens.head) + val fileName = path.toString + plat.updatedWith(testPlatform)(_.map(fileName :: _).orElse(Some(fileName :: Nil))) + else + plat + + plat1 -> acc.updatedWith(name)(v0 => v0.map(_ ++ tokens).orElse(Some(tokens))) } } @@ -94,6 +123,8 @@ private val toolArg = raw"(?://|/\*| \*) ?(?i:(${ToolName.values.mkString("|")}) /** Directive to specify to vulpix the options to pass to Dotty */ private val directiveOptionsArg = raw"//> using options (.*)".r.unanchored private val directiveJavacOptions = raw"//> using javacOpt (.*)".r.unanchored +private val directiveTargetOptions = raw"//> using target.platform (jvm|scala-js)".r.unanchored +private val directiveUnknown = raw"//> using (.*)".r.unanchored // Inspect the lines for compiler options of the form // `//> using options args`, `// scalajs: args`, `/* scalajs: args`, ` * scalajs: args` etc. @@ -109,6 +140,8 @@ def toolArgsParse(lines: List[String], filename: Option[String]): List[(String,S lines.flatMap { case directiveOptionsArg(args) => List(("scalac", args)) case directiveJavacOptions(args) => List(("javac", args)) + case directiveTargetOptions(platform) => List(("target", platform)) + case directiveUnknown(rest) => sys.error(s"Unknown directive: `//> using ${CommandLineParser.tokenize(rest).headOption.getOrElse("''")}`${filename.fold("")(f => s" in file $f")}") case _ => Nil } diff --git a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala index 7827b94e165a..9a02cd17f5bd 100644 --- a/compiler/test/dotty/tools/vulpix/ParallelTesting.scala +++ b/compiler/test/dotty/tools/vulpix/ParallelTesting.scala @@ -60,6 +60,8 @@ trait ParallelTesting extends RunnerOrchestration { self => /** Contains a list of failed tests to run, if list is empty no tests will run */ def failedTests: Option[List[String]] + protected def testPlatform: TestPlatform = TestPlatform.JVM + /** A test source whose files or directory of files is to be compiled * in a specific way defined by the `Test` */ @@ -343,7 +345,11 @@ trait ParallelTesting extends RunnerOrchestration { self => new JFile(f.getPath.replaceFirst("\\.(scala|java)$", ".check")) } case ts: SeparateCompilationSource => - Option(new JFile(ts.dir.getPath + ".check")) + val platform = + if testSource.allToolArgs.getOrElse(ToolName.Target, Nil).nonEmpty then + s".$testPlatform" + else "" + Option(new JFile(ts.dir.getPath + platform + ".check")) } } @@ -499,7 +505,8 @@ trait ParallelTesting extends RunnerOrchestration { self => val files: Array[JFile] = files0.flatMap(flattenFiles) - val toolArgs = toolArgsFor(files.toList.map(_.toPath), getCharsetFromEncodingOpt(flags0)) + val (platformFiles, toolArgs) = + platformAndToolArgsFor(files.toList.map(_.toPath), getCharsetFromEncodingOpt(flags0)) val spec = raw"(\d+)(\+)?".r val testIsFiltered = toolArgs.get(ToolName.Test) match @@ -557,7 +564,17 @@ trait ParallelTesting extends RunnerOrchestration { self => // If a test contains a Java file that cannot be parsed by Dotty's Java source parser, its // name must contain the string "JAVA_ONLY". val dottyFiles = files.filterNot(_.getName.contains("JAVA_ONLY")).map(_.getPath) - driver.process(allArgs ++ dottyFiles, reporter = reporter) + + val dottyFiles0 = + if platformFiles.isEmpty then dottyFiles + else + val excludedFiles = platformFiles + .collect { case (plat, files) if plat != testPlatform => files } + .flatten + .toSet + dottyFiles.filterNot(excludedFiles) + + driver.process(allArgs ++ dottyFiles0, reporter = reporter) // todo a better mechanism than ONLY. test: -scala-only? val javaFiles = files.filter(_.getName.endsWith(".java")).filterNot(_.getName.contains("SCALA_ONLY")).map(_.getPath) 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/library/src/scala/annotation/unroll.scala b/library/src/scala/annotation/unroll.scala new file mode 100644 index 000000000000..c37b7903d605 --- /dev/null +++ b/library/src/scala/annotation/unroll.scala @@ -0,0 +1,12 @@ +package scala.annotation + +@experimental("under review as part of SIP-61") +/**The `@unroll` annotation is reserved for parameters of classes and methods. + * + * It enables to add new parameters while preserving backwards binary compatibility, + * through code generation of hidden forwarder methods (but visible in the binary API). + * + * Read more about parameter unrolling, and the usage of `@unroll` in the reference documentation: + * https://dotty.epfl.ch/docs/reference/experimental/unrolled-defs.html + */ +final class unroll extends scala.annotation.StaticAnnotation 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 diff --git a/sjs-compiler-tests/test/scala/dotty/tools/dotc/ScalaJSCompilationTests.scala b/sjs-compiler-tests/test/scala/dotty/tools/dotc/ScalaJSCompilationTests.scala index 0f4eb633b770..0e5bd20d5c06 100644 --- a/sjs-compiler-tests/test/scala/dotty/tools/dotc/ScalaJSCompilationTests.scala +++ b/sjs-compiler-tests/test/scala/dotty/tools/dotc/ScalaJSCompilationTests.scala @@ -40,6 +40,8 @@ class ScalaJSCompilationTests extends ParallelTesting { override protected def shouldSkipTestSource(testSource: TestSource): Boolean = testSource.allToolArgs.get(ToolName.ScalaJS).exists(_.contains("--skip")) + override protected def testPlatform: TestPlatform = TestPlatform.ScalaJS + override def runMain(classPath: String, toolArgs: ToolArgs)(implicit summaryReport: SummaryReporting): Status = import scala.concurrent.ExecutionContext.Implicits.global diff --git a/tasty/src/dotty/tools/tasty/TastyFormat.scala b/tasty/src/dotty/tools/tasty/TastyFormat.scala index 8ff590fefec5..de3700c667a4 100644 --- a/tasty/src/dotty/tools/tasty/TastyFormat.scala +++ b/tasty/src/dotty/tools/tasty/TastyFormat.scala @@ -227,7 +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 + INVISIBLE -- invisible during typechecking, except when resolving from TASTy TRACKED -- a tracked class parameter / a dependent class Annotation diff --git a/tests/neg/16463.scala b/tests/neg/16463.scala index 80a84cf02bc8..6011678552b7 100644 --- a/tests/neg/16463.scala +++ b/tests/neg/16463.scala @@ -1,4 +1,4 @@ -//> using scala "3.2.1" +//!> using scala "3.2.1" import scala.compiletime.ops.int._ diff --git a/tests/neg/i19320.scala b/tests/neg/i19320.scala index e802215a1f10..3597f02cf431 100644 --- a/tests/neg/i19320.scala +++ b/tests/neg/i19320.scala @@ -1,7 +1,7 @@ -//> using scala "3.3.1" -//> using dep org.http4s::http4s-ember-client:1.0.0-M40 -//> using dep org.http4s::http4s-ember-server:1.0.0-M40 -//> using dep org.http4s::http4s-dsl:1.0.0-M40 +//!> using scala "3.3.1" +//!> using dep org.http4s::http4s-ember-client:1.0.0-M40 +//!> using dep org.http4s::http4s-ember-server:1.0.0-M40 +//!> using dep org.http4s::http4s-dsl:1.0.0-M40 //import cats.effect.* //import cats.implicits.* 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. --------------------------------------------------------------------------------------------------------------------- diff --git a/tests/neg/unroll-abstractMethod.check b/tests/neg/unroll-abstractMethod.check new file mode 100644 index 000000000000..d0874c8a44d8 --- /dev/null +++ b/tests/neg/unroll-abstractMethod.check @@ -0,0 +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 + | ^ + | 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 + | ^ + | Cannot unroll parameters of method foo: it must not be abstract diff --git a/tests/neg/unroll-abstractMethod.scala b/tests/neg/unroll-abstractMethod.scala new file mode 100644 index 000000000000..8f30fb63b20f --- /dev/null +++ b/tests/neg/unroll-abstractMethod.scala @@ -0,0 +1,11 @@ +//> using options -experimental + +import scala.annotation.unroll + +trait Unrolled { + def foo(s: String, n: Int = 1, @unroll b: Boolean = true): String // error +} + +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 new file mode 100644 index 000000000000..a398c550c461 --- /dev/null +++ b/tests/neg/unroll-clash.check @@ -0,0 +1,12 @@ +-- 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 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 new file mode 100644 index 000000000000..79a75c2ba785 --- /dev/null +++ b/tests/neg/unroll-clash.scala @@ -0,0 +1,18 @@ +//> using options -experimental + +import scala.annotation.unroll + +class Foo { + final def foo(x: Int, @unroll y: Int = 0) = x + y + def foo(x: Int) = { // error + println("Not binary compatible!") + -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/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/neg/unroll-duped.check b/tests/neg/unroll-duped.check new file mode 100644 index 000000000000..9ec09672566b --- /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 + | ^ + | 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 + | ^ + |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 + | ^ + |Cannot 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 new file mode 100644 index 000000000000..96d7528ac338 --- /dev/null +++ b/tests/neg/unroll-illegal.check @@ -0,0 +1,36 @@ +-- [E207] Declaration Error: tests/neg/unroll-illegal.scala:6:6 -------------------------------------------------------- +6 |class UnrollClass // error + | ^ + | @unroll is only allowed on a method parameter +-- [E207] Declaration Error: tests/neg/unroll-illegal.scala:9:6 -------------------------------------------------------- +9 |trait UnrollTrait // error + | ^ + | @unroll is only allowed on a method parameter +-- [E207] Declaration Error: tests/neg/unroll-illegal.scala:12:7 ------------------------------------------------------- +12 |object UnrollObject // error + | ^ + | @unroll is only allowed on a method parameter +-- [E207] Declaration Error: tests/neg/unroll-illegal.scala:18:5 ------------------------------------------------------- +18 |enum UnrollEnum { case X } // error + | ^ + | @unroll is only allowed on a method parameter +-- [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 +-- [E207] Declaration Error: tests/neg/unroll-illegal.scala:22:19 ------------------------------------------------------ +22 | type annotType = Int @unroll // error + | ^^^^^^^^^^^ + | @unroll is only allowed on a method parameter +-- [E207] Declaration Error: tests/neg/unroll-illegal.scala:25:6 ------------------------------------------------------- +25 | val unrollVal: Int = 23 // error + | ^ + | @unroll is only allowed on a method parameter +-- [E207] Declaration Error: tests/neg/unroll-illegal.scala:28:6 ------------------------------------------------------- +28 | def unrollDef: Int = 23 // error + | ^ + | @unroll is only allowed on a method parameter +-- [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-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 +} 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..6201a7d815cd --- /dev/null +++ b/tests/neg/unroll-illegal3.check @@ -0,0 +1,12 @@ +-- [E207] Declaration Error: tests/neg/unroll-illegal3.scala:7:31 ------------------------------------------------------ +7 | def foo(s: String, @unroll y: Boolean) = s + y // error + | ^ + | 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 + | ^ + | 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 + | ^ + | Cannot unroll parameters of method foo: it must not be abstract 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 +} 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 +} diff --git a/tests/neg/unroll-no-default.check b/tests/neg/unroll-no-default.check new file mode 100644 index 000000000000..d16a94ce0527 --- /dev/null +++ b/tests/neg/unroll-no-default.check @@ -0,0 +1,4 @@ +-- 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 new file mode 100644 index 000000000000..1058f34087e3 --- /dev/null +++ b/tests/neg/unroll-no-default.scala @@ -0,0 +1,7 @@ +//> using options -experimental -Xprint:unrollDefs + +import scala.annotation.unroll + +class Foo { + final def foo(x: Int, @unroll y: Int) = x + y // error +} diff --git a/tests/neg/unroll-traitConstructor.check b/tests/neg/unroll-traitConstructor.check new file mode 100644 index 000000000000..9bb74d2559bf --- /dev/null +++ b/tests/neg/unroll-traitConstructor.check @@ -0,0 +1,4 @@ +-- [E207] Declaration Error: tests/neg/unroll-traitConstructor.scala:5:32 ---------------------------------------------- +5 |trait Unroll(a: String, @unroll b: Boolean = true): // error + | ^ + | implementation restriction: Cannot unroll parameters of 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) diff --git a/tests/pos-custom-args/captures/i21347.scala b/tests/pos-custom-args/captures/i21347.scala index e74c15bff8c1..a965b7e4f26b 100644 --- a/tests/pos-custom-args/captures/i21347.scala +++ b/tests/pos-custom-args/captures/i21347.scala @@ -1,4 +1,4 @@ -//> using scala 3.6.0-RC1-bin-SNAPSHOT +//!> using scala 3.6.0-RC1-bin-SNAPSHOT import language.experimental.captureChecking @@ -8,4 +8,4 @@ def run[Cap^](f: Box[Cap]^{Cap^} => Unit): Box[Cap]^{Cap^} = ??? def main() = val b = run(_ => ()) - // val b = run[caps.CapSet](_ => ()) // this compiles \ No newline at end of file + // val b = run[caps.CapSet](_ => ()) // this compiles diff --git a/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala b/tests/run-tasty-inspector/stdlibExperimentalDefinitions.scala index 65e3a730ee7e..2c4ee5401f92 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" ) diff --git a/tests/run/unroll-caseclass-integration.jvm.check b/tests/run/unroll-caseclass-integration.jvm.check new file mode 100644 index 000000000000..0418867c80c6 --- /dev/null +++ b/tests/run/unroll-caseclass-integration.jvm.check @@ -0,0 +1,34 @@ +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" +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" +Assertion passed: found "hello31337false12345" + +public unroll.Unrolled(java.lang.String,int) +public unroll.Unrolled(java.lang.String,int,boolean) +public unroll.Unrolled(java.lang.String,int,boolean,long) +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-integration.scala-js.check b/tests/run/unroll-caseclass-integration.scala-js.check new file mode 100644 index 000000000000..dbd2d93b08dd --- /dev/null +++ b/tests/run/unroll-caseclass-integration.scala-js.check @@ -0,0 +1,30 @@ +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" +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" +Assertion passed: found "hello31337false12345" +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-integration/TestUtils_1.scala b/tests/run/unroll-caseclass-integration/TestUtils_1.scala new file mode 100644 index 000000000000..e639a54d924b --- /dev/null +++ b/tests/run/unroll-caseclass-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-caseclass-integration/Test_4.scala b/tests/run/unroll-caseclass-integration/Test_4.scala new file mode 100644 index 000000000000..dc60b5bf2059 --- /dev/null +++ b/tests/run/unroll-caseclass-integration/Test_4.scala @@ -0,0 +1,7 @@ +//> using options -experimental +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/tests/run/unroll-caseclass-integration/UnrollTestMain_1.scala b/tests/run/unroll-caseclass-integration/UnrollTestMain_1.scala new file mode 100644 index 000000000000..45ce6a768f2a --- /dev/null +++ b/tests/run/unroll-caseclass-integration/UnrollTestMain_1.scala @@ -0,0 +1,25 @@ +//> using options -experimental +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/tests/run/unroll-caseclass-integration/UnrollTestMain_2.scala b/tests/run/unroll-caseclass-integration/UnrollTestMain_2.scala new file mode 100644 index 000000000000..f6cfbfc54a2c --- /dev/null +++ b/tests/run/unroll-caseclass-integration/UnrollTestMain_2.scala @@ -0,0 +1,30 @@ +//> using options -experimental +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/tests/run/unroll-caseclass-integration/UnrollTestMain_3.scala b/tests/run/unroll-caseclass-integration/UnrollTestMain_3.scala new file mode 100644 index 000000000000..b0e4d7a5d25a --- /dev/null +++ b/tests/run/unroll-caseclass-integration/UnrollTestMain_3.scala @@ -0,0 +1,36 @@ +//> using options -experimental +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/tests/run/unroll-caseclass-integration/UnrollTestPlatformSpecificJS_3.scala b/tests/run/unroll-caseclass-integration/UnrollTestPlatformSpecificJS_3.scala new file mode 100644 index 000000000000..190890788014 --- /dev/null +++ b/tests/run/unroll-caseclass-integration/UnrollTestPlatformSpecificJS_3.scala @@ -0,0 +1,9 @@ +//> using options -experimental +//> using target.platform scala-js +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + // do nothing for scala-js + } +} diff --git a/tests/run/unroll-caseclass-integration/UnrollTestPlatformSpecific_3.scala b/tests/run/unroll-caseclass-integration/UnrollTestPlatformSpecific_3.scala new file mode 100644 index 000000000000..0ec252f740f4 --- /dev/null +++ b/tests/run/unroll-caseclass-integration/UnrollTestPlatformSpecific_3.scala @@ -0,0 +1,35 @@ +//> using options -experimental +//> using target.platform jvm +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.map(_.toString).sorted.foreach(println) + } +} diff --git a/tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_1.scala b/tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_1.scala new file mode 100644 index 000000000000..28eb2815e979 --- /dev/null +++ b/tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_1.scala @@ -0,0 +1,21 @@ +//> using options -experimental +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestScalaSpecificV1{ + 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") + } +} diff --git a/tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_2.scala b/tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_2.scala new file mode 100644 index 000000000000..5d4079a093ce --- /dev/null +++ b/tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_2.scala @@ -0,0 +1,22 @@ +//> using options -experimental +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestScalaSpecificV2{ + 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") + } +} \ No newline at end of file diff --git a/tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_3.scala b/tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_3.scala new file mode 100644 index 000000000000..46f84998baa0 --- /dev/null +++ b/tests/run/unroll-caseclass-integration/UnrollTestScalaSpecific_3.scala @@ -0,0 +1,21 @@ +//> using options -experimental +package unroll +import unroll.TestUtils.logAssertStartsWith +object UnrollTestScalaSpecificV3{ + 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") + } +} \ No newline at end of file diff --git a/tests/run/unroll-caseclass-integration/Unrolled_1.scala b/tests/run/unroll-caseclass-integration/Unrolled_1.scala new file mode 100644 index 000000000000..e3f57bddd325 --- /dev/null +++ b/tests/run/unroll-caseclass-integration/Unrolled_1.scala @@ -0,0 +1,6 @@ +//> using options -experimental +package unroll + +case class Unrolled(s: String, n: Int = 1){ + def foo = s + n +} diff --git a/tests/run/unroll-caseclass-integration/Unrolled_2.scala b/tests/run/unroll-caseclass-integration/Unrolled_2.scala new file mode 100644 index 000000000000..cb2232a57726 --- /dev/null +++ b/tests/run/unroll-caseclass-integration/Unrolled_2.scala @@ -0,0 +1,8 @@ +//> using options -experimental +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/tests/run/unroll-caseclass-integration/Unrolled_3.scala b/tests/run/unroll-caseclass-integration/Unrolled_3.scala new file mode 100644 index 000000000000..66b4981660df --- /dev/null +++ b/tests/run/unroll-caseclass-integration/Unrolled_3.scala @@ -0,0 +1,8 @@ +//> using options -experimental +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/tests/run/unroll-classMethod-integration.jvm.check b/tests/run/unroll-classMethod-integration.jvm.check new file mode 100644 index 000000000000..db6f0ee5732d --- /dev/null +++ b/tests/run/unroll-classMethod-integration.jvm.check @@ -0,0 +1,14 @@ +Assertion passed: found "cow" + "1true0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" +public boolean unroll.Unrolled.foo$default$3() +public final java.lang.String unroll.Unrolled.foo(java.lang.String) +public final java.lang.String unroll.Unrolled.foo(java.lang.String,int,boolean) +public final java.lang.String unroll.Unrolled.foo(java.lang.String,int,boolean,long) +public int unroll.Unrolled.foo$default$2() +public long unroll.Unrolled.foo$default$4() +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" diff --git a/tests/run/unroll-classMethod-integration.scala-js.check b/tests/run/unroll-classMethod-integration.scala-js.check new file mode 100644 index 000000000000..001534a32889 --- /dev/null +++ b/tests/run/unroll-classMethod-integration.scala-js.check @@ -0,0 +1,8 @@ +Assertion passed: found "cow" + "1true0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" diff --git a/tests/run/unroll-classMethod-integration/TestUtils_1.scala b/tests/run/unroll-classMethod-integration/TestUtils_1.scala new file mode 100644 index 000000000000..e639a54d924b --- /dev/null +++ b/tests/run/unroll-classMethod-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-classMethod-integration/Test_4.scala b/tests/run/unroll-classMethod-integration/Test_4.scala new file mode 100644 index 000000000000..dc60b5bf2059 --- /dev/null +++ b/tests/run/unroll-classMethod-integration/Test_4.scala @@ -0,0 +1,7 @@ +//> using options -experimental +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/tests/run/unroll-classMethod-integration/UnrollTestMain_1.scala b/tests/run/unroll-classMethod-integration/UnrollTestMain_1.scala new file mode 100644 index 000000000000..62d61c81d21d --- /dev/null +++ b/tests/run/unroll-classMethod-integration/UnrollTestMain_1.scala @@ -0,0 +1,10 @@ +//> using options -experimental +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV1{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled().foo("cow"), "cow") + } +} diff --git a/tests/run/unroll-classMethod-integration/UnrollTestMain_2.scala b/tests/run/unroll-classMethod-integration/UnrollTestMain_2.scala new file mode 100644 index 000000000000..921ee48ff0db --- /dev/null +++ b/tests/run/unroll-classMethod-integration/UnrollTestMain_2.scala @@ -0,0 +1,12 @@ +//> using options -experimental +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/tests/run/unroll-classMethod-integration/UnrollTestMain_3.scala b/tests/run/unroll-classMethod-integration/UnrollTestMain_3.scala new file mode 100644 index 000000000000..bc985b58c359 --- /dev/null +++ b/tests/run/unroll-classMethod-integration/UnrollTestMain_3.scala @@ -0,0 +1,15 @@ +//> using options -experimental +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/tests/run/unroll-classMethod-integration/UnrollTestPlatformSpecificJS_3.scala b/tests/run/unroll-classMethod-integration/UnrollTestPlatformSpecificJS_3.scala new file mode 100644 index 000000000000..190890788014 --- /dev/null +++ b/tests/run/unroll-classMethod-integration/UnrollTestPlatformSpecificJS_3.scala @@ -0,0 +1,9 @@ +//> using options -experimental +//> using target.platform scala-js +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + // do nothing for scala-js + } +} diff --git a/tests/run/unroll-classMethod-integration/UnrollTestPlatformSpecific_3.scala b/tests/run/unroll-classMethod-integration/UnrollTestPlatformSpecific_3.scala new file mode 100644 index 000000000000..decffbaf3c74 --- /dev/null +++ b/tests/run/unroll-classMethod-integration/UnrollTestPlatformSpecific_3.scala @@ -0,0 +1,32 @@ +//> using options -experimental +//> using target.platform jvm +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")).map(_.toString).sorted.foreach(println) + } +} diff --git a/tests/run/unroll-classMethod-integration/Unrolled_1.scala b/tests/run/unroll-classMethod-integration/Unrolled_1.scala new file mode 100644 index 000000000000..aa6ca65fe6a3 --- /dev/null +++ b/tests/run/unroll-classMethod-integration/Unrolled_1.scala @@ -0,0 +1,6 @@ +//> using options -experimental +package unroll + +class Unrolled{ + def foo(s: String) = s +} diff --git a/tests/run/unroll-classMethod-integration/Unrolled_2.scala b/tests/run/unroll-classMethod-integration/Unrolled_2.scala new file mode 100644 index 000000000000..2091bb4c5a9e --- /dev/null +++ b/tests/run/unroll-classMethod-integration/Unrolled_2.scala @@ -0,0 +1,8 @@ +//> using options -experimental +package unroll + +import scala.annotation.unroll + +class Unrolled{ + final def foo(s: String, @unroll n: Int = 1, b: Boolean = true) = s + n + b +} diff --git a/tests/run/unroll-classMethod-integration/Unrolled_3.scala b/tests/run/unroll-classMethod-integration/Unrolled_3.scala new file mode 100644 index 000000000000..8991bda3aeb7 --- /dev/null +++ b/tests/run/unroll-classMethod-integration/Unrolled_3.scala @@ -0,0 +1,8 @@ +//> using options -experimental +package unroll + +import scala.annotation.unroll + +class Unrolled{ + final def foo(s: String, @unroll n: Int = 1, b: Boolean = true, @unroll l: Long = 0) = s + n + b + l +} 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-curriedMethod-integration.jvm.check b/tests/run/unroll-curriedMethod-integration.jvm.check new file mode 100644 index 000000000000..bd696cc21ed5 --- /dev/null +++ b/tests/run/unroll-curriedMethod-integration.jvm.check @@ -0,0 +1,14 @@ +Assertion passed: found "cow" + "1true0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" +public boolean unroll.Unrolled.foo$default$3() +public final java.lang.String unroll.Unrolled.foo(java.lang.String,int,boolean,long,scala.Function1) +public final java.lang.String unroll.Unrolled.foo(java.lang.String,int,boolean,scala.Function1) +public final java.lang.String unroll.Unrolled.foo(java.lang.String,scala.Function1) +public int unroll.Unrolled.foo$default$2() +public long unroll.Unrolled.foo$default$4() +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" diff --git a/tests/run/unroll-curriedMethod-integration.scala-js.check b/tests/run/unroll-curriedMethod-integration.scala-js.check new file mode 100644 index 000000000000..001534a32889 --- /dev/null +++ b/tests/run/unroll-curriedMethod-integration.scala-js.check @@ -0,0 +1,8 @@ +Assertion passed: found "cow" + "1true0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" diff --git a/tests/run/unroll-curriedMethod-integration/TestUtils_1.scala b/tests/run/unroll-curriedMethod-integration/TestUtils_1.scala new file mode 100644 index 000000000000..e639a54d924b --- /dev/null +++ b/tests/run/unroll-curriedMethod-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-curriedMethod-integration/Test_4.scala b/tests/run/unroll-curriedMethod-integration/Test_4.scala new file mode 100644 index 000000000000..dc60b5bf2059 --- /dev/null +++ b/tests/run/unroll-curriedMethod-integration/Test_4.scala @@ -0,0 +1,7 @@ +//> using options -experimental +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/tests/run/unroll-curriedMethod-integration/UnrollTestMain_1.scala b/tests/run/unroll-curriedMethod-integration/UnrollTestMain_1.scala new file mode 100644 index 000000000000..411aae125e20 --- /dev/null +++ b/tests/run/unroll-curriedMethod-integration/UnrollTestMain_1.scala @@ -0,0 +1,10 @@ +//> using options -experimental +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV1{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled().foo("cow")(identity), "cow") + } +} diff --git a/tests/run/unroll-curriedMethod-integration/UnrollTestMain_2.scala b/tests/run/unroll-curriedMethod-integration/UnrollTestMain_2.scala new file mode 100644 index 000000000000..9be52201ed8b --- /dev/null +++ b/tests/run/unroll-curriedMethod-integration/UnrollTestMain_2.scala @@ -0,0 +1,12 @@ +//> using options -experimental +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/tests/run/unroll-curriedMethod-integration/UnrollTestMain_3.scala b/tests/run/unroll-curriedMethod-integration/UnrollTestMain_3.scala new file mode 100644 index 000000000000..1f281db4f497 --- /dev/null +++ b/tests/run/unroll-curriedMethod-integration/UnrollTestMain_3.scala @@ -0,0 +1,15 @@ +//> using options -experimental +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/tests/run/unroll-curriedMethod-integration/UnrollTestPlatformSpecificJS_3.scala b/tests/run/unroll-curriedMethod-integration/UnrollTestPlatformSpecificJS_3.scala new file mode 100644 index 000000000000..190890788014 --- /dev/null +++ b/tests/run/unroll-curriedMethod-integration/UnrollTestPlatformSpecificJS_3.scala @@ -0,0 +1,9 @@ +//> using options -experimental +//> using target.platform scala-js +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + // do nothing for scala-js + } +} diff --git a/tests/run/unroll-curriedMethod-integration/UnrollTestPlatformSpecific_3.scala b/tests/run/unroll-curriedMethod-integration/UnrollTestPlatformSpecific_3.scala new file mode 100644 index 000000000000..24ca27b644a1 --- /dev/null +++ b/tests/run/unroll-curriedMethod-integration/UnrollTestPlatformSpecific_3.scala @@ -0,0 +1,31 @@ +//> using options -experimental +//> using target.platform jvm +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")).map(_.toString).sorted.foreach(println) + } +} diff --git a/tests/run/unroll-curriedMethod-integration/Unrolled_1.scala b/tests/run/unroll-curriedMethod-integration/Unrolled_1.scala new file mode 100644 index 000000000000..d6b8b06da582 --- /dev/null +++ b/tests/run/unroll-curriedMethod-integration/Unrolled_1.scala @@ -0,0 +1,6 @@ +//> using options -experimental +package unroll + +class Unrolled{ + def foo(s: String)(f: String => String) = f(s) +} diff --git a/tests/run/unroll-curriedMethod-integration/Unrolled_2.scala b/tests/run/unroll-curriedMethod-integration/Unrolled_2.scala new file mode 100644 index 000000000000..5a526bf6eeb2 --- /dev/null +++ b/tests/run/unroll-curriedMethod-integration/Unrolled_2.scala @@ -0,0 +1,8 @@ +//> using options -experimental +package unroll + +import scala.annotation.unroll + +class Unrolled{ + final def foo(s: String, @unroll n: Int = 1, b: Boolean = true)(f: String => String) = f(s + n + b) +} diff --git a/tests/run/unroll-curriedMethod-integration/Unrolled_3.scala b/tests/run/unroll-curriedMethod-integration/Unrolled_3.scala new file mode 100644 index 000000000000..008576a9a5c3 --- /dev/null +++ b/tests/run/unroll-curriedMethod-integration/Unrolled_3.scala @@ -0,0 +1,8 @@ +//> using options -experimental +package unroll + +import scala.annotation.unroll + +class Unrolled{ + 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/tests/run/unroll-genericMethod-integration.jvm.check b/tests/run/unroll-genericMethod-integration.jvm.check new file mode 100644 index 000000000000..e1880e773f8e --- /dev/null +++ b/tests/run/unroll-genericMethod-integration.jvm.check @@ -0,0 +1,14 @@ +Assertion passed: found "cow" + "1true0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" +public boolean unroll.Unrolled.foo$default$3() +public final java.lang.String unroll.Unrolled.foo(java.lang.Object) +public final java.lang.String unroll.Unrolled.foo(java.lang.Object,int,boolean) +public final java.lang.String unroll.Unrolled.foo(java.lang.Object,int,boolean,long) +public int unroll.Unrolled.foo$default$2() +public long unroll.Unrolled.foo$default$4() +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" diff --git a/tests/run/unroll-genericMethod-integration.scala-js.check b/tests/run/unroll-genericMethod-integration.scala-js.check new file mode 100644 index 000000000000..001534a32889 --- /dev/null +++ b/tests/run/unroll-genericMethod-integration.scala-js.check @@ -0,0 +1,8 @@ +Assertion passed: found "cow" + "1true0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" diff --git a/tests/run/unroll-genericMethod-integration/TestUtils_1.scala b/tests/run/unroll-genericMethod-integration/TestUtils_1.scala new file mode 100644 index 000000000000..e639a54d924b --- /dev/null +++ b/tests/run/unroll-genericMethod-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-genericMethod-integration/Test_4.scala b/tests/run/unroll-genericMethod-integration/Test_4.scala new file mode 100644 index 000000000000..dc60b5bf2059 --- /dev/null +++ b/tests/run/unroll-genericMethod-integration/Test_4.scala @@ -0,0 +1,7 @@ +//> using options -experimental +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/tests/run/unroll-genericMethod-integration/UnrollTestMain_1.scala b/tests/run/unroll-genericMethod-integration/UnrollTestMain_1.scala new file mode 100644 index 000000000000..62d61c81d21d --- /dev/null +++ b/tests/run/unroll-genericMethod-integration/UnrollTestMain_1.scala @@ -0,0 +1,10 @@ +//> using options -experimental +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV1{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled().foo("cow"), "cow") + } +} diff --git a/tests/run/unroll-genericMethod-integration/UnrollTestMain_2.scala b/tests/run/unroll-genericMethod-integration/UnrollTestMain_2.scala new file mode 100644 index 000000000000..921ee48ff0db --- /dev/null +++ b/tests/run/unroll-genericMethod-integration/UnrollTestMain_2.scala @@ -0,0 +1,12 @@ +//> using options -experimental +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/tests/run/unroll-genericMethod-integration/UnrollTestMain_3.scala b/tests/run/unroll-genericMethod-integration/UnrollTestMain_3.scala new file mode 100644 index 000000000000..bc985b58c359 --- /dev/null +++ b/tests/run/unroll-genericMethod-integration/UnrollTestMain_3.scala @@ -0,0 +1,15 @@ +//> using options -experimental +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/tests/run/unroll-genericMethod-integration/UnrollTestPlatformSpecificJS_3.scala b/tests/run/unroll-genericMethod-integration/UnrollTestPlatformSpecificJS_3.scala new file mode 100644 index 000000000000..190890788014 --- /dev/null +++ b/tests/run/unroll-genericMethod-integration/UnrollTestPlatformSpecificJS_3.scala @@ -0,0 +1,9 @@ +//> using options -experimental +//> using target.platform scala-js +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + // do nothing for scala-js + } +} diff --git a/tests/run/unroll-genericMethod-integration/UnrollTestPlatformSpecific_3.scala b/tests/run/unroll-genericMethod-integration/UnrollTestPlatformSpecific_3.scala new file mode 100644 index 000000000000..2ef19e0f378e --- /dev/null +++ b/tests/run/unroll-genericMethod-integration/UnrollTestPlatformSpecific_3.scala @@ -0,0 +1,29 @@ +//> using options -experimental +//> using target.platform jvm +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")).map(_.toString).sorted.foreach(println) + + } +} diff --git a/tests/run/unroll-genericMethod-integration/Unrolled_1.scala b/tests/run/unroll-genericMethod-integration/Unrolled_1.scala new file mode 100644 index 000000000000..a1d69945bc7e --- /dev/null +++ b/tests/run/unroll-genericMethod-integration/Unrolled_1.scala @@ -0,0 +1,6 @@ +//> using options -experimental +package unroll + +class Unrolled{ + def foo[T](s: T) = s.toString +} diff --git a/tests/run/unroll-genericMethod-integration/Unrolled_2.scala b/tests/run/unroll-genericMethod-integration/Unrolled_2.scala new file mode 100644 index 000000000000..e5970388fff8 --- /dev/null +++ b/tests/run/unroll-genericMethod-integration/Unrolled_2.scala @@ -0,0 +1,8 @@ +//> using options -experimental +package unroll + +import scala.annotation.unroll + +class Unrolled{ + final def foo[T](s: T, @unroll n: Int = 1, b: Boolean = true) = s.toString + n + b +} diff --git a/tests/run/unroll-genericMethod-integration/Unrolled_3.scala b/tests/run/unroll-genericMethod-integration/Unrolled_3.scala new file mode 100644 index 000000000000..2ababa300ed1 --- /dev/null +++ b/tests/run/unroll-genericMethod-integration/Unrolled_3.scala @@ -0,0 +1,8 @@ +//> using options -experimental +package unroll + +import scala.annotation.unroll + +class Unrolled{ + 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/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)") diff --git a/tests/run/unroll-methodWithImplicit-integration.jvm.check b/tests/run/unroll-methodWithImplicit-integration.jvm.check new file mode 100644 index 000000000000..bd696cc21ed5 --- /dev/null +++ b/tests/run/unroll-methodWithImplicit-integration.jvm.check @@ -0,0 +1,14 @@ +Assertion passed: found "cow" + "1true0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" +public boolean unroll.Unrolled.foo$default$3() +public final java.lang.String unroll.Unrolled.foo(java.lang.String,int,boolean,long,scala.Function1) +public final java.lang.String unroll.Unrolled.foo(java.lang.String,int,boolean,scala.Function1) +public final java.lang.String unroll.Unrolled.foo(java.lang.String,scala.Function1) +public int unroll.Unrolled.foo$default$2() +public long unroll.Unrolled.foo$default$4() +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" diff --git a/tests/run/unroll-methodWithImplicit-integration.scala-js.check b/tests/run/unroll-methodWithImplicit-integration.scala-js.check new file mode 100644 index 000000000000..001534a32889 --- /dev/null +++ b/tests/run/unroll-methodWithImplicit-integration.scala-js.check @@ -0,0 +1,8 @@ +Assertion passed: found "cow" + "1true0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" 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..dc60b5bf2059 --- /dev/null +++ b/tests/run/unroll-methodWithImplicit-integration/Test_4.scala @@ -0,0 +1,7 @@ +//> using options -experimental +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_1.scala b/tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_1.scala new file mode 100644 index 000000000000..e4e91e6709f3 --- /dev/null +++ b/tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_1.scala @@ -0,0 +1,11 @@ +//> using options -experimental +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/tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_2.scala b/tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_2.scala new file mode 100644 index 000000000000..69a5a0b39ab6 --- /dev/null +++ b/tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_2.scala @@ -0,0 +1,13 @@ +//> using options -experimental +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/tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_3.scala b/tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_3.scala new file mode 100644 index 000000000000..ffd528e15f5e --- /dev/null +++ b/tests/run/unroll-methodWithImplicit-integration/UnrollTestMain_3.scala @@ -0,0 +1,16 @@ +//> using options -experimental +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/tests/run/unroll-methodWithImplicit-integration/UnrollTestPlatformSpecificJS_3.scala b/tests/run/unroll-methodWithImplicit-integration/UnrollTestPlatformSpecificJS_3.scala new file mode 100644 index 000000000000..190890788014 --- /dev/null +++ b/tests/run/unroll-methodWithImplicit-integration/UnrollTestPlatformSpecificJS_3.scala @@ -0,0 +1,9 @@ +//> using options -experimental +//> using target.platform scala-js +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + // do nothing for scala-js + } +} diff --git a/tests/run/unroll-methodWithImplicit-integration/UnrollTestPlatformSpecific_3.scala b/tests/run/unroll-methodWithImplicit-integration/UnrollTestPlatformSpecific_3.scala new file mode 100644 index 000000000000..86cd5514cfd5 --- /dev/null +++ b/tests/run/unroll-methodWithImplicit-integration/UnrollTestPlatformSpecific_3.scala @@ -0,0 +1,31 @@ +//> using options -experimental +//> using target.platform jvm +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")).map(_.toString).sorted.foreach(println) + + } +} diff --git a/tests/run/unroll-methodWithImplicit-integration/Unrolled_1.scala b/tests/run/unroll-methodWithImplicit-integration/Unrolled_1.scala new file mode 100644 index 000000000000..1de415b26952 --- /dev/null +++ b/tests/run/unroll-methodWithImplicit-integration/Unrolled_1.scala @@ -0,0 +1,6 @@ +//> using options -experimental +package unroll + +class Unrolled{ + def foo(s: String)(implicit f: String => String) = f(s) +} diff --git a/tests/run/unroll-methodWithImplicit-integration/Unrolled_2.scala b/tests/run/unroll-methodWithImplicit-integration/Unrolled_2.scala new file mode 100644 index 000000000000..01a5d2fb037a --- /dev/null +++ b/tests/run/unroll-methodWithImplicit-integration/Unrolled_2.scala @@ -0,0 +1,8 @@ +//> using options -experimental +package unroll + +import scala.annotation.unroll + +class Unrolled{ + final def foo(s: String, @unroll n: Int = 1, b: Boolean = true)(implicit f: String => String) = f(s + n + b) +} diff --git a/tests/run/unroll-methodWithImplicit-integration/Unrolled_3.scala b/tests/run/unroll-methodWithImplicit-integration/Unrolled_3.scala new file mode 100644 index 000000000000..07627f604d76 --- /dev/null +++ b/tests/run/unroll-methodWithImplicit-integration/Unrolled_3.scala @@ -0,0 +1,8 @@ +//> using options -experimental +package unroll + +import scala.annotation.unroll + +class Unrolled{ + 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/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@") diff --git a/tests/run/unroll-objectMethod-integration.jvm.check b/tests/run/unroll-objectMethod-integration.jvm.check new file mode 100644 index 000000000000..b29f42ca6d8e --- /dev/null +++ b/tests/run/unroll-objectMethod-integration.jvm.check @@ -0,0 +1,21 @@ +Assertion passed: found "cow1" + "true0" +Assertion passed: found "cow2" + "true0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" +public boolean unroll.Unrolled$.foo$default$3() +public final java.lang.String unroll.Unrolled$.foo(java.lang.String,int) +public final java.lang.String unroll.Unrolled$.foo(java.lang.String,int,boolean) +public final java.lang.String unroll.Unrolled$.foo(java.lang.String,int,boolean,long) +public int unroll.Unrolled$.foo$default$2() +public long unroll.Unrolled$.foo$default$4() +public static boolean unroll.Unrolled.foo$default$3() +public static int unroll.Unrolled.foo$default$2() +public static java.lang.String unroll.Unrolled.foo(java.lang.String,int) +public static java.lang.String unroll.Unrolled.foo(java.lang.String,int,boolean) +public static java.lang.String unroll.Unrolled.foo(java.lang.String,int,boolean,long) +public static long unroll.Unrolled.foo$default$4() +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" diff --git a/tests/run/unroll-objectMethod-integration.scala-js.check b/tests/run/unroll-objectMethod-integration.scala-js.check new file mode 100644 index 000000000000..248ef79ed88a --- /dev/null +++ b/tests/run/unroll-objectMethod-integration.scala-js.check @@ -0,0 +1,9 @@ +Assertion passed: found "cow1" + "true0" +Assertion passed: found "cow2" + "true0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" 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..dc60b5bf2059 --- /dev/null +++ b/tests/run/unroll-objectMethod-integration/Test_4.scala @@ -0,0 +1,7 @@ +//> using options -experimental +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/tests/run/unroll-objectMethod-integration/UnrollTestMain_1.scala b/tests/run/unroll-objectMethod-integration/UnrollTestMain_1.scala new file mode 100644 index 000000000000..9cba53c444ce --- /dev/null +++ b/tests/run/unroll-objectMethod-integration/UnrollTestMain_1.scala @@ -0,0 +1,11 @@ +//> using options -experimental +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/tests/run/unroll-objectMethod-integration/UnrollTestMain_2.scala b/tests/run/unroll-objectMethod-integration/UnrollTestMain_2.scala new file mode 100644 index 000000000000..2fe609543d51 --- /dev/null +++ b/tests/run/unroll-objectMethod-integration/UnrollTestMain_2.scala @@ -0,0 +1,12 @@ +//> using options -experimental +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/tests/run/unroll-objectMethod-integration/UnrollTestMain_3.scala b/tests/run/unroll-objectMethod-integration/UnrollTestMain_3.scala new file mode 100644 index 000000000000..e6d442d3bf7c --- /dev/null +++ b/tests/run/unroll-objectMethod-integration/UnrollTestMain_3.scala @@ -0,0 +1,15 @@ +//> using options -experimental +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/tests/run/unroll-objectMethod-integration/UnrollTestPlatformSpecificJS_3.scala b/tests/run/unroll-objectMethod-integration/UnrollTestPlatformSpecificJS_3.scala new file mode 100644 index 000000000000..190890788014 --- /dev/null +++ b/tests/run/unroll-objectMethod-integration/UnrollTestPlatformSpecificJS_3.scala @@ -0,0 +1,9 @@ +//> using options -experimental +//> using target.platform scala-js +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + // do nothing for scala-js + } +} diff --git a/tests/run/unroll-objectMethod-integration/UnrollTestPlatformSpecific_3.scala b/tests/run/unroll-objectMethod-integration/UnrollTestPlatformSpecific_3.scala new file mode 100644 index 000000000000..61450765623a --- /dev/null +++ b/tests/run/unroll-objectMethod-integration/UnrollTestPlatformSpecific_3.scala @@ -0,0 +1,50 @@ +//> using options -experimental +//> using target.platform jvm +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + val instance = Unrolled + val instanceCls = Class.forName("unroll.Unrolled$") + + instanceCls.getMethods.filter(_.getName.contains("foo")).map(_.toString).sorted.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")).map(_.toString).sorted.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" + ) + + } +} diff --git a/tests/run/unroll-objectMethod-integration/Unrolled_1.scala b/tests/run/unroll-objectMethod-integration/Unrolled_1.scala new file mode 100644 index 000000000000..042ab3180cdc --- /dev/null +++ b/tests/run/unroll-objectMethod-integration/Unrolled_1.scala @@ -0,0 +1,7 @@ +//> using options -experimental +package unroll + +object Unrolled{ + + def foo(s: String, n: Int = 1) = s + n +} diff --git a/tests/run/unroll-objectMethod-integration/Unrolled_2.scala b/tests/run/unroll-objectMethod-integration/Unrolled_2.scala new file mode 100644 index 000000000000..bfef86beb6b2 --- /dev/null +++ b/tests/run/unroll-objectMethod-integration/Unrolled_2.scala @@ -0,0 +1,8 @@ +//> using options -experimental +package unroll + +import scala.annotation.unroll + +object Unrolled{ + final def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b +} diff --git a/tests/run/unroll-objectMethod-integration/Unrolled_3.scala b/tests/run/unroll-objectMethod-integration/Unrolled_3.scala new file mode 100644 index 000000000000..c76521e731d8 --- /dev/null +++ b/tests/run/unroll-objectMethod-integration/Unrolled_3.scala @@ -0,0 +1,8 @@ +//> using options -experimental +package unroll + +import scala.annotation.unroll + +object Unrolled{ + final def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0) = s + n + b + l +} diff --git a/tests/run/unroll-primaryConstructor-integration.jvm.check b/tests/run/unroll-primaryConstructor-integration.jvm.check new file mode 100644 index 000000000000..f5f6e28d5635 --- /dev/null +++ b/tests/run/unroll-primaryConstructor-integration.jvm.check @@ -0,0 +1,13 @@ +Assertion passed: found "cow1" + "true0" +Assertion passed: found "cow2" + "true0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" + +public unroll.Unrolled(java.lang.String,int) +public unroll.Unrolled(java.lang.String,int,boolean) +public unroll.Unrolled(java.lang.String,int,boolean,long) +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" diff --git a/tests/run/unroll-primaryConstructor-integration.scala-js.check b/tests/run/unroll-primaryConstructor-integration.scala-js.check new file mode 100644 index 000000000000..248ef79ed88a --- /dev/null +++ b/tests/run/unroll-primaryConstructor-integration.scala-js.check @@ -0,0 +1,9 @@ +Assertion passed: found "cow1" + "true0" +Assertion passed: found "cow2" + "true0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" 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..dc60b5bf2059 --- /dev/null +++ b/tests/run/unroll-primaryConstructor-integration/Test_4.scala @@ -0,0 +1,7 @@ +//> using options -experimental +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/tests/run/unroll-primaryConstructor-integration/UnrollTestMain_1.scala b/tests/run/unroll-primaryConstructor-integration/UnrollTestMain_1.scala new file mode 100644 index 000000000000..ac80b2a5734b --- /dev/null +++ b/tests/run/unroll-primaryConstructor-integration/UnrollTestMain_1.scala @@ -0,0 +1,11 @@ +//> using options -experimental +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/tests/run/unroll-primaryConstructor-integration/UnrollTestMain_2.scala b/tests/run/unroll-primaryConstructor-integration/UnrollTestMain_2.scala new file mode 100644 index 000000000000..5140d999ee9b --- /dev/null +++ b/tests/run/unroll-primaryConstructor-integration/UnrollTestMain_2.scala @@ -0,0 +1,12 @@ +//> using options -experimental +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/tests/run/unroll-primaryConstructor-integration/UnrollTestMain_3.scala b/tests/run/unroll-primaryConstructor-integration/UnrollTestMain_3.scala new file mode 100644 index 000000000000..c80c33672d3e --- /dev/null +++ b/tests/run/unroll-primaryConstructor-integration/UnrollTestMain_3.scala @@ -0,0 +1,15 @@ +//> using options -experimental +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/tests/run/unroll-primaryConstructor-integration/UnrollTestPlatformSpecificJS_3.scala b/tests/run/unroll-primaryConstructor-integration/UnrollTestPlatformSpecificJS_3.scala new file mode 100644 index 000000000000..190890788014 --- /dev/null +++ b/tests/run/unroll-primaryConstructor-integration/UnrollTestPlatformSpecificJS_3.scala @@ -0,0 +1,9 @@ +//> using options -experimental +//> using target.platform scala-js +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + // do nothing for scala-js + } +} diff --git a/tests/run/unroll-primaryConstructor-integration/UnrollTestPlatformSpecific_3.scala b/tests/run/unroll-primaryConstructor-integration/UnrollTestPlatformSpecific_3.scala new file mode 100644 index 000000000000..0ec252f740f4 --- /dev/null +++ b/tests/run/unroll-primaryConstructor-integration/UnrollTestPlatformSpecific_3.scala @@ -0,0 +1,35 @@ +//> using options -experimental +//> using target.platform jvm +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.map(_.toString).sorted.foreach(println) + } +} diff --git a/tests/run/unroll-primaryConstructor-integration/Unrolled_1.scala b/tests/run/unroll-primaryConstructor-integration/Unrolled_1.scala new file mode 100644 index 000000000000..0ddd25a70127 --- /dev/null +++ b/tests/run/unroll-primaryConstructor-integration/Unrolled_1.scala @@ -0,0 +1,6 @@ +//> using options -experimental +package unroll + +class Unrolled(s: String, n: Int = 1){ + def foo = s + n +} diff --git a/tests/run/unroll-primaryConstructor-integration/Unrolled_2.scala b/tests/run/unroll-primaryConstructor-integration/Unrolled_2.scala new file mode 100644 index 000000000000..c8558df1af55 --- /dev/null +++ b/tests/run/unroll-primaryConstructor-integration/Unrolled_2.scala @@ -0,0 +1,8 @@ +//> using options -experimental +package unroll + +import scala.annotation.unroll + +class Unrolled(s: String, n: Int = 1, @unroll b: Boolean = true){ + final def foo = s + n + b +} diff --git a/tests/run/unroll-primaryConstructor-integration/Unrolled_3.scala b/tests/run/unroll-primaryConstructor-integration/Unrolled_3.scala new file mode 100644 index 000000000000..c6be439e1dec --- /dev/null +++ b/tests/run/unroll-primaryConstructor-integration/Unrolled_3.scala @@ -0,0 +1,8 @@ +//> using options -experimental +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/tests/run/unroll-secondParameterList-integration.jvm.check b/tests/run/unroll-secondParameterList-integration.jvm.check new file mode 100644 index 000000000000..d9ba05c5fc84 --- /dev/null +++ b/tests/run/unroll-secondParameterList-integration.jvm.check @@ -0,0 +1,14 @@ +Assertion passed: found "cow" + "1true0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" +public boolean unroll.Unrolled.foo$default$4(scala.Function1) +public final java.lang.String unroll.Unrolled.foo(scala.Function1,java.lang.String) +public final java.lang.String unroll.Unrolled.foo(scala.Function1,java.lang.String,int,boolean) +public final java.lang.String unroll.Unrolled.foo(scala.Function1,java.lang.String,int,boolean,long) +public int unroll.Unrolled.foo$default$3(scala.Function1) +public long unroll.Unrolled.foo$default$5(scala.Function1) +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" diff --git a/tests/run/unroll-secondParameterList-integration.scala-js.check b/tests/run/unroll-secondParameterList-integration.scala-js.check new file mode 100644 index 000000000000..001534a32889 --- /dev/null +++ b/tests/run/unroll-secondParameterList-integration.scala-js.check @@ -0,0 +1,8 @@ +Assertion passed: found "cow" + "1true0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" 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..dc60b5bf2059 --- /dev/null +++ b/tests/run/unroll-secondParameterList-integration/Test_4.scala @@ -0,0 +1,7 @@ +//> using options -experimental +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/tests/run/unroll-secondParameterList-integration/UnrollTestMain_1.scala b/tests/run/unroll-secondParameterList-integration/UnrollTestMain_1.scala new file mode 100644 index 000000000000..163225fb93bf --- /dev/null +++ b/tests/run/unroll-secondParameterList-integration/UnrollTestMain_1.scala @@ -0,0 +1,10 @@ +//> using options -experimental +package unroll + +import unroll.TestUtils.logAssertStartsWith + +object UnrollTestMainV1{ + def main(args: Array[String]): Unit = { + logAssertStartsWith(new Unrolled().foo(identity)("cow"), "cow") + } +} diff --git a/tests/run/unroll-secondParameterList-integration/UnrollTestMain_2.scala b/tests/run/unroll-secondParameterList-integration/UnrollTestMain_2.scala new file mode 100644 index 000000000000..de0776c569db --- /dev/null +++ b/tests/run/unroll-secondParameterList-integration/UnrollTestMain_2.scala @@ -0,0 +1,12 @@ +//> using options -experimental +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/tests/run/unroll-secondParameterList-integration/UnrollTestMain_3.scala b/tests/run/unroll-secondParameterList-integration/UnrollTestMain_3.scala new file mode 100644 index 000000000000..b18a7f18b4d0 --- /dev/null +++ b/tests/run/unroll-secondParameterList-integration/UnrollTestMain_3.scala @@ -0,0 +1,15 @@ +//> using options -experimental +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/tests/run/unroll-secondParameterList-integration/UnrollTestPlatformSpecificJS_3.scala b/tests/run/unroll-secondParameterList-integration/UnrollTestPlatformSpecificJS_3.scala new file mode 100644 index 000000000000..190890788014 --- /dev/null +++ b/tests/run/unroll-secondParameterList-integration/UnrollTestPlatformSpecificJS_3.scala @@ -0,0 +1,9 @@ +//> using options -experimental +//> using target.platform scala-js +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + // do nothing for scala-js + } +} diff --git a/tests/run/unroll-secondParameterList-integration/UnrollTestPlatformSpecific_3.scala b/tests/run/unroll-secondParameterList-integration/UnrollTestPlatformSpecific_3.scala new file mode 100644 index 000000000000..a3cb5b7a8f0c --- /dev/null +++ b/tests/run/unroll-secondParameterList-integration/UnrollTestPlatformSpecific_3.scala @@ -0,0 +1,32 @@ +//> using options -experimental +//> using target.platform jvm +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")).map(_.toString).sorted.foreach(println) + } +} diff --git a/tests/run/unroll-secondParameterList-integration/Unrolled_1.scala b/tests/run/unroll-secondParameterList-integration/Unrolled_1.scala new file mode 100644 index 000000000000..fbe0a58dca24 --- /dev/null +++ b/tests/run/unroll-secondParameterList-integration/Unrolled_1.scala @@ -0,0 +1,6 @@ +//> using options -experimental +package unroll + +class Unrolled{ + def foo(f: String => String)(s: String) = f(s) +} diff --git a/tests/run/unroll-secondParameterList-integration/Unrolled_2.scala b/tests/run/unroll-secondParameterList-integration/Unrolled_2.scala new file mode 100644 index 000000000000..68c4170f6f6e --- /dev/null +++ b/tests/run/unroll-secondParameterList-integration/Unrolled_2.scala @@ -0,0 +1,8 @@ +//> using options -experimental +package unroll + +import scala.annotation.unroll + +class Unrolled{ + final def foo(f: String => String)(s: String, @unroll n: Int = 1, b: Boolean = true) = f(s + n + b) +} diff --git a/tests/run/unroll-secondParameterList-integration/Unrolled_3.scala b/tests/run/unroll-secondParameterList-integration/Unrolled_3.scala new file mode 100644 index 000000000000..ddbe8c4cfaf4 --- /dev/null +++ b/tests/run/unroll-secondParameterList-integration/Unrolled_3.scala @@ -0,0 +1,8 @@ +//> using options -experimental +package unroll + +import scala.annotation.unroll + +class Unrolled{ + 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/tests/run/unroll-secondaryConstructor-integration.jvm.check b/tests/run/unroll-secondaryConstructor-integration.jvm.check new file mode 100644 index 000000000000..016acefc31a1 --- /dev/null +++ b/tests/run/unroll-secondaryConstructor-integration.jvm.check @@ -0,0 +1,14 @@ +Assertion passed: found "cow1" + "true0" +Assertion passed: found "cow2" + "true0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" + +public unroll.Unrolled() +public unroll.Unrolled(java.lang.String,int) +public unroll.Unrolled(java.lang.String,int,boolean) +public unroll.Unrolled(java.lang.String,int,boolean,long) +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" diff --git a/tests/run/unroll-secondaryConstructor-integration.scala-js.check b/tests/run/unroll-secondaryConstructor-integration.scala-js.check new file mode 100644 index 000000000000..248ef79ed88a --- /dev/null +++ b/tests/run/unroll-secondaryConstructor-integration.scala-js.check @@ -0,0 +1,9 @@ +Assertion passed: found "cow1" + "true0" +Assertion passed: found "cow2" + "true0" +Assertion passed: found "cow1true" + "0" +Assertion passed: found "cow2true" + "0" +Assertion passed: found "cow2false" + "0" +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" 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..dc60b5bf2059 --- /dev/null +++ b/tests/run/unroll-secondaryConstructor-integration/Test_4.scala @@ -0,0 +1,7 @@ +//> using options -experimental +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_1.scala b/tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_1.scala new file mode 100644 index 000000000000..ac80b2a5734b --- /dev/null +++ b/tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_1.scala @@ -0,0 +1,11 @@ +//> using options -experimental +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/tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_2.scala b/tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_2.scala new file mode 100644 index 000000000000..5140d999ee9b --- /dev/null +++ b/tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_2.scala @@ -0,0 +1,12 @@ +//> using options -experimental +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/tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_3.scala b/tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_3.scala new file mode 100644 index 000000000000..c80c33672d3e --- /dev/null +++ b/tests/run/unroll-secondaryConstructor-integration/UnrollTestMain_3.scala @@ -0,0 +1,15 @@ +//> using options -experimental +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/tests/run/unroll-secondaryConstructor-integration/UnrollTestPlatformSpecificJS_3.scala b/tests/run/unroll-secondaryConstructor-integration/UnrollTestPlatformSpecificJS_3.scala new file mode 100644 index 000000000000..190890788014 --- /dev/null +++ b/tests/run/unroll-secondaryConstructor-integration/UnrollTestPlatformSpecificJS_3.scala @@ -0,0 +1,9 @@ +//> using options -experimental +//> using target.platform scala-js +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + // do nothing for scala-js + } +} diff --git a/tests/run/unroll-secondaryConstructor-integration/UnrollTestPlatformSpecific_3.scala b/tests/run/unroll-secondaryConstructor-integration/UnrollTestPlatformSpecific_3.scala new file mode 100644 index 000000000000..0ec252f740f4 --- /dev/null +++ b/tests/run/unroll-secondaryConstructor-integration/UnrollTestPlatformSpecific_3.scala @@ -0,0 +1,35 @@ +//> using options -experimental +//> using target.platform jvm +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.map(_.toString).sorted.foreach(println) + } +} diff --git a/tests/run/unroll-secondaryConstructor-integration/Unrolled_1.scala b/tests/run/unroll-secondaryConstructor-integration/Unrolled_1.scala new file mode 100644 index 000000000000..855b20efa8f6 --- /dev/null +++ b/tests/run/unroll-secondaryConstructor-integration/Unrolled_1.scala @@ -0,0 +1,10 @@ +//> using options -experimental +package unroll + +class Unrolled(){ + var foo = "" + def this(s: String, n: Int = 1) = { + this() + foo = s + n + } +} diff --git a/tests/run/unroll-secondaryConstructor-integration/Unrolled_2.scala b/tests/run/unroll-secondaryConstructor-integration/Unrolled_2.scala new file mode 100644 index 000000000000..b8f1f4f28328 --- /dev/null +++ b/tests/run/unroll-secondaryConstructor-integration/Unrolled_2.scala @@ -0,0 +1,13 @@ +//> using options -experimental +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/tests/run/unroll-secondaryConstructor-integration/Unrolled_3.scala b/tests/run/unroll-secondaryConstructor-integration/Unrolled_3.scala new file mode 100644 index 000000000000..1da3e0d69ec6 --- /dev/null +++ b/tests/run/unroll-secondaryConstructor-integration/Unrolled_3.scala @@ -0,0 +1,13 @@ +//> using options -experimental +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/tests/run/unroll-traitMethod-integration.jvm.check b/tests/run/unroll-traitMethod-integration.jvm.check new file mode 100644 index 000000000000..1f83c55f588f --- /dev/null +++ b/tests/run/unroll-traitMethod-integration.jvm.check @@ -0,0 +1,31 @@ +Assertion passed: found "cow1" + "true0" +Assertion passed: found "cow2" + "true0" +Assertion passed: found "cow1" + "true0" +Assertion passed: found "cow2" + "true0" +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" + +public default boolean unroll.Unrolled.foo$default$3() +public default int unroll.Unrolled.foo$default$2() +public default java.lang.String unroll.Unrolled.foo(java.lang.String,int) +public default java.lang.String unroll.Unrolled.foo(java.lang.String,int,boolean) +public default java.lang.String unroll.Unrolled.foo(java.lang.String,int,boolean,long) +public default long unroll.Unrolled.foo$default$4() +public static boolean unroll.Unrolled.foo$default$3$(unroll.Unrolled) +public static int unroll.Unrolled.foo$default$2$(unroll.Unrolled) +public static java.lang.String unroll.Unrolled.foo$(unroll.Unrolled,java.lang.String,int) +public static java.lang.String unroll.Unrolled.foo$(unroll.Unrolled,java.lang.String,int,boolean) +public static java.lang.String unroll.Unrolled.foo$(unroll.Unrolled,java.lang.String,int,boolean,long) +public static long unroll.Unrolled.foo$default$4$(unroll.Unrolled) +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" diff --git a/tests/run/unroll-traitMethod-integration.scala-js.check b/tests/run/unroll-traitMethod-integration.scala-js.check new file mode 100644 index 000000000000..1ac3ffa822f0 --- /dev/null +++ b/tests/run/unroll-traitMethod-integration.scala-js.check @@ -0,0 +1,18 @@ +Assertion passed: found "cow1" + "true0" +Assertion passed: found "cow2" + "true0" +Assertion passed: found "cow1" + "true0" +Assertion passed: found "cow2" + "true0" +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 "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" +Assertion passed: found "cow1true0" +Assertion passed: found "cow2true0" +Assertion passed: found "cow2false0" +Assertion passed: found "cow2false3" 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..dc60b5bf2059 --- /dev/null +++ b/tests/run/unroll-traitMethod-integration/Test_4.scala @@ -0,0 +1,7 @@ +//> using options -experimental +import unroll.* + +@main def Test: Unit = + UnrollTestMainV1.main(Array.empty[String]) + UnrollTestMainV2.main(Array.empty[String]) + UnrollTestMainV3.main(Array.empty[String]) diff --git a/tests/run/unroll-traitMethod-integration/UnrollTestMain_1.scala b/tests/run/unroll-traitMethod-integration/UnrollTestMain_1.scala new file mode 100644 index 000000000000..b290c95e1f9c --- /dev/null +++ b/tests/run/unroll-traitMethod-integration/UnrollTestMain_1.scala @@ -0,0 +1,15 @@ +//> using options -experimental +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/tests/run/unroll-traitMethod-integration/UnrollTestMain_2.scala b/tests/run/unroll-traitMethod-integration/UnrollTestMain_2.scala new file mode 100644 index 000000000000..6721b302e3f1 --- /dev/null +++ b/tests/run/unroll-traitMethod-integration/UnrollTestMain_2.scala @@ -0,0 +1,17 @@ +//> using options -experimental +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/tests/run/unroll-traitMethod-integration/UnrollTestMain_3.scala b/tests/run/unroll-traitMethod-integration/UnrollTestMain_3.scala new file mode 100644 index 000000000000..d4091763a86f --- /dev/null +++ b/tests/run/unroll-traitMethod-integration/UnrollTestMain_3.scala @@ -0,0 +1,21 @@ +//> using options -experimental +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/tests/run/unroll-traitMethod-integration/UnrollTestPlatformSpecificJS_3.scala b/tests/run/unroll-traitMethod-integration/UnrollTestPlatformSpecificJS_3.scala new file mode 100644 index 000000000000..190890788014 --- /dev/null +++ b/tests/run/unroll-traitMethod-integration/UnrollTestPlatformSpecificJS_3.scala @@ -0,0 +1,9 @@ +//> using options -experimental +//> using target.platform scala-js +package unroll + +object UnrollTestPlatformSpecificV3{ + def apply() = { + // do nothing for scala-js + } +} diff --git a/tests/run/unroll-traitMethod-integration/UnrollTestPlatformSpecific_3.scala b/tests/run/unroll-traitMethod-integration/UnrollTestPlatformSpecific_3.scala new file mode 100644 index 000000000000..bf004b8f656d --- /dev/null +++ b/tests/run/unroll-traitMethod-integration/UnrollTestPlatformSpecific_3.scala @@ -0,0 +1,30 @@ +//> using options -experimental +//> using target.platform jvm +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")).map(_.toString).sorted.foreach(println) + + } +} diff --git a/tests/run/unroll-traitMethod-integration/Unrolled_1.scala b/tests/run/unroll-traitMethod-integration/Unrolled_1.scala new file mode 100644 index 000000000000..aa9375698103 --- /dev/null +++ b/tests/run/unroll-traitMethod-integration/Unrolled_1.scala @@ -0,0 +1,8 @@ +//> using options -experimental +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/tests/run/unroll-traitMethod-integration/Unrolled_2.scala b/tests/run/unroll-traitMethod-integration/Unrolled_2.scala new file mode 100644 index 000000000000..242054d41be4 --- /dev/null +++ b/tests/run/unroll-traitMethod-integration/Unrolled_2.scala @@ -0,0 +1,10 @@ +//> using options -experimental +package unroll + +import scala.annotation.unroll + +trait Unrolled{ + final def foo(s: String, n: Int = 1, @unroll b: Boolean = true) = s + n + b +} + +object Unrolled extends Unrolled diff --git a/tests/run/unroll-traitMethod-integration/Unrolled_3.scala b/tests/run/unroll-traitMethod-integration/Unrolled_3.scala new file mode 100644 index 000000000000..398ddbb4da22 --- /dev/null +++ b/tests/run/unroll-traitMethod-integration/Unrolled_3.scala @@ -0,0 +1,10 @@ +//> using options -experimental +package unroll + +import scala.annotation.unroll + +trait Unrolled{ + final def foo(s: String, n: Int = 1, @unroll b: Boolean = true, @unroll l: Long = 0) = s + n + b + l +} + +object Unrolled extends Unrolled 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"))