Skip to content
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

[Proposal] Typeclass Traits #4153

Closed
wants to merge 54 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
fb7084c
Add opaque types: parsing & pickling
odersky Feb 21, 2018
b3193a5
Store opaque info in annotation
odersky Feb 21, 2018
e583338
Keep track of opaque companion links
odersky Feb 21, 2018
3e53f63
Maintain companion aliases as GADT bounds
odersky Feb 21, 2018
d0eec14
Test cases
odersky Feb 21, 2018
8b2117b
Allow for higher-kinded opaque types
odersky Feb 21, 2018
e0da82b
Handle higher-kinded comparisons involving GADTs
odersky Feb 21, 2018
2247529
Change companion detection scheme
odersky Feb 22, 2018
c70bcb0
Eliminate boxing for opaque types
odersky Feb 22, 2018
af5c199
Make OpaqueAlias and LinkedType special classes
odersky Feb 22, 2018
f2cf353
Make implicit scope include companion objects of opaque types
odersky Feb 22, 2018
71ae943
Add reference documentation
odersky Feb 22, 2018
89dd537
Fix test
odersky Feb 22, 2018
893eebb
Fix indentation
odersky Feb 22, 2018
77317b0
Treat companions of opaque types specially
odersky Feb 23, 2018
4006366
Replace annotation extractor with access methods
odersky Feb 23, 2018
fa099ed
Simplify companion scheme
odersky Feb 24, 2018
55cd70f
add test
odersky Feb 24, 2018
6161baf
Follow GADT bounds when computing members
odersky Mar 9, 2018
6fee4bb
Consider GADT bounds for findMember and underlying
odersky Mar 12, 2018
ea65c25
Extend opaque companion context to inlined code
odersky Mar 12, 2018
e50e669
Remove stray println
odersky Mar 14, 2018
e3e58eb
Disable failing FromTasty tests
odersky Mar 14, 2018
252f04a
Fix ctx of opaque type companion
nicolasstucki Mar 15, 2018
c8ff1d6
Fix ctx and call of inlined opaque extension methods
nicolasstucki Mar 15, 2018
cf740ba
Fix rebase breakage
odersky Mar 31, 2018
b158666
Fix modifier printing
odersky Mar 31, 2018
22a7167
Schema for named extensions
odersky Mar 14, 2018
3effb96
Parser for fixed syntax
odersky Mar 14, 2018
a8d48e9
Desugarings for extensions
odersky Mar 14, 2018
2084371
Avoid `extension` as an identifier.
odersky Mar 14, 2018
68ab0bb
Pretty printing, fixes, and tests
odersky Mar 15, 2018
e318da7
Adapt RefinedPrinter to new scheme for modText
odersky Mar 31, 2018
9c773c6
Pretty printing, fixes, and tests
odersky Mar 15, 2018
a7b4719
[Proposal] Common Declarations
odersky Mar 21, 2018
9a220cf
Fix typos
odersky Mar 21, 2018
61b179b
Small changes to test
odersky Mar 22, 2018
5ce44c6
Uniform extensions
odersky Mar 25, 2018
c1c74a3
Also add cases where extended traits are used as direct parameters
odersky Mar 26, 2018
153554b
Treat common traits and type classes alike
odersky Mar 27, 2018
556add7
More examples for typeclass encodings
odersky Mar 28, 2018
e4beb48
Allow extension re-use through automatically inserted forwarders
odersky Mar 29, 2018
2e754c3
Explain possible typeclass trait extension
odersky Mar 31, 2018
a0542fe
Fix rebase breakage
odersky Mar 31, 2018
46c7809
Polishings
odersky Mar 31, 2018
0453e05
Small fixes to docs
odersky Apr 1, 2018
434a689
Fix test
odersky Apr 2, 2018
6e18885
Flesh out documentation
odersky Apr 2, 2018
26ce98a
Fix more typos
odersky Apr 2, 2018
4b1ba83
Merge vendethiel/patch-1
odersky Apr 3, 2018
0cfc375
Name-mangle both injector parameters
odersky Apr 4, 2018
07256c2
Disentangle common and typeclass
odersky Apr 5, 2018
97f7590
Make the encoding of `common` more regular.
odersky Apr 5, 2018
37f8424
Rename `common` from a trait companion to `at`.
odersky Apr 5, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/backend/jvm/GenBCode.scala
Expand Up @@ -57,7 +57,7 @@ class GenBCode extends Phase {
if (myOutput eq null) {
val path = Directory(ctx.settings.outputDir.value)
myOutput =
if (path.extension == "jar") JarArchive.create(path)
if (path.`extension` == "jar") JarArchive.create(path)
else new PlainDirectory(path)
}
myOutput
Expand Down
110 changes: 95 additions & 15 deletions compiler/src/dotty/tools/dotc/ast/Desugar.scala
Expand Up @@ -11,6 +11,7 @@ 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,77 @@ object desugar {
Bind(name, Ident(nme.WILDCARD)).withPos(tree.pos)
}

/** extension id <type-params> <implicit-params> for <extended> : <parents> { <body> }
* ->
* implicit class <id> <type-params> ($this: <extended>) <combined-params>
* extends <parents> {
* import $this._
* <body1>
* }
*
* where
*
* (<type-params>, <evidence-params>) = desugarTypeBindings(<type-params0>)
* <combined-params> = <implicit-params> concatenated with <evidence-params> in one clause
* <body1> = <body> with each occurrence of unqualified `this` substituted by `$this`.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* extension <id> <type-params> <implicit-params> for <extended> { <defs> }
* ->
* implicit class <id> <type-params> (private val $this: <extended>)
* extends AnyVal {
* import $this._
* <body2>
* }
*
* where
*
* <body2> = <body1> where each method definition gets <combined-params> as last parameter section.
*/
def extensionDef(tree: Extension)(implicit ctx: Context): Tree = {
val Extension(name, constr, extended, impl) = tree
val isSimpleExtension = impl.parents.isEmpty

val firstParams = ValDef(nme.SELF, extended, EmptyTree).withFlags(Private | Local | ParamAccessor) :: Nil
val importSelf = Import(Ident(nme.SELF), Ident(nme.WILDCARD) :: Nil)
val body1 = importSelf :: substThis.transform(impl.body)
val impl1 =
if (isSimpleExtension) {
val (typeParams, evidenceParams) =
desugarTypeBindings(constr.tparams, forPrimaryConstructor = false)
cpy.Template(impl)(
constr = cpy.DefDef(constr)(tparams = typeParams, vparamss = firstParams :: Nil),
parents = ref(defn.AnyValType) :: Nil,
body = body1.map {
case ddef: DefDef =>
def resetFlags(vdef: ValDef) =
vdef.withMods(vdef.mods &~ PrivateLocalParamAccessor | Param)
val originalParams = constr.vparamss.headOption.getOrElse(Nil).map(resetFlags)
addEvidenceParams(addEvidenceParams(ddef, originalParams), evidenceParams)
case other =>
other
})
}
else
cpy.Template(impl)(
constr = cpy.DefDef(constr)(vparamss = firstParams :: constr.vparamss),
body = body1)
val mods1 =
if (isSimpleExtension) tree.mods
else tree.mods.withAddedMod(Mod.InstanceDcl())
val icls = TypeDef(name, impl1).withMods(mods1 | Implicit)
desugr.println(i"desugar $tree --> $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)
}
}

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 +836,7 @@ object desugar {
else defDef(tree)
case tree: ModuleDef => moduleDef(tree)
case tree: PatDef => patDef(tree)
case tree: Extension => extensionDef(tree)
}

/** { stats; <empty > }
Expand Down
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Expand Up @@ -1244,6 +1244,8 @@ object Trees {
Stats.record("TreeAccumulator.foldOver total")
def localCtx =
if (tree.hasType && tree.symbol.exists) ctx.withOwner(tree.symbol) else ctx
def templateCtx =
ctx.handleOpaqueCompanion(ctx.fresh, ctx.owner)
tree match {
case Ident(name) =>
x
Expand Down Expand Up @@ -1320,6 +1322,7 @@ object Trees {
implicit val ctx = localCtx
this(x, rhs)
case tree @ Template(constr, parents, self, _) =>
implicit val ctx = templateCtx
this(this(this(this(x, constr), parents), self), tree.body)
case Import(expr, selectors) =>
this(x, expr)
Expand Down
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/tpd.scala
Expand Up @@ -1017,8 +1017,11 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo {
/** A key to be used in a context property that tracks enclosing inlined calls */
private val InlinedCalls = new Property.Key[List[Tree]]

override def inlineContext(call: Tree)(implicit ctx: Context): Context =
ctx.fresh.setProperty(InlinedCalls, call :: enclosingInlineds)
override def inlineContext(call: Tree)(implicit ctx: Context): Context = {
val ictx = ctx.fresh.setProperty(InlinedCalls, call :: enclosingInlineds)
def stopAt(owner: Symbol) = owner.is(Package) || ctx.owner.isContainedIn(owner)
(ictx /: call.symbol.ownersIterator.takeWhile(!stopAt(_)))(ctx.handleOpaqueCompanion)
}

/** All enclosing calls that are currently inlined, from innermost to outermost */
def enclosingInlineds(implicit ctx: Context): List[Tree] =
Expand Down
22 changes: 22 additions & 0 deletions compiler/src/dotty/tools/dotc/ast/untpd.scala
Expand Up @@ -40,6 +40,16 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
def withName(name: Name)(implicit ctx: Context) = cpy.ModuleDef(this)(name.toTermName, impl)
}

