Skip to content

Add augment clauses #4043

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 34 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
876ba16
Add opaque types: parsing & pickling
odersky Feb 21, 2018
49d59d1
Store opaque info in annotation
odersky Feb 21, 2018
a9ae016
Keep track of opaque companion links
odersky Feb 21, 2018
d8faef8
Maintain companion aliases as GADT bounds
odersky Feb 21, 2018
4643971
Test cases
odersky Feb 21, 2018
36d5373
Allow for higher-kinded opaque types
odersky Feb 21, 2018
08d3b3c
Handle higher-kinded comparisons involving GADTs
odersky Feb 21, 2018
82d9261
Change companion detection scheme
odersky Feb 22, 2018
35ded60
Eliminate boxing for opaque types
odersky Feb 22, 2018
1e9f806
Make OpaqueAlias and LinkedType special classes
odersky Feb 22, 2018
52d0a37
Make implicit scope include companion objects of opaque types
odersky Feb 22, 2018
1a522f3
Add reference documentation
odersky Feb 22, 2018
9333219
Fix test
odersky Feb 22, 2018
929e4a3
Fix indentation
odersky Feb 22, 2018
d191092
Treat companions of opaque types specially
odersky Feb 23, 2018
30c39db
Replace annotation extractor with access methods
odersky Feb 23, 2018
d4e079c
Simplify companion scheme
odersky Feb 24, 2018
827534a
add test
odersky Feb 24, 2018
33cd1ef
Add augment clauses
odersky Feb 26, 2018
fb37b7f
Allow for `type id` in type patterns
odersky Feb 27, 2018
1c13524
Convert tests to use new type pattern syntax
odersky Feb 27, 2018
7cc1e37
Base augment syntax on type patterns
odersky Feb 27, 2018
8dd9be5
Fix typo
odersky Feb 27, 2018
f18245d
Make annotation names compilation-order independent
odersky Feb 27, 2018
7ace3d7
Require that non-extending augments only add extension methods
odersky Feb 27, 2018
f338416
Implement extension methods as implicit value classes
odersky Feb 27, 2018
4dd12d3
Partially fix idempotency of static method order
nicolasstucki Feb 27, 2018
f83d333
Allow labels naming an augment
odersky Feb 27, 2018
1d61128
Use semantic names for augmentations
odersky Feb 28, 2018
f37611d
Add regression test for idempotency issue on static methods
nicolasstucki Feb 28, 2018
d7570e2
Add docs for extension methods
odersky Feb 28, 2018
a0e6eb7
More docs for augmentations
odersky Feb 28, 2018
02f9f7c
Remove duplicate test
odersky Mar 1, 2018
8f1f0f0
Fix typos
odersky Mar 7, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 143 additions & 16 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import core._
import util.Positions._, Types._, Contexts._, Constants._, Names._, NameOps._, Flags._
import SymDenotations._, Symbols._, StdNames._, Annotations._, Trees._
import Decorators._, transform.SymUtils._
import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName}
import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName, AugmentName}
import language.higherKinds
import typer.FrontEnd
import collection.mutable.ListBuffer
import util.Property
import config.Printers.desugr
import reporting.diagnostic.messages._
import reporting.trace

Expand Down Expand Up @@ -154,6 +155,25 @@ object desugar {
ValDef(epname, tpt, EmptyTree).withFlags(paramFlags | Implicit)
}

private def desugarTypeBindings(
bindings: List[TypeDef],
forPrimaryConstructor: Boolean = false)(implicit ctx: Context): (List[TypeDef], List[ValDef]) = {
val epbuf = new ListBuffer[ValDef]
def desugarContextBounds(rhs: Tree): Tree = rhs match {
case ContextBounds(tbounds, cxbounds) =>
epbuf ++= makeImplicitParameters(cxbounds, forPrimaryConstructor)
tbounds
case LambdaTypeTree(tparams, body) =>
cpy.LambdaTypeTree(rhs)(tparams, desugarContextBounds(body))
case _ =>
rhs
}
val bindings1 = bindings mapConserve { tparam =>
cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs))
}
(bindings1, epbuf.toList)
}

