Skip to content

Commit 945d2a8

Browse files
committed
Add augment clauses
The syntax is still open to discussion.
1 parent 56cf8f9 commit 945d2a8

File tree

10 files changed

+276
-36
lines changed

10 files changed

+276
-36
lines changed

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

Lines changed: 57 additions & 0 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

@@ -749,6 +750,61 @@ object desugar {
749750
Bind(name, Ident(nme.WILDCARD)).withPos(tree.pos)
750751
}
751752

753+
/** augment <name> <type-params> <params> extends <parents> { <body>} }
754+
* ->
755+
* implicit class <deconame> <type-params> ($this: name <type-args>) <params>
756+
* extends <parents> { <body1> }
757+
*
758+
* augment <type-param> <params> extends <parents> { <body>} }
759+
* ->
760+
* implicit class <deconame> <type-param> ($this: <type-arg>) <params>
761+
* extends <parents> { <body1> }
762+
*
763+
* where
764+
*
765+
* <deconame> = <name>To<parent>$<n> where <parent> is first extended class name
766+
* = <name>Augmentation$<n> if no such <parent> exists
767+
* <n> = counter making prefix unique
768+
* <type-args> = references to <type-params>
769+
* <body1> = <body> with each occurrence of unqualified `this` substituted by `$this`.
770+
*/
771+
def augmentation(tree: Augment)(implicit ctx: Context): Tree = {
772+
val Augment(name, impl) = tree
773+
val constr @ DefDef(_, tparams, vparamss, _, _) = impl.constr
774+
var decorated: Tree = Ident(name)
775+
if (tparams.nonEmpty)
776+
decorated =
777+
if (name.isEmpty) refOfDef(tparams.head)
778+
else AppliedTypeTree(decorated, tparams.map(refOfDef))
779+
val firstParam = ValDef(nme.SELF, decorated, EmptyTree).withFlags(Private | Local | ParamAccessor)
780+
val constr1 = cpy.DefDef(constr)(vparamss = (firstParam :: Nil) :: vparamss)
781+
val substThis = new UntypedTreeMap {
782+
override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
783+
case This(Ident(tpnme.EMPTY)) => Ident(nme.SELF).withPos(tree.pos)
784+
case _ => super.transform(tree)
785+
}
786+
}
787+
def targetSuffix(tree: Tree): String = tree match {
788+
case Apply(tycon, args) => targetSuffix(tycon)
789+
case TypeApply(tycon, args) => targetSuffix(tycon)
790+
case Select(pre, nme.CONSTRUCTOR) => targetSuffix(pre)
791+
case New(tpt) => targetSuffix(tpt)
792+
case AppliedTypeTree(tycon, _) => targetSuffix(tycon)
793+
case tree: RefTree => "To" ++ tree.name.toString
794+
case _ => str.Augmentation
795+
}
796+
val decoName: TypeName = impl.parents match {
797+
case parent :: _ => name ++ targetSuffix(parent)
798+
case _ => name ++ str.Augmentation
799+
}
800+
val icls =
801+
TypeDef(UniqueName.fresh(decoName.toTermName).toTypeName,
802+
cpy.Template(impl)(constr = constr1, body = substThis.transform(impl.body)))
803+
.withFlags(Implicit)
804+
desugr.println(i"desugar $name --> $icls")
805+
classDef(icls)
806+
}
807+
752808
def defTree(tree: Tree)(implicit ctx: Context): Tree = tree match {
753809
case tree: ValDef => valDef(tree)
754810
case tree: TypeDef => if (tree.isClassDef) classDef(tree) else tree
@@ -757,6 +813,7 @@ object desugar {
757813
else defDef(tree)
758814
case tree: ModuleDef => moduleDef(tree)
759815
case tree: PatDef => patDef(tree)
816+
case tree: Augment => augmentation(tree)
760817
}
761818

762819
/** { stats; <empty > }

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
4040
def withName(name: Name)(implicit ctx: Context) = cpy.ModuleDef(this)(name.toTermName, impl)
4141
}
4242

43+
/** augment name impl */
44+
case class Augment(name: TypeName, impl: Template) extends DefTree
45+
4346
case class ParsedTry(expr: Tree, handler: Tree, finalizer: Tree) extends TermTree
4447

4548
case class SymbolLit(str: String) extends TermTree
@@ -411,6 +414,10 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
411414
case tree: ModuleDef if (name eq tree.name) && (impl eq tree.impl) => tree
412415
case _ => finalize(tree, untpd.ModuleDef(name, impl))
413416
}
417+
def Augment(tree: Tree)(name: TypeName, impl: Template) = tree match {
418+
case tree: Augment if (name eq tree.name) && (impl eq tree.impl) => tree
419+
case _ => finalize(tree, untpd.Augment(name, impl))
420+
}
414421
def ParsedTry(tree: Tree)(expr: Tree, handler: Tree, finalizer: Tree) = tree match {
415422
case tree: ParsedTry
416423
if (expr eq tree.expr) && (handler eq tree.handler) && (finalizer eq tree.finalizer) => tree

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 = noPrinter
2021
val dottydoc: Printer = noPrinter
2122
val exhaustivity: Printer = noPrinter
2223
val gadts: Printer = noPrinter

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ object StdNames {
4242
final val AbstractFunction = "AbstractFunction"
4343
final val Tuple = "Tuple"
4444
final val Product = "Product"
45+
final val Augmentation = "Augmentation"
4546

4647
def sanitize(str: String) = str.replaceAll("""[<>]""", """\$""")
4748
}
@@ -256,17 +257,6 @@ object StdNames {
256257
val FAKE_LOCAL_THIS: N = "this$"
257258
val LAZY_FIELD_OFFSET: N = "OFFSET$"
258259
val LAZY_SLOW_SUFFIX: N = "$lzycompute"
259-
val UNIVERSE_BUILD_PREFIX: N = "$u.build."
260-
val UNIVERSE_BUILD: N = "$u.build"
261-
val UNIVERSE_PREFIX: N = "$u."
262-
val UNIVERSE_SHORT: N = "$u"
263-
val MIRROR_PREFIX: N = "$m."
264-
val MIRROR_SHORT: N = "$m"
265-
val MIRROR_UNTYPED: N = "$m$untyped"
266-
val REIFY_FREE_PREFIX: N = "free$"
267-
val REIFY_FREE_THIS_SUFFIX: N = "$this"
268-
val REIFY_FREE_VALUE_SUFFIX: N = "$value"
269-
val REIFY_SYMDEF_PREFIX: N = "symdef$"
270260
val OUTER: N = "$outer"
271261
val REFINE_CLASS: N = "<refinement>"
272262
val ROOTPKG: N = "_root_"

compiler/src/dotty/tools/dotc/parsing/Parsers.scala

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1831,8 +1831,10 @@ object Parsers {
18311831
* DefParams ::= DefParam {`,' DefParam}
18321832
* DefParam ::= {Annotation} [`inline'] Param
18331833
* Param ::= id `:' ParamType [`=' Expr]
1834+
* ImplicitParamClause ::= [nl] ‘(’ ImplicitMods ClsParams ‘)’)
1835+
* ImplicitMods ::= `implicit` [`unused`] | `unused` `implicit`
18341836
*/
1835-
def paramClauses(owner: Name, ofCaseClass: Boolean = false): List[List[ValDef]] = {
1837+
def paramClauses(owner: Name, ofCaseClass: Boolean = false, ofAugmentation: Boolean = false): List[List[ValDef]] = {
18361838
var imods: Modifiers = EmptyModifiers
18371839
var implicitOffset = -1 // use once
18381840
var firstClauseOfCaseClass = ofCaseClass
@@ -1878,7 +1880,7 @@ object Parsers {
18781880
}
18791881
}
18801882
def paramClause(): List[ValDef] = inParens {
1881-
if (in.token == RPAREN) Nil
1883+
if (!ofAugmentation && in.token == RPAREN) Nil
18821884
else {
18831885
def funArgMods(): Unit = {
18841886
if (in.token == IMPLICIT) {
@@ -1891,7 +1893,8 @@ object Parsers {
18911893
}
18921894
}
18931895
funArgMods()
1894-
1896+
if (ofAugmentation && !imods.is(Implicit))
1897+
syntaxError(i"parameters of augment clause must be implicit")
18951898
commaSeparated(() => param())
18961899
}
18971900
}
@@ -1901,7 +1904,7 @@ object Parsers {
19011904
imods = EmptyModifiers
19021905
paramClause() :: {
19031906
firstClauseOfCaseClass = false
1904-
if (imods is Implicit) Nil else clauses()
1907+
if (imods.is(Implicit) || ofAugmentation) Nil else clauses()
19051908
}
19061909
} else Nil
19071910
}
@@ -2180,15 +2183,15 @@ object Parsers {
21802183
}
21812184
}
21822185

2183-
/** ClassDef ::= id ClassConstr TemplateOpt
2186+
/** ClassDef ::= id ClassConstr [TemplateClause]
21842187
*/
21852188
def classDef(start: Offset, mods: Modifiers): TypeDef = atPos(start, nameStart) {
21862189
classDefRest(start, mods, ident().toTypeName)
21872190
}
21882191

21892192
def classDefRest(start: Offset, mods: Modifiers, name: TypeName): TypeDef = {
21902193
val constr = classConstr(name, isCaseClass = mods is Case)
2191-
val templ = templateOpt(constr)
2194+
val templ = templateClauseOpt(constr)
21922195
TypeDef(name, templ).withMods(mods).setComment(in.getDocComment(start))
21932196
}
21942197

@@ -2197,7 +2200,7 @@ object Parsers {
21972200
def classConstr(owner: Name, isCaseClass: Boolean = false): DefDef = atPos(in.lastOffset) {
21982201
val tparams = typeParamClauseOpt(ParamOwner.Class)
21992202
val cmods = fromWithinClassConstr(constrModsOpt(owner))
2200-
val vparamss = paramClauses(owner, isCaseClass)
2203+
val vparamss = paramClauses(owner, ofCaseClass = isCaseClass)
22012204
makeConstructor(tparams, vparamss).withMods(cmods)
22022205
}
22032206

@@ -2206,14 +2209,14 @@ object Parsers {
22062209
def constrModsOpt(owner: Name): Modifiers =
22072210
modifiers(accessModifierTokens, annotsAsMods())
22082211

2209-
/** ObjectDef ::= id TemplateOpt
2212+
/** ObjectDef ::= id [TemplateClause]
22102213
*/
22112214
def objectDef(start: Offset, mods: Modifiers): ModuleDef = atPos(start, nameStart) {
22122215
objectDefRest(start, mods, ident())
22132216
}
22142217

22152218
def objectDefRest(start: Offset, mods: Modifiers, name: TermName): ModuleDef = {
2216-
val template = templateOpt(emptyConstructor)
2219+
val template = templateClauseOpt(emptyConstructor)
22172220
ModuleDef(name, template).withMods(mods).setComment(in.getDocComment(start))
22182221
}
22192222

@@ -2223,7 +2226,7 @@ object Parsers {
22232226
val modName = ident()
22242227
val clsName = modName.toTypeName
22252228
val constr = classConstr(clsName)
2226-
val impl = templateOpt(constr, isEnum = true)
2229+
val impl = templateClauseOpt(constr, isEnum = true, bodyRequired = true)
22272230
TypeDef(clsName, impl).withMods(addMod(mods, enumMod)).setComment(in.getDocComment(start))
22282231
}
22292232

@@ -2269,6 +2272,19 @@ object Parsers {
22692272
Template(constr, parents, EmptyValDef, Nil)
22702273
}
22712274

2275+
/** Augmentation ::= ‘augment’ (id | [id] ClassTypeParamClause)
2276+
* [[nl] ImplicitParamClause] TemplateClause
2277+
*/
2278+
def augmentation(): Augment = atPos(in.skipToken(), nameStart) {
2279+
val (name, tparams) =
2280+
if (isIdent) (ident().toTypeName, typeParamClauseOpt(ParamOwner.Class))
2281+
else (tpnme.EMPTY, typeParamClause(ParamOwner.Class))
2282+
val vparamss = paramClauses(name, ofAugmentation = true)
2283+
val constr = makeConstructor(tparams, vparamss)
2284+
val templ = templateClauseOpt(constr, bodyRequired = true)
2285+
Augment(name, templ)
2286+
}
2287+
22722288
/* -------- TEMPLATES ------------------------------------------- */
22732289

22742290
/** ConstrApp ::= SimpleType {ParArgumentExprs}
@@ -2285,26 +2301,27 @@ object Parsers {
22852301
* @return a pair consisting of the template, and a boolean which indicates
22862302
* whether the template misses a body (i.e. no {...} part).
22872303
*/
2288-
def template(constr: DefDef, isEnum: Boolean = false): (Template, Boolean) = {
2304+
def template(constr: DefDef, isEnum: Boolean = false, bodyRequired: Boolean = false): (Template, Boolean) = {
22892305
newLineOptWhenFollowedBy(LBRACE)
22902306
if (in.token == LBRACE) (templateBodyOpt(constr, Nil, isEnum), false)
22912307
else {
22922308
val parents = tokenSeparated(WITH, constrApp)
22932309
newLineOptWhenFollowedBy(LBRACE)
2294-
if (isEnum && in.token != LBRACE)
2310+
if (bodyRequired && in.token != LBRACE)
22952311
syntaxErrorOrIncomplete(ExpectedTokenButFound(LBRACE, in.token))
22962312
val missingBody = in.token != LBRACE
22972313
(templateBodyOpt(constr, parents, isEnum), missingBody)
22982314
}
22992315
}
23002316

2301-
/** TemplateOpt = [`extends' Template | TemplateBody]
2317+
/** TemplateClause = `extends' Template | TemplateBody
2318+
* TemplateClauseOpt = [TemplateClause]
23022319
*/
2303-
def templateOpt(constr: DefDef, isEnum: Boolean = false): Template =
2304-
if (in.token == EXTENDS) { in.nextToken(); template(constr, isEnum)._1 }
2320+
def templateClauseOpt(constr: DefDef, isEnum: Boolean = false, bodyRequired: Boolean = false): Template =
2321+
if (in.token == EXTENDS) { in.nextToken(); template(constr, isEnum, bodyRequired)._1 }
23052322
else {
23062323
newLineOptWhenFollowedBy(LBRACE)
2307-
if (in.token == LBRACE) template(constr, isEnum)._1
2324+
if (in.token == LBRACE || bodyRequired) template(constr, isEnum, bodyRequired)._1
23082325
else Template(constr, Nil, EmptyValDef, Nil)
23092326
}
23102327

@@ -2380,6 +2397,7 @@ object Parsers {
23802397
* TemplateStat ::= Import
23812398
* | Annotations Modifiers Def
23822399
* | Annotations Modifiers Dcl
2400+
* | Augmentation
23832401
* | Expr1
23842402
* |
23852403
* EnumStat ::= TemplateStat
@@ -2410,6 +2428,8 @@ object Parsers {
24102428
setLastStatOffset()
24112429
if (in.token == IMPORT)
24122430
stats ++= importClause()
2431+
else if (in.token == AUGMENT)
2432+
stats += augmentation()
24132433
else if (isExprIntro)
24142434
stats += expr1()
24152435
else if (isDefIntro(modifierTokensOrCase))
@@ -2454,6 +2474,7 @@ object Parsers {
24542474
* BlockStat ::= Import
24552475
* | Annotations [implicit] [lazy] Def
24562476
* | Annotations LocalModifiers TmplDef
2477+
* | Augmentation
24572478
* | Expr1
24582479
* |
24592480
*/
@@ -2464,6 +2485,8 @@ object Parsers {
24642485
setLastStatOffset()
24652486
if (in.token == IMPORT)
24662487
stats ++= importClause()
2488+
else if (in.token == AUGMENT)
2489+
stats += augmentation()
24672490
else if (isExprIntro)
24682491
stats += expr(Location.InBlock)
24692492
else if (isDefIntro(localModifierTokens))

compiler/src/dotty/tools/dotc/parsing/Tokens.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,7 @@ object Tokens extends TokensCommon {
179179
final val ENUM = 63; enter(ENUM, "enum")
180180
final val UNUSED = 64; enter(UNUSED, "unused")
181181
final val OPAQUE = 65; enter(OPAQUE, "opaque")
182+
final val AUGMENT = 66; enter(AUGMENT, "augment")
182183

183184
/** special symbols */
184185
final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line")
@@ -199,7 +200,7 @@ object Tokens extends TokensCommon {
199200
/** XML mode */
200201
final val XMLSTART = 96; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate
201202

202-
final val alphaKeywords = tokenRange(IF, OPAQUE)
203+
final val alphaKeywords = tokenRange(IF, AUGMENT)
203204
final val symbolicKeywords = tokenRange(USCORE, VIEWBOUND)
204205
final val symbolicTokens = tokenRange(COMMA, VIEWBOUND)
205206
final val keywords = alphaKeywords | symbolicKeywords
@@ -239,7 +240,7 @@ object Tokens extends TokensCommon {
239240

240241
/** Is token only legal as start of statement (eof also included)? */
241242
final val mustStartStatTokens = defIntroTokens | modifierTokens | BitSet(
242-
IMPORT, PACKAGE)
243+
IMPORT, PACKAGE, AUGMENT)
243244

244245
final val canStartStatTokens = canStartExpressionTokens | mustStartStatTokens | BitSet(
245246
AT, CASE)

0 commit comments

Comments
 (0)