diff --git a/compiler/src/dotty/tools/dotc/transform/Constructors.scala b/compiler/src/dotty/tools/dotc/transform/Constructors.scala index 9a0df830c6d7..9dfc4e113fa3 100644 --- a/compiler/src/dotty/tools/dotc/transform/Constructors.scala +++ b/compiler/src/dotty/tools/dotc/transform/Constructors.scala @@ -262,7 +262,8 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase = val setter = if (symSetter.exists) symSetter else sym.accessorNamed(Mixin.traitSetterName(sym.asTerm)) - constrStats += Apply(ref(setter), intoConstr(stat.rhs, sym).withSpan(stat.span) :: Nil) + if setter.exists then + constrStats += Apply(ref(setter), intoConstr(stat.rhs, sym).withSpan(stat.span) :: Nil) clsStats += cpy.DefDef(stat)(rhs = EmptyTree) case DefDef(nme.CONSTRUCTOR, ((outerParam @ ValDef(nme.OUTER, _, _)) :: _) :: Nil, _, _) => clsStats += mapOuter(outerParam.symbol).transform(stat) diff --git a/compiler/src/dotty/tools/dotc/transform/Mixin.scala b/compiler/src/dotty/tools/dotc/transform/Mixin.scala index 9a19c0dc414f..898368497762 100644 --- a/compiler/src/dotty/tools/dotc/transform/Mixin.scala +++ b/compiler/src/dotty/tools/dotc/transform/Mixin.scala @@ -135,7 +135,12 @@ class Mixin extends MiniPhase with SymTransformer { thisPhase => if (sym.is(Accessor, butNot = Deferred) && ownerIsTrait) { val sym1 = if (sym.is(Lazy) || sym.symbol.isConstExprFinalVal) sym - else sym.copySymDenotation(initFlags = sym.flags &~ (ParamAccessor | Inline) | Deferred) + else + if sym.hasAnnotation(defn.UnrollAnnot) then + ??? + sym.copySymDenotation(initFlags = sym.flags &~ (ParamAccessor | Inline)) + else + sym.copySymDenotation(initFlags = sym.flags &~ (ParamAccessor | Inline) | Deferred) sym1.ensureNotPrivate } else if sym.isAllOf(ModuleClass | Private) && ownerIsTrait then diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index d5bb6a52ce39..dd13df3dba8b 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -142,9 +142,9 @@ class PostTyper extends MacroTransform with InfoTransformer { thisPhase => report.error("Unrolled method must be final and concrete", method.srcPos) res = false val isCtor = method.isConstructor - if isCtor && method.owner.is(Trait) then - report.error("implementation restriction: Unrolled method cannot be a trait constructor", method.srcPos) - res = false + // if isCtor && method.owner.is(Trait) then + // report.error("implementation restriction: Unrolled method cannot be a trait constructor", method.srcPos) + // res = false if !(isCtor || method.is(Final) || method.owner.is(ModuleClass)) then report.error(s"Unrolled method ${method} must be final", method.srcPos) res = false diff --git a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala index 0b817d492263..136e597633c7 100644 --- a/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala +++ b/compiler/src/dotty/tools/dotc/transform/UnrollDefinitions.scala @@ -264,7 +264,25 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { ).setDefTree } - def generateSyntheticDefs(tree: Tree, compute: ComputeIndicies)(using Context): Option[(Symbol, Option[Symbol], Seq[DefDef])] = tree match { + def generateSyntheticDefs(tree: Tree, compute: ComputeIndicies)(using Context): Option[(Symbol, Option[Symbol], Seq[ValOrDefDef])] = tree match { + case valdef: ValDef if valdef.symbol.is(ParamAccessor) && valdef.symbol.owner.is(Trait) => + val ctor = valdef.symbol.owner.primaryConstructor + compute(ctor) match { + case Nil => None + case Seq((paramClauseIndex, annotationIndices)) => + val defaultParams = ctor.paramSymss(paramClauseIndex).drop(annotationIndices.head) + // assert(firstAnnotated.hasAnnotation(defn.UnrollAnnot)) + // report.warning(i"TODO: implemet for $defaultParams") + + if defaultParams.exists(_.name == valdef.name) then + val rhs = ref(defn.ScalaPredefModule).select(defn.Predef_undefined).ensureApplied + val valdef0 = cpy.ValDef(valdef)(rhs = rhs) + Some((valdef.symbol, Some(valdef.symbol), Seq(valdef0))) + else + None + case multiple => sys.error("Cannot have multiple parameter lists containing `@unroll` annotation") + } + case defdef: DefDef if defdef.paramss.nonEmpty => import dotty.tools.dotc.core.NameOps.isConstructorName @@ -276,6 +294,8 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { val isCaseFromProduct = defdef.name.toString == "fromProduct" && defdef.symbol.owner.companionClass.is(CaseClass) + val isTraitConstructor = defdef.name.isConstructorName && defdef.symbol.owner.is(Trait) + val annotated = if (isCaseCopy) defdef.symbol.owner.primaryConstructor else if (isCaseApply) defdef.symbol.owner.companionClass.primaryConstructor @@ -286,7 +306,9 @@ class UnrollDefinitions extends MacroTransform, IdentityDenotTransformer { case Nil => None case Seq((paramClauseIndex, annotationIndices)) => val paramCount = annotated.paramSymss(paramClauseIndex).size - if isCaseFromProduct then + if isTraitConstructor then + None // we must not generate forwarder methods for trait constructors + else if isCaseFromProduct then Some((defdef.symbol, Some(defdef.symbol), Seq(generateFromProduct(annotationIndices, paramCount, defdef)))) else val (generatedDefs, _) = diff --git a/tests/neg/unroll-traitConstructor.check b/tests/neg/unroll-traitConstructor.check deleted file mode 100644 index 74e3eff5999b..000000000000 --- a/tests/neg/unroll-traitConstructor.check +++ /dev/null @@ -1,4 +0,0 @@ --- Error: tests/neg/unroll-traitConstructor.scala:5:12 ----------------------------------------------------------------- -5 |trait Unroll(a: String, @unroll b: Boolean = true): // error - | ^ - | implementation restriction: Unrolled method cannot be a trait constructor diff --git a/tests/neg/unroll-traitConstructor.scala b/tests/neg/unroll-traitConstructor.scala deleted file mode 100644 index 3c48852d8303..000000000000 --- a/tests/neg/unroll-traitConstructor.scala +++ /dev/null @@ -1,8 +0,0 @@ -//> 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/run/unroll-traitConstructor/Test_3.scala b/tests/run/unroll-traitConstructor/Test_3.scala new file mode 100644 index 000000000000..8ee87331bfce --- /dev/null +++ b/tests/run/unroll-traitConstructor/Test_3.scala @@ -0,0 +1,9 @@ +//> using options -experimental + +@main def Test: Unit = + val bar = Bar(1, "abc", 2, 20) + assert(bar.res == 23) + + val foo = Foo(1, "abc", 2) + assert(foo.res == 13) +end Test diff --git a/tests/run/unroll-traitConstructor/Unroll_1.scala b/tests/run/unroll-traitConstructor/Unroll_1.scala new file mode 100644 index 000000000000..b688dec6ffbe --- /dev/null +++ b/tests/run/unroll-traitConstructor/Unroll_1.scala @@ -0,0 +1,8 @@ +//> using options -experimental + +// import scala.annotation.unroll + +trait Unrolled(x: Int, s: String)(y: Int): + def res: Int = x + y + +class Foo(x: Int, s: String, y: Int) extends Unrolled(x, s)(y) diff --git a/tests/run/unroll-traitConstructor/Unroll_2.scala b/tests/run/unroll-traitConstructor/Unroll_2.scala new file mode 100644 index 000000000000..b5950404b873 --- /dev/null +++ b/tests/run/unroll-traitConstructor/Unroll_2.scala @@ -0,0 +1,8 @@ +//> using options -experimental + +import scala.annotation.unroll + +trait Unrolled(x: Int, s: String)(y: Int, @unroll z: Int = 10): + def res: Int = x + y + z + +class Bar(x: Int, s: String, y: Int, z: Int) extends Unrolled(x, s)(y, z)