/** Expand context bounds to evidence params. E.g.,
*
* def f[T >: L <: H : B](params)
Expand All @@ -171,21 +191,8 @@ object desugar {
private def defDef(meth: DefDef, isPrimaryConstructor: Boolean = false)(implicit ctx: Context): Tree = {
val DefDef(name, tparams, vparamss, tpt, rhs) = meth
val mods = meth.mods
val epbuf = new ListBuffer[ValDef]
def desugarContextBounds(rhs: Tree): Tree = rhs match {
case ContextBounds(tbounds, cxbounds) =>
epbuf ++= makeImplicitParameters(cxbounds, isPrimaryConstructor)
tbounds
case LambdaTypeTree(tparams, body) =>
cpy.LambdaTypeTree(rhs)(tparams, desugarContextBounds(body))
case _ =>
rhs
}
val tparams1 = tparams mapConserve { tparam =>
cpy.TypeDef(tparam)(rhs = desugarContextBounds(tparam.rhs))
}

val meth1 = addEvidenceParams(cpy.DefDef(meth)(tparams = tparams1), epbuf.toList)
val (tparams1, evidenceParams) = desugarTypeBindings(tparams, isPrimaryConstructor)
val meth1 = addEvidenceParams(cpy.DefDef(meth)(tparams = tparams1), evidenceParams)

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

def decomposeTypePattern(tree: Tree)(implicit ctx: Context): (Tree, List[TypeDef]) = {
val bindingsBuf = new ListBuffer[TypeDef]
val elimTypeDefs = new untpd.UntypedTreeMap {
override def transform(tree: Tree)(implicit ctx: Context) = tree match {
case tree: TypeDef =>
bindingsBuf += tree
Ident(tree.name).withPos(tree.pos)
case _ =>
super.transform(tree)
}
}
(elimTypeDefs.transform(tree), bindingsBuf.toList)
}

/** augment [<id> @] <type-pattern> <params> extends <parents> { <body>} }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still using brackets unlike the proposed docs which use parens.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, the brackets are EBNF syntax brackets here. Feel free to change to a better notation.

* ->
* implicit class <deconame> <type-params> ($this: <decorated>) <combined-params>
* extends <parents> { <body1> }
*
* where
*
* <deco-name> = <id>, if there is a `<id> @` binding
* = unqiue, expanded name relative to top-level class of <deco-core>, otherwise
* <deco-core> = "_augment_<from>_to_<to>" if <to> is nonempty
* = "_augment_<from>" otherwise
* <from> = underlying type name of <decorated>, or ""
* <to> = underlying type name of first extended parent, or ""
*
* (<decorated>, <type-params0>) = decomposeTypePattern(<type-pattern>)
* (<type-params>, <evidence-params>) = desugarTypeBindings(<type-params0>)
* <combined-params> = <params> concatenated with <evidence-params> in one clause
* <body1> = <body> with each occurrence of unqualified `this` substituted by `$this`.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* augment [<id> @] <type-pattern> <params> { <body> }
* ->
* implicit class <deconame> <type-params> ($this: <decorated>)
* extends AnyVal { <body2> }
*
* where
*
* <body2> = <body1> where each method definition gets <combined-params> as last parameter section.
* <deco-name>, <type-params> are as above.
*/
def augmentation(tree: Augment)(implicit ctx: Context): Tree = {
val Augment(id, augmented, impl) = tree
val isSimpleExtension =
impl.parents.isEmpty &&
impl.self.isEmpty &&
impl.body.forall(_.isInstanceOf[DefDef])
val (decorated, bindings) = decomposeTypePattern(augmented)
val (typeParams, evidenceParams) =
desugarTypeBindings(bindings, forPrimaryConstructor = !isSimpleExtension)
val decoName = id match {
case Ident(name) =>
name.asTypeName
case EmptyTree =>
def clsName(tree: Tree): String = leadingName("", tree)
val fromName = clsName(augmented)
val toName = impl.parents match {
case parent :: _ if !clsName(parent).isEmpty => "_to_" + clsName(parent)
case _ => ""
}
val core = s"${str.AUGMENT}$fromName$toName".toTermName
AugmentName.fresh(core.expandedName(ctx.owner.topLevelClass)).toTypeName
}

val firstParam = ValDef(nme.SELF, decorated, EmptyTree).withFlags(Private | Local | ParamAccessor)
var constr1 =
cpy.DefDef(impl.constr)(
tparams = typeParams.map(_.withFlags(Param | Private | Local)),
vparamss = (firstParam :: Nil) :: impl.constr.vparamss)
var parents1 = impl.parents
var body1 = substThis.transform(impl.body)
if (isSimpleExtension) {
constr1 = cpy.DefDef(constr1)(vparamss = constr1.vparamss.take(1))
parents1 = ref(defn.AnyValType) :: Nil
body1 = body1.map {
case ddef: DefDef =>
def resetFlags(vdef: ValDef) =
vdef.withMods(vdef.mods &~ PrivateLocalParamAccessor | Param)
val originalParams = impl.constr.vparamss.headOption.getOrElse(Nil).map(resetFlags)
addEvidenceParams(addEvidenceParams(ddef, originalParams), evidenceParams)
}
}
else
constr1 = addEvidenceParams(constr1, evidenceParams)

val icls =
TypeDef(decoName,
cpy.Template(impl)(constr = constr1, parents = parents1, body = body1))
.withFlags(Implicit)
desugr.println(i"desugar $augmented --> $icls")
classDef(icls)
}

