Skip to content

Commit d5fbf96

Browse files
committed
Fix desugaring of context bounds in extensions
Fixes #11586 Unfixes #11583
1 parent 5e0100d commit d5fbf96

File tree

8 files changed

+90
-89
lines changed

8 files changed

+90
-89
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 58 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -218,15 +218,32 @@ object desugar {
218218
* def f$default$2[T](x: Int) = x + "m"
219219
*/
220220
private def defDef(meth: DefDef, isPrimaryConstructor: Boolean = false)(using Context): Tree =
221-
addDefaultGetters(elimContextBounds(meth, isPrimaryConstructor))
221+
addDefaultGetters(elimContextBounds(Nil, meth, isPrimaryConstructor, false))
222222

223-
private def elimContextBounds(meth: DefDef, isPrimaryConstructor: Boolean)(using Context): DefDef =
223+
private def defDef(extParamss: List[ParamClause], meth: DefDef)(using Context): Tree =
224+
addDefaultGetters(elimContextBounds(extParamss, meth, false, true))
225+
226+
private def elimContextBounds(extParamss: List[ParamClause], meth: DefDef, isPrimaryConstructor: Boolean, ext: Boolean)(using Context): DefDef =
224227
val DefDef(_, paramss, tpt, rhs) = meth
228+
229+
rhs match
230+
case MacroTree(call) =>
231+
cpy.DefDef(meth)(rhs = call).withMods(meth.mods | Macro | Erased)
232+
case _ =>
233+
cpy.DefDef(meth)(
234+
name = normalizeName(meth, tpt).asTermName,
235+
paramss =
236+
elimContextBounds(extParamss, isPrimaryConstructor, ext) ++
237+
elimContextBounds(paramss, isPrimaryConstructor, ext)
238+
)
239+
end elimContextBounds
240+
241+
private def elimContextBounds(paramss: List[ParamClause], isPrimaryConstructor: Boolean, ext: Boolean)(using Context): List[ParamClause] =
225242
val evidenceParamBuf = ListBuffer[ValDef]()
226243

227244
def desugarContextBounds(rhs: Tree): Tree = rhs match
228245
case ContextBounds(tbounds, cxbounds) =>
229-
val iflag = if sourceVersion.isAtLeast(`future`) then Given else Implicit
246+
val iflag = if ext || sourceVersion.isAtLeast(`future`) then Given else Implicit
230247
evidenceParamBuf ++= makeImplicitParameters(
231248
cxbounds, iflag, forPrimaryConstructor = isPrimaryConstructor)
232249
tbounds
@@ -240,15 +257,7 @@ object desugar {
240257
tparam => cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs))
241258
}(identity)
242259

243-
rhs match
244-
case MacroTree(call) =>
245-
cpy.DefDef(meth)(rhs = call).withMods(meth.mods | Macro | Erased)
246-
case _ =>
247-
addEvidenceParams(
248-
cpy.DefDef(meth)(
249-
name = normalizeName(meth, tpt).asTermName,
250-
paramss = paramssNoContextBounds),
251-
evidenceParamBuf.toList)
260+
addEvidenceParams(paramssNoContextBounds, evidenceParamBuf.toList)
252261
end elimContextBounds
253262

254263
def addDefaultGetters(meth: DefDef)(using Context): Tree =
@@ -348,22 +357,22 @@ object desugar {
348357
adaptToExpectedTpt(tree)
349358
}
350359

351-
/** Add all evidence parameters in `params` as implicit parameters to `meth`.
352-
* If the parameters of `meth` end in an implicit parameter list or using clause,
360+
/** Add all evidence parameters in `params` as implicit parameters to `paramss`.
361+
* If the parameters of `paramss` end in an implicit parameter list or using clause,
353362
* evidence parameters are added in front of that list. Otherwise they are added
354363
* as a separate parameter clause.
355364
*/
356-
private def addEvidenceParams(meth: DefDef, params: List[ValDef])(using Context): DefDef =
357-
params match
358-
case Nil =>
359-
meth
360-
case evidenceParams =>
361-
val paramss1 = meth.paramss.reverse match
362-
case ValDefs(vparams @ (vparam :: _)) :: rparamss if vparam.mods.isOneOf(GivenOrImplicit) =>
363-
((evidenceParams ++ vparams) :: rparamss).reverse
364-
case _ =>
365-
meth.paramss :+ evidenceParams
366-
cpy.DefDef(meth)(paramss = paramss1)
365+
366+
private def addEvidenceParams(paramss: List[ParamClause], params: List[ValDef])(using Context): List[ParamClause] =
367+
paramss.reverse match
368+
case ValDefs(vparams @ (vparam :: _)) :: rparamss if vparam.mods.isOneOf(GivenOrImplicit) =>
369+
((params ++ vparams) :: rparamss).reverse
370+
case _ =>
371+
params match
372+
case Nil =>
373+
paramss
374+
case evidenceParams =>
375+
paramss :+ evidenceParams
367376

