diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index a537cdeb0f0e..880086022147 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -218,15 +218,32 @@ object desugar { * def f$default$2[T](x: Int) = x + "m" */ private def defDef(meth: DefDef, isPrimaryConstructor: Boolean = false)(using Context): Tree = - addDefaultGetters(elimContextBounds(meth, isPrimaryConstructor)) + addDefaultGetters(elimContextBounds(Nil, meth, isPrimaryConstructor)) - private def elimContextBounds(meth: DefDef, isPrimaryConstructor: Boolean)(using Context): DefDef = + private def defDef(extParamss: List[ParamClause], meth: DefDef)(using Context): Tree = + addDefaultGetters(elimContextBounds(extParamss, meth, false)) + + private def elimContextBounds(extParamss: List[ParamClause], meth: DefDef, isPrimaryConstructor: Boolean)(using Context): DefDef = val DefDef(_, paramss, tpt, rhs) = meth + + rhs match + case MacroTree(call) => + cpy.DefDef(meth)(rhs = call).withMods(meth.mods | Macro | Erased) + case _ => + cpy.DefDef(meth)( + name = normalizeName(meth, tpt).asTermName, + paramss = + elimContextBounds(extParamss, isPrimaryConstructor, true) ++ + elimContextBounds(paramss, isPrimaryConstructor, false) + ) + end elimContextBounds + + private def elimContextBounds(paramss: List[ParamClause], isPrimaryConstructor: Boolean, ext: Boolean)(using Context): List[ParamClause] = val evidenceParamBuf = ListBuffer[ValDef]() def desugarContextBounds(rhs: Tree): Tree = rhs match case ContextBounds(tbounds, cxbounds) => - val iflag = if sourceVersion.isAtLeast(`future`) then Given else Implicit + val iflag = if ext || sourceVersion.isAtLeast(`future`) then Given else Implicit evidenceParamBuf ++= makeImplicitParameters( cxbounds, iflag, forPrimaryConstructor = isPrimaryConstructor) tbounds @@ -240,15 +257,7 @@ object desugar { tparam => cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs)) }(identity) - rhs match - case MacroTree(call) => - cpy.DefDef(meth)(rhs = call).withMods(meth.mods | Macro | Erased) - case _ => - addEvidenceParams( - cpy.DefDef(meth)( - name = normalizeName(meth, tpt).asTermName, - paramss = paramssNoContextBounds), - evidenceParamBuf.toList) + addEvidenceParams(paramssNoContextBounds, evidenceParamBuf.toList) end elimContextBounds def addDefaultGetters(meth: DefDef)(using Context): Tree = @@ -348,22 +357,22 @@ object desugar { adaptToExpectedTpt(tree) } - /** Add all evidence parameters in `params` as implicit parameters to `meth`. - * If the parameters of `meth` end in an implicit parameter list or using clause, + /** Add all evidence parameters in `params` as implicit parameters to `paramss`. + * If the parameters of `paramss` end in an implicit parameter list or using clause, * evidence parameters are added in front of that list. Otherwise they are added * as a separate parameter clause. */ - private def addEvidenceParams(meth: DefDef, params: List[ValDef])(using Context): DefDef = - params match - case Nil => - meth - case evidenceParams => - val paramss1 = meth.paramss.reverse match - case ValDefs(vparams @ (vparam :: _)) :: rparamss if vparam.mods.isOneOf(GivenOrImplicit) => - ((evidenceParams ++ vparams) :: rparamss).reverse - case _ => - meth.paramss :+ evidenceParams - cpy.DefDef(meth)(paramss = paramss1) + + private def addEvidenceParams(paramss: List[ParamClause], params: List[ValDef])(using Context): List[ParamClause] = + paramss.reverse match + case ValDefs(vparams @ (vparam :: _)) :: rparamss if vparam.mods.isOneOf(GivenOrImplicit) => + ((params ++ vparams) :: rparamss).reverse + case _ => + params match + case Nil => + paramss + case evidenceParams => + paramss :+ evidenceParams /** The implicit evidence parameters of `meth`, as generated by `desugar.defDef` */ private def evidenceParams(meth: DefDef)(using Context): List[ValDef] = @@ -487,9 +496,8 @@ object desugar { case ddef: DefDef if ddef.name.isConstructorName => decompose( defDef( - addEvidenceParams( - cpy.DefDef(ddef)(paramss = joinParams(constrTparams, ddef.paramss)), - evidenceParams(constr1).map(toDefParam(_, keepAnnotations = false, keepDefault = false))))) + cpy.DefDef(ddef)(paramss = addEvidenceParams(joinParams(constrTparams, ddef.paramss), + evidenceParams(constr1).map(toDefParam(_, keepAnnotations = false, keepDefault = false)))))) case stat => stat } @@ -906,34 +914,29 @@ object desugar { /** Transform extension construct to list of extension methods */ def extMethods(ext: ExtMethods)(using Context): Tree = flatTree { for mdef <- ext.methods yield - defDef( - cpy.DefDef(mdef)( - name = normalizeName(mdef, ext).asTermName, - paramss = mdef.paramss match - case params1 :: paramss1 if mdef.name.isRightAssocOperatorName => - def badRightAssoc(problem: String) = - report.error(i"right-associative extension method $problem", mdef.srcPos) - ext.paramss ++ mdef.paramss - def noVParam = badRightAssoc("must start with a single parameter") - def checkVparam(params: ParamClause) = params match - case ValDefs(vparam :: Nil) => - if !vparam.mods.is(Given) then - val (leadingUsing, otherExtParamss) = ext.paramss.span(isUsingOrTypeParamClause) - leadingUsing ::: params1 :: otherExtParamss ::: paramss1 - else badRightAssoc("cannot start with using clause") - case _ => - noVParam - params1 match - case TypeDefs(_) => paramss1 match - case params2 :: _ => checkVparam(params2) - case _ => noVParam - case _ => - checkVparam(params1) - + def ret(ess: List[ParamClause], mss: List[ParamClause]) = + defDef( + ess, + cpy.DefDef(mdef)( + name = normalizeName(mdef, ext).asTermName, + paramss = mss + ).withMods(mdef.mods | ExtensionMethod) + ) + mdef.paramss match + case params1 :: paramss1 if mdef.name.isRightAssocOperatorName => + def badRightAssoc(problem: String) = + report.error(i"right-associative extension method $problem", mdef.srcPos) + ret(ext.paramss, mdef.paramss) + params1 match + case ValDefs(vparam :: Nil) => + if !vparam.mods.is(Given) then + val (leadingUsing, otherExtParamss) = ext.paramss.span(isUsingOrTypeParamClause) + ret(Nil, leadingUsing ::: params1 :: otherExtParamss ::: paramss1) + else badRightAssoc("cannot start with using clause") case _ => - ext.paramss ++ mdef.paramss - ).withMods(mdef.mods | ExtensionMethod) - ) + badRightAssoc("must start with a single parameter") + case _ => + ret(ext.paramss, mdef.paramss) } /** Transforms diff --git a/tests/neg/i10901.check b/tests/neg/i10901.check index 01a1cc0cdb21..55a8f23e60ac 100644 --- a/tests/neg/i10901.check +++ b/tests/neg/i10901.check @@ -1,21 +1,18 @@ -- [E008] Not Found Error: tests/neg/i10901.scala:45:38 ---------------------------------------------------------------- 45 | val pos1: Point2D[Int,Double] = x º y // error | ^^^ - | value º is not a member of object BugExp4Point2D.IntT. - | An extension method was tried, but could not be fully constructed: + |value º is not a member of object BugExp4Point2D.IntT. + |An extension method was tried, but could not be fully constructed: | - | º(x) failed with + | º(x) failed with | - | Ambiguous overload. The overloaded alternatives of method º in object dsl with types - | [T1, T2] - | (x: BugExp4Point2D.ColumnType[T1]) - | (y: BugExp4Point2D.ColumnType[T2]) - | (implicit evidence$7: Numeric[T1], evidence$8: Numeric[T2]): BugExp4Point2D.Point2D[T1, T2] - | [T1, T2] - | (x: T1) - | (y: BugExp4Point2D.ColumnType[T2]) - | (implicit evidence$5: Numeric[T1], evidence$6: Numeric[T2]): BugExp4Point2D.Point2D[T1, T2] - | both match arguments ((x : BugExp4Point2D.IntT.type)) + | Ambiguous overload. The overloaded alternatives of method º in object dsl with types + | [T1, T2] + | (x: BugExp4Point2D.ColumnType[T1]) + | (y: BugExp4Point2D.ColumnType[T2])(using x$3: Numeric[T1], x$4: Numeric[T2]): BugExp4Point2D.Point2D[T1, T2] + | [T1, T2] + | (x: T1)(y: BugExp4Point2D.ColumnType[T2])(using x$3: Numeric[T1], x$4: Numeric[T2]): BugExp4Point2D.Point2D[T1, T2] + | both match arguments ((x : BugExp4Point2D.IntT.type)) -- [E008] Not Found Error: tests/neg/i10901.scala:48:38 ---------------------------------------------------------------- 48 | val pos4: Point2D[Int,Double] = x º 201.1 // error | ^^^ @@ -26,9 +23,8 @@ | | Ambiguous overload. The overloaded alternatives of method º in object dsl with types | [T1, T2] - | (x: BugExp4Point2D.ColumnType[T1]) - | (y: T2)(implicit evidence$9: Numeric[T1], evidence$10: Numeric[T2]): BugExp4Point2D.Point2D[T1, T2] - | [T1, T2](x: T1)(y: T2)(implicit evidence$3: Numeric[T1], evidence$4: Numeric[T2]): BugExp4Point2D.Point2D[T1, T2] + | (x: BugExp4Point2D.ColumnType[T1])(y: T2)(using x$3: Numeric[T1], x$4: Numeric[T2]): BugExp4Point2D.Point2D[T1, T2] + | [T1, T2](x: T1)(y: T2)(using x$3: Numeric[T1], x$4: Numeric[T2]): BugExp4Point2D.Point2D[T1, T2] | both match arguments ((x : BugExp4Point2D.IntT.type)) -- [E008] Not Found Error: tests/neg/i10901.scala:62:16 ---------------------------------------------------------------- 62 | val y = "abc".foo // error diff --git a/tests/neg/i10901.scala b/tests/neg/i10901.scala index 19a2e3023c85..5ef6d6e08554 100644 --- a/tests/neg/i10901.scala +++ b/tests/neg/i10901.scala @@ -9,27 +9,27 @@ object BugExp4Point2D { object dsl { - extension [T1:Numeric, T2:Numeric](x: T1) + extension [T1, T2](x: T1) // N - N @targetName("point2DConstant") - def º(y: T2): Point2D[T1,T2] = ??? + def º(y: T2)(using Numeric[T1], Numeric[T2]): Point2D[T1,T2] = ??? // N - C @targetName("point2DConstantData") - def º(y: ColumnType[T2]): Point2D[T1,T2] = ??? + def º(y: ColumnType[T2])(using Numeric[T1], Numeric[T2]): Point2D[T1,T2] = ??? - extension [T1:Numeric, T2:Numeric](x: ColumnType[T1]) + extension [T1, T2](x: ColumnType[T1]) // C - C @targetName("point2DData") - def º(y: ColumnType[T2]): Point2D[T1,T2] = ??? + def º(y: ColumnType[T2])(using Numeric[T1], Numeric[T2]): Point2D[T1,T2] = ??? // C - N @targetName("point2DDataConstant") - def º(y: T2): Point2D[T1,T2] = ??? + def º(y: T2)(using Numeric[T1], Numeric[T2]): Point2D[T1,T2] = ??? } diff --git a/tests/neg/missing-implicit1.check b/tests/neg/missing-implicit1.check index 4ff1d5d84225..f93175f8ea40 100644 --- a/tests/neg/missing-implicit1.check +++ b/tests/neg/missing-implicit1.check @@ -19,7 +19,7 @@ -- Error: tests/neg/missing-implicit1.scala:23:42 ---------------------------------------------------------------------- 23 | List(1, 2, 3).traverse(x => Option(x)) // error | ^ - |no implicit argument of type testObjectInstance.Zip[Option] was found for an implicit parameter of method traverse in trait Traverse + |no implicit argument of type testObjectInstance.Zip[Option] was found for parameter x$3 of method traverse in trait Traverse | |The following import might fix the problem: | diff --git a/tests/neg/missing-implicit1.scala b/tests/neg/missing-implicit1.scala index 66c70f446d37..f530004fae4f 100644 --- a/tests/neg/missing-implicit1.scala +++ b/tests/neg/missing-implicit1.scala @@ -1,7 +1,7 @@ object testObjectInstance: trait Zip[F[_]] trait Traverse[F[_]] { - extension [A, B, G[_] : Zip](fa: F[A]) def traverse(f: A => G[B]): G[F[B]] + extension [A, B, G[_]](fa: F[A]) def traverse(f: A => G[B])(using Zip[G]): G[F[B]] } object instances { diff --git a/tests/neg/missing-implicit4.check b/tests/neg/missing-implicit4.check index 4653dd8df351..108ceb37cf2f 100644 --- a/tests/neg/missing-implicit4.check +++ b/tests/neg/missing-implicit4.check @@ -19,9 +19,9 @@ -- Error: tests/neg/missing-implicit4.scala:20:42 ---------------------------------------------------------------------- 20 | List(1, 2, 3).traverse(x => Option(x)) // error | ^ - |no implicit argument of type Zip[Option] was found for an implicit parameter of method traverse in trait Traverse + | no implicit argument of type Zip[Option] was found for parameter x$3 of method traverse in trait Traverse | - |The following import might fix the problem: + | The following import might fix the problem: | - | import instances.zipOption + | import instances.zipOption | diff --git a/tests/neg/missing-implicit4.scala b/tests/neg/missing-implicit4.scala index 317dc516c7c3..1a22560578b3 100644 --- a/tests/neg/missing-implicit4.scala +++ b/tests/neg/missing-implicit4.scala @@ -1,7 +1,7 @@ def testLocalInstance = trait Zip[F[_]] trait Traverse[F[_]] { - extension [A, B, G[_] : Zip](fa: F[A]) def traverse(f: A => G[B]): G[F[B]] + extension [A, B, G[_]](fa: F[A]) def traverse(f: A => G[B])(using Zip[G]): G[F[B]] } object instances { diff --git a/tests/pos/i11358.scala b/tests/pos/i11358.scala index aa246a45fba4..3046589fde6b 100644 --- a/tests/pos/i11358.scala +++ b/tests/pos/i11358.scala @@ -9,6 +9,6 @@ object Test: def test7 = +++(IArray(1, 2))[Int](IArray(2, 3)) def test8 = +++(IArray(1, 2))[Int](List(2, 3)) - extension [A: reflect.ClassTag](arr: IArray[A]) - def +++[B >: A: reflect.ClassTag](suffix: IArray[B]): IArray[B] = ??? - def +++[B >: A: reflect.ClassTag](suffix: IterableOnce[B]): IArray[B] = ??? + extension [A](arr: IArray[A]) + def +++[B >: A](suffix: IArray[B])(using reflect.ClassTag[A], reflect.ClassTag[B]): IArray[B] = ??? + def +++[B >: A](suffix: IterableOnce[B])(using reflect.ClassTag[A], reflect.ClassTag[B]): IArray[B] = ??? diff --git a/tests/pos/i11583.scala b/tests/pos/i11583.scala deleted file mode 100644 index d3c3dcb84329..000000000000 --- a/tests/pos/i11583.scala +++ /dev/null @@ -1,2 +0,0 @@ -extension (s: String) - def :*:[T <: Tuple](that: T) : String *: T = ??? diff --git a/tests/pos/i11586.scala b/tests/pos/i11586.scala new file mode 100644 index 000000000000..5518a61f76e4 --- /dev/null +++ b/tests/pos/i11586.scala @@ -0,0 +1,10 @@ +type Conv[T] = [X] =>> X => T + +trait SemiGroup[T]: + extension [U: Conv[T]](x: U) + def combine(y: T): T + extension (x: T) + def combine[U: Conv[T]](y: U): T + +trait Q[T, R: SemiGroup] extends SemiGroup[T]: + def res(x: R, y: R) = x.combine(y)