private val substThis = new UntypedTreeMap {
override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
case This(Ident(tpnme.EMPTY)) => Ident(nme.SELF).withPos(tree.pos)
case _ => super.transform(tree)
}
}

private val leadingName = new UntypedTreeAccumulator[String] {
override def apply(x: String, tree: Tree)(implicit ctx: Context): String =
if (x.isEmpty)
tree match {
case Select(pre, nme.CONSTRUCTOR) => foldOver(x, pre)
case tree: RefTree if tree.name.isTypeName => tree.name.toString
case tree: TypeDef => tree.name.toString
case tree: Tuple => "Tuple"
case tree: Function => "Function"
case _ => foldOver(x, tree)
}
else x
}

def defTree(tree: Tree)(implicit ctx: Context): Tree = tree match {
case tree: ValDef => valDef(tree)
case tree: TypeDef => if (tree.isClassDef) classDef(tree) else tree
Expand All @@ -757,6 +883,7 @@ object desugar {
else defDef(tree)
case tree: ModuleDef => moduleDef(tree)
case tree: PatDef => patDef(tree)
case tree: Augment => augmentation(tree)
}

/** { stats; <empty > }
Expand Down
9 changes: 9 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def withName(name: Name)(implicit ctx: Context) = cpy.ModuleDef(this)(name.toTermName, impl)
}

/** augment id @ augmented impl */
case class Augment(id: Tree, augmented: Tree, impl: Template) extends DefTree

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

case class SymbolLit(str: String) extends TermTree
Expand Down Expand Up @@ -124,6 +127,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {

case class Sealed() extends Mod(Flags.Sealed)

case class Opaque() extends Mod(Flags.Opaque)

case class Override() extends Mod(Flags.Override)

case class Abstract() extends Mod(Flags.Abstract)
Expand Down Expand Up @@ -409,6 +414,10 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case tree: ModuleDef if (name eq tree.name) && (impl eq tree.impl) => tree
case _ => finalize(tree, untpd.ModuleDef(name, impl))
}
def Augment(tree: Tree)(id: Tree, augmented: Tree, impl: Template) = tree match {
case tree: Augment if (id eq tree.id) && (augmented eq tree.augmented) && (impl eq tree.impl) => tree
case _ => finalize(tree, untpd.Augment(id, augmented, impl))
}
def ParsedTry(tree: Tree)(expr: Tree, handler: Tree, finalizer: Tree) = tree match {
case tree: ParsedTry
if (expr eq tree.expr) && (handler eq tree.handler) && (finalizer eq tree.finalizer) => tree
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/Printers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ object Printers {
val checks: Printer = noPrinter
val config: Printer = noPrinter
val cyclicErrors: Printer = noPrinter
val desugr: Printer = noPrinter
val dottydoc: Printer = noPrinter
val exhaustivity: Printer = noPrinter
val gadts: Printer = noPrinter
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ class Definitions {
lazy val EqualsPatternClass = enterSpecialPolyClass(tpnme.EQUALS_PATTERN, EmptyFlags, Seq(AnyType))

lazy val RepeatedParamClass = enterSpecialPolyClass(tpnme.REPEATED_PARAM_CLASS, Covariant, Seq(ObjectType, SeqType))
lazy val OpaqueAliasAnnot = enterSpecialPolyClass(tpnme.OPAQUE_ALIAS, EmptyFlags, Seq(AnyType))

// fundamental classes
lazy val StringClass = ctx.requiredClass("java.lang.String")
Expand Down Expand Up @@ -1157,6 +1158,7 @@ class Definitions {
AnyRefAlias,
RepeatedParamClass,
ByNameParamClass2x,
OpaqueAliasAnnot,
AnyValClass,
NullClass,
NothingClass,
Expand Down
17 changes: 11 additions & 6 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,14 @@ object Flags {

final val AccessorOrSealed = Accessor.toCommonFlags

/** A mutable var */
/** A mutable var */
final val Mutable = termFlag(12, "mutable")

/** An opqaue type */
final val Opaque = typeFlag(12, "opaque")

final val MutableOrOpaque = Mutable.toCommonFlags

/** Symbol is local to current class (i.e. private[this] or protected[this]
* pre: Private or Protected are also set
*/
Expand All @@ -264,7 +269,7 @@ object Flags {
*/
final val ParamAccessor = termFlag(14, "<paramaccessor>")

/** A value or class implementing a module */
/** A value or class implementing a module */
final val Module = commonFlag(15, "module")
final val ModuleVal = Module.toTermFlags
final val ModuleClass = Module.toTypeFlags
Expand Down Expand Up @@ -441,12 +446,12 @@ object Flags {
/** Flags representing source modifiers */
final val SourceModifierFlags =
commonFlags(Private, Protected, Abstract, Final, Inline,
Sealed, Case, Implicit, Override, AbsOverride, Lazy, JavaStatic, Erased)
Sealed, Case, Implicit, Override, AbsOverride, Lazy, JavaStatic, Erased, Opaque)

/** Flags representing modifiers that can appear in trees */
final val ModifierFlags =
SourceModifierFlags | Module | Param | Synthetic | Package | Local |
commonFlags(Mutable)
SourceModifierFlags | Module | Param | Synthetic | Package | Local
// | Mutable is subsumed by commonFlags(Opaque) from SourceModifierFlags
// | Trait is subsumed by commonFlags(Lazy) from SourceModifierFlags

assert(ModifierFlags.isTermFlags && ModifierFlags.isTypeFlags)
Expand All @@ -457,7 +462,7 @@ object Flags {
/** Flags guaranteed to be set upon symbol creation */
final val FromStartFlags =
Module | Package | Deferred | MethodOrHKCommon | Param | ParamAccessor.toCommonFlags |
Scala2ExistentialCommon | Mutable.toCommonFlags | Touched | JavaStatic |
Scala2ExistentialCommon | MutableOrOpaque | Touched | JavaStatic |
CovariantOrOuter | ContravariantOrLabel | CaseAccessor.toCommonFlags |
NonMember | Erroneous | ImplicitCommon | Permanent | Synthetic |
SuperAccessorOrScala2x | Inline
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/core/NameKinds.scala
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ object NameKinds {
val SkolemName = new UniqueNameKind("?")
val LiftedTreeName = new UniqueNameKind("liftedTree")
val SuperArgName = new UniqueNameKind("$superArg$")
val AugmentName = new UniqueNameKind("_")

/** A kind of unique extension methods; Unlike other unique names, these can be
* unmangled.
Expand Down
17 changes: 4 additions & 13 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ object StdNames {
final val LOCALDUMMY_PREFIX = "<local " // owner of local blocks
final val ANON_CLASS = "$anon"
final val ANON_FUN = "$anonfun"
final val AUGMENT = "_augment_"

final val REPL_SESSION_LINE = "rs$line$"
final val REPL_ASSIGN_SUFFIX = "$assign"
Expand Down Expand Up @@ -143,8 +144,6 @@ object StdNames {
val WHILE_PREFIX: N = "while$"
val DEFAULT_EXCEPTION_NAME: N = "ex$"
val INITIALIZER_PREFIX: N = "initial$"
val COMPANION_MODULE_METHOD: N = "companion$module"
val COMPANION_CLASS_METHOD: N = "companion$class"
val BOUNDTYPE_ANNOT: N = "$boundType$"
val QUOTE: N = "'"
val TYPE_QUOTE: N = "type_'"
Expand Down Expand Up @@ -191,6 +190,8 @@ object StdNames {
final val EQUALS_PATTERN: N = "<equals>"
final val LOCAL_CHILD: N = "<local child>"
final val REPEATED_PARAM_CLASS: N = "<repeated>"
final val OPAQUE_ALIAS: N = "<opaque>"
final val LINKED_TYPE: N = "<linked-type>"
final val WILDCARD_STAR: N = "_*"
final val REIFY_TREECREATOR_PREFIX: N = "$treecreator"
final val REIFY_TYPECREATOR_PREFIX: N = "$typecreator"
Expand Down Expand Up @@ -246,6 +247,7 @@ object StdNames {

// Compiler-internal
val ANYname: N = "<anyname>"
val COMPANION: N = "<companion>"
val CONSTRUCTOR: N = "<init>"
val STATIC_CONSTRUCTOR: N = "<clinit>"
val DEFAULT_CASE: N = "defaultCase$"
Expand All @@ -254,17 +256,6 @@ object StdNames {
val FAKE_LOCAL_THIS: N = "this$"
val LAZY_FIELD_OFFSET: N = "OFFSET$"
val LAZY_SLOW_SUFFIX: N = "$lzycompute"
val UNIVERSE_BUILD_PREFIX: N = "$u.build."
val UNIVERSE_BUILD: N = "$u.build"
val UNIVERSE_PREFIX: N = "$u."
val UNIVERSE_SHORT: N = "$u"
val MIRROR_PREFIX: N = "$m."
val MIRROR_SHORT: N = "$m"
val MIRROR_UNTYPED: N = "$m$untyped"
val REIFY_FREE_PREFIX: N = "free$"
val REIFY_FREE_THIS_SUFFIX: N = "$this"
val REIFY_FREE_VALUE_SUFFIX: N = "$value"
val REIFY_SYMDEF_PREFIX: N = "symdef$"
val OUTER: N = "$outer"
val REFINE_CLASS: N = "<refinement>"
val ROOTPKG: N = "_root_"
Expand Down
Loading