368377
/** The implicit evidence parameters of `meth`, as generated by `desugar.defDef` */
369378
private def evidenceParams(meth: DefDef)(using Context): List[ValDef] =
@@ -487,9 +496,8 @@ object desugar {
487496
case ddef: DefDef if ddef.name.isConstructorName =>
488497
decompose(
489498
defDef(
490-
addEvidenceParams(
491-
cpy.DefDef(ddef)(paramss = joinParams(constrTparams, ddef.paramss)),
492-
evidenceParams(constr1).map(toDefParam(_, keepAnnotations = false, keepDefault = false)))))
499+
cpy.DefDef(ddef)(paramss = addEvidenceParams(joinParams(constrTparams, ddef.paramss),
500+
evidenceParams(constr1).map(toDefParam(_, keepAnnotations = false, keepDefault = false))))))
493501
case stat =>
494502
stat
495503
}
@@ -906,34 +914,29 @@ object desugar {
906914
/** Transform extension construct to list of extension methods */
907915
def extMethods(ext: ExtMethods)(using Context): Tree = flatTree {
908916
for mdef <- ext.methods yield
909-
defDef(
910-
cpy.DefDef(mdef)(
911-
name = normalizeName(mdef, ext).asTermName,
912-
paramss = mdef.paramss match
913-
case params1 :: paramss1 if mdef.name.isRightAssocOperatorName =>
914-
def badRightAssoc(problem: String) =
915-
report.error(i"right-associative extension method $problem", mdef.srcPos)
916-
ext.paramss ++ mdef.paramss
917-
def noVParam = badRightAssoc("must start with a single parameter")
918-
def checkVparam(params: ParamClause) = params match
919-
case ValDefs(vparam :: Nil) =>
920-
if !vparam.mods.is(Given) then
921-
val (leadingUsing, otherExtParamss) = ext.paramss.span(isUsingOrTypeParamClause)
922-
leadingUsing ::: params1 :: otherExtParamss ::: paramss1
923-
else badRightAssoc("cannot start with using clause")
924-
case _ =>
925-
noVParam
926-
params1 match
927-
case TypeDefs(_) => paramss1 match
928-
case params2 :: _ => checkVparam(params2)
929-
case _ => noVParam
930-
case _ =>
931-
checkVparam(params1)
932-
917+
def ret(ess: List[ParamClause], mss: List[ParamClause]) =
918+
defDef(
919+
ess,
920+
cpy.DefDef(mdef)(
921+
name = normalizeName(mdef, ext).asTermName,
922+
paramss = mss
923+
).withMods(mdef.mods | ExtensionMethod)
924+
)
925+
mdef.paramss match
926+
case params1 :: paramss1 if mdef.name.isRightAssocOperatorName =>
927+
def badRightAssoc(problem: String) =
928+
report.error(i"right-associative extension method $problem", mdef.srcPos)
929+
ret(ext.paramss, mdef.paramss)
930+
params1 match
931+
case ValDefs(vparam :: Nil) =>
932+
if !vparam.mods.is(Given) then
933+
val (leadingUsing, otherExtParamss) = ext.paramss.span(isUsingOrTypeParamClause)
934+
ret(Nil, leadingUsing ::: params1 :: otherExtParamss ::: paramss1)
935+
else badRightAssoc("cannot start with using clause")
933936
case _ =>
934-
ext.paramss ++ mdef.paramss
935-
).withMods(mdef.mods | ExtensionMethod)
936-
)
937+
badRightAssoc("must start with a single parameter")
938+
case _ =>
939+
ret(ext.paramss, mdef.paramss)
937940
}
938941

939942
/** Transforms

tests/neg/i10901.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ object BugExp4Point2D {
4343
val y = DoubleT
4444

4545
val pos1: Point2D[Int,Double] = x º y // error
46-
val pos2: Point2D[Int,Double] = 100 º 200.1 // ok
47-
val pos3: Point2D[Int,Double] = 101 º y // ok
46+
val pos2: Point2D[Int,Double] = 100 º 200.1 // error
47+
val pos3: Point2D[Int,Double] = 101 º y // error
4848
val pos4: Point2D[Int,Double] = x º 201.1 // error
4949

5050
}

tests/neg/i11358.scala

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
object Test:
2+
3+
def test1: IArray[Int] = IArray(1, 2) +++ IArray(2, 3) // error
4+
def test2: IArray[Int] = IArray(1, 2) +++ List(2, 3) // error
5+
def test3 = +++[Int](IArray(1, 2))(IArray(2, 3))
6+
def test4 = +++[Int](IArray(1, 2))(List(2, 3))
7+
def test5: IArray[Int] = IArray(1, 2).+++[Int](IArray(2, 3)) // error
8+
def test6: IArray[Int] = IArray(1, 2).+++[Int](List(2, 3)) // error
9+
def test7 = +++(IArray(1, 2))[Int](IArray(2, 3)) // error
10+
def test8 = +++(IArray(1, 2))[Int](List(2, 3)) // error
11+
12+
extension [A: reflect.ClassTag](arr: IArray[A])
13+
def +++[B >: A: reflect.ClassTag](suffix: IArray[B]): IArray[B] = ???
14+
def +++[B >: A: reflect.ClassTag](suffix: IterableOnce[B]): IArray[B] = ???

tests/neg/missing-implicit1.check

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,7 @@
1616
|
1717
| import testObjectInstance.instances.traverseList
1818
|
19-
-- Error: tests/neg/missing-implicit1.scala:23:42 ----------------------------------------------------------------------
19+
-- [E008] Not Found Error: tests/neg/missing-implicit1.scala:23:18 -----------------------------------------------------
2020
23 | List(1, 2, 3).traverse(x => Option(x)) // error
21-
| ^
22-
|no implicit argument of type testObjectInstance.Zip[Option] was found for an implicit parameter of method traverse in trait Traverse
23-
|
24-
|The following import might fix the problem:
25-
|
26-
| import testObjectInstance.instances.zipOption
27-
|
21+
| ^^^^^^^^^^^^^^^^^^^^^^
22+
| value traverse is not a member of List[Int] - did you mean List[Int].reverse?

tests/neg/missing-implicit4.check

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,7 @@
1616
|
1717
| import instances.traverseList
1818
|
19-
-- Error: tests/neg/missing-implicit4.scala:20:42 ----------------------------------------------------------------------
19+
-- [E008] Not Found Error: tests/neg/missing-implicit4.scala:20:18 -----------------------------------------------------
2020
20 | List(1, 2, 3).traverse(x => Option(x)) // error
21-
| ^
22-
|no implicit argument of type Zip[Option] was found for an implicit parameter of method traverse in trait Traverse
23-
|
24-
|The following import might fix the problem:
25-
|
26-
| import instances.zipOption
27-
|
21+
| ^^^^^^^^^^^^^^^^^^^^^^
22+
| value traverse is not a member of List[Int] - did you mean List[Int].reverse?

tests/pos/i11358.scala

Lines changed: 0 additions & 14 deletions
This file was deleted.

tests/pos/i11583.scala

Lines changed: 0 additions & 2 deletions
This file was deleted.

tests/pos/i11586.scala

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
type Conv[T] = [X] =>> X => T
2+
3+
trait SemiGroup[T]:
4+
extension [U: Conv[T]](x: U)
5+
def combine(y: T): T
6+
extension (x: T)
7+
def combine[U: Conv[T]](y: U): T
8+
9+
trait Q[T, R: SemiGroup] extends SemiGroup[T]:
10+
def res(x: R, y: R) = x.combine(y)

0 commit comments

Comments
 (0)