Skip to content

Commit ce4cc6a

Browse files
committed
Desugarings for extensions
1 parent 7111bca commit ce4cc6a

File tree

3 files changed

+94
-17
lines changed

3 files changed

+94
-17
lines changed

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

Lines changed: 91 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import language.higherKinds
1111
import typer.FrontEnd
1212
import collection.mutable.ListBuffer
1313
import util.Property
14+
import config.Printers.desugr
1415
import reporting.diagnostic.messages._
1516
import reporting.trace
1617

@@ -154,6 +155,25 @@ object desugar {
154155
ValDef(epname, tpt, EmptyTree).withFlags(paramFlags | Implicit)
155156
}
156157

158+
private def desugarTypeBindings(
159+
bindings: List[TypeDef],
160+
forPrimaryConstructor: Boolean = false)(implicit ctx: Context): (List[TypeDef], List[ValDef]) = {
161+
val epbuf = new ListBuffer[ValDef]
162+
def desugarContextBounds(rhs: Tree): Tree = rhs match {
163+
case ContextBounds(tbounds, cxbounds) =>
164+
epbuf ++= makeImplicitParameters(cxbounds, forPrimaryConstructor)
165+
tbounds
166+
case LambdaTypeTree(tparams, body) =>
167+
cpy.LambdaTypeTree(rhs)(tparams, desugarContextBounds(body))
168+
case _ =>
169+
rhs
170+
}
171+
val bindings1 = bindings mapConserve { tparam =>
172+
cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs))
173+
}
174+
(bindings1, epbuf.toList)
175+
}
176+
157177
/** Expand context bounds to evidence params. E.g.,
158178
*
159179
* def f[T >: L <: H : B](params)
@@ -171,21 +191,8 @@ object desugar {
171191
private def defDef(meth: DefDef, isPrimaryConstructor: Boolean = false)(implicit ctx: Context): Tree = {
172192
val DefDef(name, tparams, vparamss, tpt, rhs) = meth
173193
val mods = meth.mods
174-
val epbuf = new ListBuffer[ValDef]
175-
def desugarContextBounds(rhs: Tree): Tree = rhs match {
176-
case ContextBounds(tbounds, cxbounds) =>
177-
epbuf ++= makeImplicitParameters(cxbounds, isPrimaryConstructor)
178-
tbounds
179-
case LambdaTypeTree(tparams, body) =>
180-
cpy.LambdaTypeTree(rhs)(tparams, desugarContextBounds(body))
181-
case _ =>
182-
rhs
183-
}
184-
val tparams1 = tparams mapConserve { tparam =>
185-
cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs))
186-
}
187-
188-
val meth1 = addEvidenceParams(cpy.DefDef(meth)(tparams = tparams1), epbuf.toList)
194+
val (tparams1, evidenceParams) = desugarTypeBindings(tparams, isPrimaryConstructor)
195+
val meth1 = addEvidenceParams(cpy.DefDef(meth)(tparams = tparams1), evidenceParams)
189196

190197
/** The longest prefix of parameter lists in vparamss whose total length does not exceed `n` */
191198
def takeUpTo(vparamss: List[List[ValDef]], n: Int): List[List[ValDef]] = vparamss match {
@@ -293,6 +300,7 @@ object desugar {
293300
def isAnyVal(tree: Tree): Boolean = tree match {
294301
case Ident(tpnme.AnyVal) => true
295302
case Select(qual, tpnme.AnyVal) => isScala(qual)
303+
case TypedSplice(tree) => tree.tpe.isRef(defn.AnyValClass)
296304
case _ => false
297305
}
298306
def isScala(tree: Tree): Boolean = tree match {
@@ -749,6 +757,73 @@ object desugar {
749757
Bind(name, Ident(nme.WILDCARD)).withPos(tree.pos)
750758
}
751759

760+
/** extension id <type-params> <implicit-params> for <extended> : <parents> { <body> }
761+
* ->
762+
* implicit class <id> <type-params> ($this: <extended>) <combined-params>
763+
* extends <parents> {
764+
* import $this._
765+
* <body1>
766+
* }
767+
*
768+
* where
769+
*
770+
* (<type-params>, <evidence-params>) = desugarTypeBindings(<type-params0>)
771+
* <combined-params> = <implicit-params> concatenated with <evidence-params> in one clause
772+
* <body1> = <body> with each occurrence of unqualified `this` substituted by `$this`.
773+
*
774+
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
775+
*
776+
* extension <id> <type-params> <implicit-params> for <extended> { <defs> }
777+
* ->
778+
* implicit class <id> <type-params> (private val $this: <extended>)
779+
* extends AnyVal {
780+
* import $this._
781+
* <body2>
782+
* }
783+
*
784+
* where
785+
*
786+
* <body2> = <body1> where each method definition gets <combined-params> as last parameter section.
787+
*/
788+
def extension(tree: Extension)(implicit ctx: Context): Tree = {
789+
val Extension(name, extended, impl) = tree
790+
val isSimpleExtension = impl.parents.isEmpty
791+
792+
val firstParams = ValDef(nme.SELF, extended, EmptyTree).withFlags(Private | Local | ParamAccessor) :: Nil
793+
val body1 = substThis.transform(impl.body)
794+
val impl1 =
795+
if (isSimpleExtension) {
796+
val (typeParams, evidenceParams) =
797+
desugarTypeBindings(impl.constr.tparams, forPrimaryConstructor = false)
798+
cpy.Template(impl)(
799+
constr = cpy.DefDef(impl.constr)(tparams = typeParams, vparamss = firstParams :: Nil),
800+
parents = ref(defn.AnyValType) :: Nil,
801+
body = body1.map {
802+
case ddef: DefDef =>
803+
def resetFlags(vdef: ValDef) =
804+
vdef.withMods(vdef.mods &~ PrivateLocalParamAccessor | Param)
805+
val originalParams = impl.constr.vparamss.headOption.getOrElse(Nil).map(resetFlags)
806+
addEvidenceParams(addEvidenceParams(ddef, originalParams), evidenceParams)
807+
case other =>
808+
other
809+
})
810+
}
811+
else
812+
cpy.Template(impl)(
813+
constr = cpy.DefDef(impl.constr)(vparamss = firstParams :: impl.constr.vparamss),
814+
body = body1)
815+
val icls = TypeDef(name, impl1).withMods(tree.mods.withAddedMod(Mod.Extension()) | Implicit)
816+
desugr.println(i"desugar $extended --> $icls")
817+
classDef(icls)
818+
}
819+
820+
private val substThis = new UntypedTreeMap {
821+
override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
822+
case This(Ident(tpnme.EMPTY)) => Ident(nme.SELF).withPos(tree.pos)
823+
case _ => super.transform(tree)
824+
}
825+
}
826+
752827
def defTree(tree: Tree)(implicit ctx: Context): Tree = tree match {
753828
case tree: ValDef => valDef(tree)
754829
case tree: TypeDef => if (tree.isClassDef) classDef(tree) else tree
@@ -757,6 +832,7 @@ object desugar {
757832
else defDef(tree)
758833
case tree: ModuleDef => moduleDef(tree)
759834
case tree: PatDef => patDef(tree)
835+
case tree: Extension => extension(tree)
760836
}
761837

762838
/** { stats; <empty > }

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
4141
}
4242

4343
/** extend extended impl */
44-
case class Extension(name: TypeName, extended: Tree, impl: Template) extends DefTree
44+
case class Extension(name: TypeName, extended: Tree, impl: Template) extends MemberDef
4545

4646
case class ParsedTry(expr: Tree, handler: Tree, finalizer: Tree) extends TermTree
4747

@@ -143,7 +143,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
143143

144144
case class EnumCase() extends Mod(Flags.EmptyFlags)
145145

146-
case class InstanceDecl() extends Mod(Flags.EmptyFlags)
146+
case class Extension() extends Mod(Flags.EmptyFlags)
147147
}
148148

149149
/** Modifiers and annotations for definitions

compiler/src/dotty/tools/dotc/config/Printers.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ object Printers {
1717
val checks: Printer = noPrinter
1818
val config: Printer = noPrinter
1919
val cyclicErrors: Printer = noPrinter
20+
val desugr: Printer = new Printer
2021
val dottydoc: Printer = noPrinter
2122
val exhaustivity: Printer = noPrinter
2223
val gadts: Printer = noPrinter

0 commit comments

Comments
 (0)