Skip to content

Commit 7723864

Browse files
authored
Merge pull request #9255 from dotty-staging/change-extension
Unified extension methods
2 parents 2dd1c93 + e160140 commit 7723864

File tree

184 files changed

+1366
-1159
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

184 files changed

+1366
-1159
lines changed

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

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,7 @@ object desugar {
559559
val copiedAccessFlags = if migrateTo3 then EmptyFlags else AccessFlags
560560

561561
// Methods to add to a case class C[..](p1: T1, ..., pN: Tn)(moreParams)
562-
// def _1: T1 = this.p1
562+
// def _1: T1 = this.p1
563563
// ...
564564
// def _N: TN = this.pN (unless already given as valdef or parameterless defdef)
565565
// def copy(p1: T1 = p1: @uncheckedVariance, ...,
@@ -572,7 +572,7 @@ object desugar {
572572
val caseClassMeths = {
573573
def syntheticProperty(name: TermName, tpt: Tree, rhs: Tree) =
574574
DefDef(name, Nil, Nil, tpt, rhs).withMods(synthetic)
575-
575+
576576
def productElemMeths =
577577
val caseParams = derivedVparamss.head.toArray
578578
val selectorNamesInBody = normalizedBody.collect {
@@ -850,6 +850,7 @@ object desugar {
850850
* where every definition in `body` is expanded to an extension method
851851
* taking type parameters `tparams` and a leading paramter `(x: T)`.
852852
* See: collectiveExtensionBody
853+
* TODO: drop this part
853854
*/
854855
def moduleDef(mdef: ModuleDef)(implicit ctx: Context): Tree = {
855856
val impl = mdef.impl
@@ -906,6 +907,25 @@ object desugar {
906907
}
907908
}
908909

910+
/** Transform extension construct to list of extension methods */
911+
def extMethods(ext: ExtMethods)(using Context): Tree = flatTree {
912+
for mdef <- ext.methods yield
913+
if mdef.tparams.nonEmpty then
914+
ctx.error("extension method cannot have type parameters here, all type parameters go after `extension`",
915+
mdef.tparams.head.sourcePos)
916+
defDef(
917+
cpy.DefDef(mdef)(
918+
name = mdef.name.toExtensionName,
919+
tparams = ext.tparams ++ mdef.tparams,
920+
vparamss = mdef.vparamss match
921+
case vparams1 :: vparamss1 if !isLeftAssoc(mdef.name) =>
922+
vparams1 :: ext.vparamss ::: vparamss1
923+
case _ =>
924+
ext.vparamss ++ mdef.vparamss
925+
).withMods(mdef.mods | Extension)
926+
)
927+
}
928+
909929
/** Transform the statements of a collective extension
910930
* @param stats the original statements as they were parsed
911931
* @param tparams the collective type parameters
@@ -934,6 +954,7 @@ object desugar {
934954
stat match
935955
case mdef: DefDef =>
936956
cpy.DefDef(mdef)(
957+
name = mdef.name.toExtensionName,
937958
tparams = tparams ++ mdef.tparams,
938959
vparamss = vparamss ::: mdef.vparamss,
939960
).withMods(mdef.mods | Extension)
@@ -964,16 +985,21 @@ object desugar {
964985
/** The normalized name of `mdef`. This means
965986
* 1. Check that the name does not redefine a Scala core class.
966987
* If it does redefine, issue an error and return a mangled name instead of the original one.
967-
* 2. If the name is missing (this can be the case for instance definitions), invent one instead.
988+
* 2. Check that the name does not start with `extension_` unless the
989+
* method is an extension method.
990+
* 3. If the name is missing (this can be the case for instance definitions), invent one instead.
968991
*/
969992
def normalizeName(mdef: MemberDef, impl: Tree)(implicit ctx: Context): Name = {
970993
var name = mdef.name
971994
if (name.isEmpty) name = name.likeSpaced(inventGivenOrExtensionName(impl))
995+
def errPos = mdef.source.atSpan(mdef.nameSpan)
972996
if (ctx.owner == defn.ScalaPackageClass && defn.reservedScalaClassNames.contains(name.toTypeName)) {
973997
val kind = if (name.isTypeName) "class" else "object"
974-
ctx.error(IllegalRedefinitionOfStandardKind(kind, name), mdef.sourcePos)
998+
ctx.error(IllegalRedefinitionOfStandardKind(kind, name), errPos)
975999
name = name.errorName
9761000
}
1001+
if name.isExtensionName && !mdef.mods.is(Extension) then
1002+
ctx.error(em"illegal method name: $name may not start with `extension_`", errPos)
9771003
name
9781004
}
9791005

@@ -1247,7 +1273,7 @@ object desugar {
12471273
}
12481274

12491275
private def isTopLevelDef(stat: Tree)(using Context): Boolean = stat match
1250-
case _: ValDef | _: PatDef | _: DefDef | _: Export => true
1276+
case _: ValDef | _: PatDef | _: DefDef | _: Export | _: ExtMethods => true
12511277
case stat: ModuleDef =>
12521278
stat.mods.isOneOf(GivenOrImplicit)
12531279
case stat: TypeDef =>

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
114114
case class ContextBounds(bounds: TypeBoundsTree, cxBounds: List[Tree])(implicit @constructorOnly src: SourceFile) extends TypTree
115115
case class PatDef(mods: Modifiers, pats: List[Tree], tpt: Tree, rhs: Tree)(implicit @constructorOnly src: SourceFile) extends DefTree
116116
case class Export(expr: Tree, selectors: List[ImportSelector])(implicit @constructorOnly src: SourceFile) extends Tree
117+
case class ExtMethods(tparams: List[TypeDef], vparamss: List[List[ValDef]], methods: List[DefDef])(implicit @constructorOnly src: SourceFile) extends Tree
117118
case class MacroTree(expr: Tree)(implicit @constructorOnly src: SourceFile) extends Tree
118119

119120
case class ImportSelector(imported: Ident, renamed: Tree = EmptyTree, bound: Tree = EmptyTree)(implicit @constructorOnly src: SourceFile) extends Tree {
@@ -617,6 +618,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
617618
case tree: Export if (expr eq tree.expr) && (selectors eq tree.selectors) => tree
618619
case _ => finalize(tree, untpd.Export(expr, selectors)(tree.source))
619620
}
621+
def ExtMethods(tree: Tree)(tparams: List[TypeDef], vparamss: List[List[ValDef]], methods: List[DefDef])(using Context): Tree = tree match
622+
case tree: ExtMethods if (tparams eq tree.tparams) && (vparamss eq tree.vparamss) && (methods == tree.methods) => tree
623+
case _ => finalize(tree, untpd.ExtMethods(tparams, vparamss, methods)(tree.source))
620624
def ImportSelector(tree: Tree)(imported: Ident, renamed: Tree, bound: Tree)(implicit ctx: Context): Tree = tree match {
621625
case tree: ImportSelector if (imported eq tree.imported) && (renamed eq tree.renamed) && (bound eq tree.bound) => tree
622626
case _ => finalize(tree, untpd.ImportSelector(imported, renamed, bound)(tree.source))
@@ -683,6 +687,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
683687
cpy.PatDef(tree)(mods, transform(pats), transform(tpt), transform(rhs))
684688
case Export(expr, selectors) =>
685689
cpy.Export(tree)(transform(expr), selectors)
690+
case ExtMethods(tparams, vparamss, methods) =>
691+
cpy.ExtMethods(tree)(transformSub(tparams), vparamss.mapConserve(transformSub(_)), transformSub(methods))
686692
case ImportSelector(imported, renamed, bound) =>
687693
cpy.ImportSelector(tree)(transformSub(imported), transform(renamed), transform(bound))
688694
case Number(_, _) | TypedSplice(_) =>
@@ -742,6 +748,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
742748
this(this(this(x, pats), tpt), rhs)
743749
case Export(expr, _) =>
744750
this(x, expr)
751+
case ExtMethods(tparams, vparamss, methods) =>
752+
this(vparamss.foldLeft(this(x, tparams))(apply), methods)
745753
case ImportSelector(imported, renamed, bound) =>
746754
this(this(this(x, imported), renamed), bound)
747755
case Number(_, _) =>

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,21 @@ object NameOps {
131131
else name.toTermName
132132
}
133133

134+
/** Does this name start with `extension_`? */
135+
def isExtensionName: Boolean = name match
136+
case name: SimpleName => name.startsWith("extension_")
137+
case _ => false
138+
139+
/** Add an `extension_` in front of this name */
140+
def toExtensionName = termName("extension_" ++ name.toString)
141+
142+
/** Drop `extension_` in front of this name, if it has this prefix */
143+
def dropExtension = name match
144+
case name: SimpleName if name.startsWith("extension_") =>
145+
name.drop("extension_".length)
146+
case _ =>
147+
name
148+
134149
/** The expanded name.
135150
* This is the fully qualified name of `base` with `ExpandPrefixName` as separator,
136151
* followed by `kind` and the name.

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,6 +1190,15 @@ object SymDenotations {
11901190
else if (this.exists) owner.enclosingMethod
11911191
else NoSymbol
11921192

1193+
/** The closest enclosing extension method containing this definition,
1194+
* provided the extension method appears in the same class.
1195+
*/
1196+
final def enclosingExtensionMethod(using Context): Symbol =
1197+
if this.isAllOf(ExtensionMethod) then symbol
1198+
else if this.isClass then NoSymbol
1199+
else if this.exists then owner.enclosingExtensionMethod
1200+
else NoSymbol
1201+
11931202
/** The top-level class containing this denotation,
11941203
* except for a toplevel module, where its module class is returned.
11951204
*/

0 commit comments

Comments
 (0)