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 5, 2021
1 parent 5e0100d commit ee7bc0b
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 56 deletions.
111 changes: 57 additions & 54 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,27 @@ 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) ++
elimContextBounds(paramss, isPrimaryConstructor)
)
end elimContextBounds

private def elimContextBounds(paramss: List[ParamClause], isPrimaryConstructor: Boolean)(using Context): List[ParamClause] =
val evidenceParamBuf = ListBuffer[ValDef]()

def desugarContextBounds(rhs: Tree): Tree = rhs match
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
2 changes: 0 additions & 2 deletions tests/pos/i11583.scala

This file was deleted.

12 changes: 12 additions & 0 deletions tests/pos/i11586.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import language.future

type Conv[T] = [X] =>> X => T

trait SemiGroup[T]:
extension [U: Conv[T]](x: U)
def * (y: T): T
extension (x: T)
def * [U: Conv[T]](y: U): T

trait Q[T, R: SemiGroup] extends SemiGroup[T]:
def res(x: R, y: R) = x * y

0 comments on commit ee7bc0b

Please sign in to comment.