diff --git a/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala b/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala index 99a1df8bbe47..680949260515 100644 --- a/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala +++ b/src/compiler/scala/tools/nsc/tasty/TreeUnpickler.scala @@ -734,24 +734,21 @@ class TreeUnpickler[Tasty <: TastyUniverse]( val isMacro = repr.originalFlagSet.is(Erased | Macro) checkUnsupportedFlags(repr.tastyOnlyFlags &~ (Extension | Exported | Infix | optFlag(isMacro)(Erased))) val isCtor = sym.isConstructor - val paramDefss = readParamss(skipTypeParams=isCtor)(localCtx) // TODO: this can mix type and term parameters, must skip type params if ctor + val paramDefss = readParamss(skipTypeParams=isCtor)(localCtx) val typeParams: List[Symbol] = { if (isCtor) { - // skipTypeParams() sym.owner.typeParams } else { - // readParams[NoCycle](TYPEPARAM)(localCtx).map(symFromNoCycle) - - // TODO: Handle extension methods with multiple param lists val first = paramDefss.take(1).flatten.map(symFromNoCycle) if (first.headOption.exists(_.isType)) first else Nil } } val vparamss: List[List[NoCycle]] = { - // TODO: Handle extension methods with multiple param lists - val droppedClauses = if (typeParams.isEmpty) 0 else 1 - paramDefss.drop(droppedClauses).ensuring(_.flatten.forall(nc => symFromNoCycle(nc).isTerm)) + val valueClauses = paramDefss.drop(if (typeParams.isEmpty) 0 else 1) + val hasTypeParams = valueClauses.exists(_.exists(nc => symFromNoCycle(nc).isType)) + unsupportedWhen(hasTypeParams, s"extension method with secondary type parameter list: $tname") + valueClauses } val tpt = readTpt()(localCtx) if (isMacro) { diff --git a/test/tasty/neg/src-2/TestDependentExtension.check b/test/tasty/neg/src-2/TestDependentExtension.check new file mode 100644 index 000000000000..4a8c391801cf --- /dev/null +++ b/test/tasty/neg/src-2/TestDependentExtension.check @@ -0,0 +1,4 @@ +TestDependentExtension_fail.scala:11: error: Unsupported Scala 3 extension method with secondary type parameter list: extract; found in trait tastytest.DependentExtension. + val res = implicitly[DependentExtension].extract(box)(_ + 1) + ^ +1 error diff --git a/test/tasty/neg/src-2/TestDependentExtension_fail.scala b/test/tasty/neg/src-2/TestDependentExtension_fail.scala new file mode 100644 index 000000000000..cfd4bdee8a84 --- /dev/null +++ b/test/tasty/neg/src-2/TestDependentExtension_fail.scala @@ -0,0 +1,15 @@ +package tastytest + +object TestDependentExtension { + import DependentExtension._ + + def test = { + val box = new Box { + type Repr = Int + val value: Int = 23 + } + val res = implicitly[DependentExtension].extract(box)(_ + 1) + assert(res == 24) + } + +} diff --git a/test/tasty/neg/src-2/TestRealFunctor.check b/test/tasty/neg/src-2/TestRealFunctor.check new file mode 100644 index 000000000000..f992b382783d --- /dev/null +++ b/test/tasty/neg/src-2/TestRealFunctor.check @@ -0,0 +1,4 @@ +TestRealFunctor_fail.scala:6: error: Unsupported Scala 3 extension method with secondary type parameter list: map; found in trait tastytest.RealFunctor. + def map[A, B](fa: List[A])(f: A => B): List[B] = fa.map(f) + ^ +1 error diff --git a/test/tasty/neg/src-2/TestRealFunctor_fail.scala b/test/tasty/neg/src-2/TestRealFunctor_fail.scala new file mode 100644 index 000000000000..75e868da2d67 --- /dev/null +++ b/test/tasty/neg/src-2/TestRealFunctor_fail.scala @@ -0,0 +1,9 @@ +package tastytest + +object TestRealFunctor { + // in this test, we try to implement an extension method that has two type-parameter lists + implicit object ListRealFunctor extends RealFunctor[List] { + def map[A, B](fa: List[A])(f: A => B): List[B] = fa.map(f) + } + +} diff --git a/test/tasty/neg/src-3/DependentExtension.scala b/test/tasty/neg/src-3/DependentExtension.scala new file mode 100644 index 000000000000..f076c9197246 --- /dev/null +++ b/test/tasty/neg/src-3/DependentExtension.scala @@ -0,0 +1,20 @@ +package tastytest + +import DependentExtension.Box + +// `DependentExtension` uses two type parameter lists in its `extract` method. the second type parameter list is +// dependent on the first term parameter list. It is unclear how to interpret this. +trait DependentExtension { + extension [B <: Box](b: B) def extract[R >: b.Repr, O](f: R => O): O = f(b.value) +} + +object DependentExtension { + + given DependentExtension with {} + + trait Box { + type Repr + val value: Repr + } + +} diff --git a/test/tasty/neg/src-3/RealFunctor.scala b/test/tasty/neg/src-3/RealFunctor.scala new file mode 100644 index 000000000000..ec2b3977ca56 --- /dev/null +++ b/test/tasty/neg/src-3/RealFunctor.scala @@ -0,0 +1,7 @@ +package tastytest + +// This `Functor` uses two type parameter lists in its `map` method. +// In this example, it may be safe to merge the type parameter lists. +trait RealFunctor[F[_]] { + extension [A](fa: F[A]) def map[B](f: A => B): F[B] +}