Skip to content

Commit

Permalink
Fix desugaring of context bounds in extensions
Browse files Browse the repository at this point in the history
Fixes scala#11586
Unfixes scala#11583
  • Loading branch information
rjolly committed Mar 8, 2021
1 parent 5e0100d commit d5fbf96
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 89 deletions.
113 changes: 58 additions & 55 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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, false))

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, true))

private def elimContextBounds(extParamss: List[ParamClause], meth: DefDef, isPrimaryConstructor: Boolean, ext: 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, ext) ++
elimContextBounds(paramss, isPrimaryConstructor, ext)
)
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
Expand All @@ -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 =
Expand Down Expand Up @@ -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] =
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions tests/neg/i10901.scala
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ object BugExp4Point2D {
val y = DoubleT

val pos1: Point2D[Int,Double] = x º y // error
val pos2: Point2D[Int,Double] = 100 º 200.1 // ok
val pos3: Point2D[Int,Double] = 101 º y // ok
val pos2: Point2D[Int,Double] = 100 º 200.1 // error
val pos3: Point2D[Int,Double] = 101 º y // error
val pos4: Point2D[Int,Double] = x º 201.1 // error

}
Expand Down
14 changes: 14 additions & 0 deletions tests/neg/i11358.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
object Test:

def test1: IArray[Int] = IArray(1, 2) +++ IArray(2, 3) // error
def test2: IArray[Int] = IArray(1, 2) +++ List(2, 3) // error
def test3 = +++[Int](IArray(1, 2))(IArray(2, 3))
def test4 = +++[Int](IArray(1, 2))(List(2, 3))
def test5: IArray[Int] = IArray(1, 2).+++[Int](IArray(2, 3)) // error
def test6: IArray[Int] = IArray(1, 2).+++[Int](List(2, 3)) // error
def test7 = +++(IArray(1, 2))[Int](IArray(2, 3)) // error
def test8 = +++(IArray(1, 2))[Int](List(2, 3)) // error

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] = ???
11 changes: 3 additions & 8 deletions tests/neg/missing-implicit1.check
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,7 @@
|
| import testObjectInstance.instances.traverseList
|
-- Error: tests/neg/missing-implicit1.scala:23:42 ----------------------------------------------------------------------
-- [E008] Not Found Error: tests/neg/missing-implicit1.scala:23:18 -----------------------------------------------------
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
|
|The following import might fix the problem:
|
| import testObjectInstance.instances.zipOption
|
| ^^^^^^^^^^^^^^^^^^^^^^
| value traverse is not a member of List[Int] - did you mean List[Int].reverse?
11 changes: 3 additions & 8 deletions tests/neg/missing-implicit4.check
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,7 @@
|
| import instances.traverseList
|
-- Error: tests/neg/missing-implicit4.scala:20:42 ----------------------------------------------------------------------
-- [E008] Not Found Error: tests/neg/missing-implicit4.scala:20:18 -----------------------------------------------------
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
|
|The following import might fix the problem:
|
| import instances.zipOption
|
| ^^^^^^^^^^^^^^^^^^^^^^
| value traverse is not a member of List[Int] - did you mean List[Int].reverse?
14 changes: 0 additions & 14 deletions tests/pos/i11358.scala

This file was deleted.

2 changes: 0 additions & 2 deletions tests/pos/i11583.scala

This file was deleted.

10 changes: 10 additions & 0 deletions tests/pos/i11586.scala
Original file line number Diff line number Diff line change
@@ -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)

0 comments on commit d5fbf96

Please sign in to comment.