/** extension name tparams vparamss for tpt impl
*
* where `tparams` and `vparamss` are part of `constr`.
*/
case class Extension(name: TypeName, constr: DefDef, tpt: Tree, impl: Template)
extends MemberDef{
type ThisTree[-T >: Untyped] <: Trees.NameTree[T] with Trees.MemberDef[T] with Extension
def withName(name: Name)(implicit ctx: Context) = cpy.Extension(this)(name.toTypeName, constr, tpt, impl)
}

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

case class SymbolLit(str: String) extends TermTree
Expand Down Expand Up @@ -124,6 +134,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 All @@ -137,6 +149,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
case class Enum() extends Mod(Flags.EmptyFlags)

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

case class InstanceDcl() extends Mod(Flags.EmptyFlags)
}

/** Modifiers and annotations for definitions
Expand Down Expand Up @@ -409,6 +423,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 Extension(tree: Tree)(name: TypeName, constr: DefDef, tpt: Tree, impl: Template) = tree match {
case tree: Extension if (name eq tree.name) && (constr eq tree.constr) && (tpt eq tree.tpt) && (impl eq tree.impl) => tree
case _ => finalize(tree, untpd.Extension(name, constr, tpt, 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 Expand Up @@ -492,6 +510,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
override def transform(tree: Tree)(implicit ctx: Context): Tree = tree match {
case ModuleDef(name, impl) =>
cpy.ModuleDef(tree)(name, transformSub(impl))
case Extension(name, constr, tpt, impl) =>
cpy.Extension(tree)(name, transformSub(constr), transform(tpt), transformSub(impl))
case ParsedTry(expr, handler, finalizer) =>
cpy.ParsedTry(tree)(transform(expr), transform(handler), transform(finalizer))
case SymbolLit(str) =>
Expand Down Expand Up @@ -541,6 +561,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo {
override def foldOver(x: X, tree: Tree)(implicit ctx: Context): X = tree match {
case ModuleDef(name, impl) =>
this(x, impl)
case Extension(name, constr, tpt, impl) =>
this(this(this(x, constr), tpt), impl)
case ParsedTry(expr, handler, finalizer) =>
this(this(this(x, expr), handler), finalizer)
case SymbolLit(str) =>
Expand Down
1 change: 1 addition & 0 deletions compiler/src/dotty/tools/dotc/config/Printers.scala
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: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/config/Settings.scala
Expand Up @@ -144,7 +144,7 @@ object Settings {
Path(arg) match {
case _: Directory =>
update(arg, args)
case p if p.extension == "jar" =>
case p if p.`extension` == "jar" =>
update(arg, args)
case _ =>
fail(s"'$arg' does not exist or is not a directory", args)
Expand Down
2 changes: 2 additions & 0 deletions compiler/src/dotty/tools/dotc/core/Definitions.scala
Expand Up @@ -537,6 +537,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 @@ -1171,6 +1172,7 @@ class Definitions {
AnyKindClass,
RepeatedParamClass,
ByNameParamClass2x,
OpaqueAliasAnnot,
AnyValClass,
NullClass,
NothingClass,
Expand Down
28 changes: 19 additions & 9 deletions compiler/src/dotty/tools/dotc/core/Flags.scala
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 @@ -439,15 +444,20 @@ object Flags {
// --------- Combined Flag Sets and Conjunctions ----------------------

/** Flags representing source modifiers */
final val SourceModifierFlags =
commonFlags(Private, Protected, Abstract, Final, Inline,
Sealed, Case, Implicit, Override, AbsOverride, Lazy, JavaStatic, Erased)
private val CommonSourceModifierFlags =
commonFlags(Private, Protected, Final, Case, Implicit, Override, JavaStatic)

final val TypeSourceModifierFlags =
CommonSourceModifierFlags.toTypeFlags | Abstract | Sealed | Opaque

final val TermSourceModifierFlags =
CommonSourceModifierFlags.toTermFlags | Inline | AbsOverride | Lazy | Erased

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

assert(ModifierFlags.isTermFlags && ModifierFlags.isTypeFlags)

Expand All @@ -457,7 +467,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
5 changes: 3 additions & 2 deletions compiler/src/dotty/tools/dotc/core/StdNames.scala
Expand Up @@ -143,8 +143,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 +189,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 @@ -247,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 Down