From 0dc0191137abcd1153bb536114a47e0732cf5d8a Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 26 Nov 2018 14:46:38 +0100 Subject: [PATCH 01/56] Allow `,` in parents of template New syntax A extends B, C { ... } instead of A extends B with C { ... } The old syntax is still allowed, but it's planned to phase it out together with `with` as a type operator. Also: disallow A extends { ... } (make it a migration warning under -language:Scala2) While doing these changes, applied some refactorings to handle templates in the parser which hopefully make the code clearer. --- .../dotty/tools/dotc/parsing/Parsers.scala | 109 ++++++++++++------ .../dotty/tools/dotc/parsing/Scanners.scala | 5 + docs/docs/internals/syntax.md | 12 +- tests/invalid/neg/typelevel.scala | 2 +- tests/neg/i2770.scala | 4 +- tests/pos/Iter2.scala | 12 +- tests/pos/this-types.scala | 2 +- tests/pos/traits_1.scala | 2 +- tests/pos/typeclass-encoding.scala | 4 +- tests/run/generic/SearchResult.scala | 2 +- tests/run/t6090.scala | 2 +- tests/run/tasty-positioned.check | 2 +- 12 files changed, 98 insertions(+), 60 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 63c36ba56e7c..6628fb8b0b20 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1407,7 +1407,7 @@ object Parsers { } else simpleExpr() - /** SimpleExpr ::= new Template + /** SimpleExpr ::= ‘new’ (ConstrApp [TemplateBody] | TemplateBody) * | BlockExpr * | ‘'{’ BlockExprContents ‘}’ * | ‘'(’ ExprsInParens ‘)’ @@ -1451,15 +1451,7 @@ object Parsers { atSpan(in.offset)(Quote(inBrackets(typ()))) case NEW => canApply = false - val start = in.skipToken() - val (impl, missingBody) = template(emptyConstructor) - impl.parents match { - case parent :: Nil if missingBody => - if (parent.isType) ensureApplied(wrapNew(parent)) - else parent.withSpan(Span(start, in.lastOffset)) - case _ => - New(impl.withSpan(Span(start, in.lastOffset))) - } + newExpr() case _ => if (isLiteral) literal() else { @@ -1489,6 +1481,33 @@ object Parsers { } } + /** SimpleExpr ::= ‘new’ (ConstrApp {`with` ConstrApp} [TemplateBody] | TemplateBody) + */ + def newExpr(): Tree = { + val start = in.skipToken() + def reposition(t: Tree) = t.withSpan(Span(start, in.lastOffset)) + newLineOptWhenFollowedBy(LBRACE) + val parents = + if (in.token == LBRACE) Nil + else constrApp() :: { + if (in.token == WITH) { + // Enable this for 3.1, when we drop `with` for inheritance: + // in.errorUnlessInScala2Mode( + // "anonymous class with multiple parents is no longer supported; use a named class instead") + in.nextToken() + tokenSeparated(WITH, constrApp) + } + else Nil + } + newLineOptWhenFollowedBy(LBRACE) + parents match { + case parent :: Nil if in.token != LBRACE => + reposition(if (parent.isType) ensureApplied(wrapNew(parent)) else parent) + case _ => + New(reposition(templateBodyOpt(emptyConstructor, parents, isEnum = false))) + } + } + /** ExprsInParens ::= ExprInParens {`,' ExprInParens} */ def exprsInParensOpt(): List[Tree] = @@ -2414,18 +2433,18 @@ object Parsers { } def objectDefRest(start: Offset, mods: Modifiers, name: TermName): ModuleDef = { - val template = templateOpt(emptyConstructor) - finalizeDef(ModuleDef(name, template), mods, start) + val templ = templateOpt(emptyConstructor) + finalizeDef(ModuleDef(name, templ), mods, start) } - /** EnumDef ::= id ClassConstr [`extends' [ConstrApps]] EnumBody + /** EnumDef ::= id ClassConstr [`extends' ConstrApps] EnumBody */ def enumDef(start: Offset, mods: Modifiers, enumMod: Mod): TypeDef = atSpan(start, nameStart) { val modName = ident() val clsName = modName.toTypeName val constr = classConstr() - val impl = templateOpt(constr, isEnum = true) - finalizeDef(TypeDef(clsName, impl), addMod(mods, enumMod), start) + val templ = templateOpt(constr, isEnum = true) + finalizeDef(TypeDef(clsName, templ), addMod(mods, enumMod), start) } /** EnumCase = `case' (id ClassConstr [`extends' ConstrApps] | ids) @@ -2481,34 +2500,48 @@ object Parsers { else t } - /** Template ::= ConstrApps [TemplateBody] | TemplateBody - * ConstrApps ::= ConstrApp {`with' ConstrApp} - * - * @return a pair consisting of the template, and a boolean which indicates - * whether the template misses a body (i.e. no {...} part). + /** ConstrApps ::= ConstrApp {‘with’ ConstrApp} (to be deprecated in 3.1) + * | ConstrApp {‘,’ ConstrApp} */ - def template(constr: DefDef, isEnum: Boolean = false): (Template, Boolean) = { + def constrApps(): List[Tree] = { + val t = constrApp() + val ts = + if (in.token == WITH) { + in.nextToken() + tokenSeparated(WITH, constrApp) + } + else if (in.token == COMMA) { + in.nextToken() + tokenSeparated(COMMA, constrApp) + } + else Nil + t :: ts + } + + /** Template ::= [‘extends’ ConstrApps] [TemplateBody] + */ + def template(constr: DefDef, isEnum: Boolean = false): Template = { + val parents = + if (in.token == EXTENDS) { + in.nextToken() + constrApps() + } + else Nil newLineOptWhenFollowedBy(LBRACE) - if (in.token == LBRACE) (templateBodyOpt(constr, Nil, isEnum), false) - else { - val parents = tokenSeparated(WITH, constrApp) - newLineOptWhenFollowedBy(LBRACE) - if (isEnum && in.token != LBRACE) - syntaxErrorOrIncomplete(ExpectedTokenButFound(LBRACE, in.token)) - val missingBody = in.token != LBRACE - (templateBodyOpt(constr, parents, isEnum), missingBody) - } + if (isEnum && in.token != LBRACE) + syntaxErrorOrIncomplete(ExpectedTokenButFound(LBRACE, in.token)) + templateBodyOpt(constr, parents, isEnum) } - /** TemplateOpt = [`extends' Template | TemplateBody] + /** TemplateOpt = [Template] */ - def templateOpt(constr: DefDef, isEnum: Boolean = false): Template = - if (in.token == EXTENDS) { in.nextToken(); template(constr, isEnum)._1 } - else { - newLineOptWhenFollowedBy(LBRACE) - if (in.token == LBRACE) template(constr, isEnum)._1 - else Template(constr, Nil, EmptyValDef, Nil) - } + def templateOpt(constr: DefDef, isEnum: Boolean = false): Template = { + newLineOptWhenFollowedBy(LBRACE) + if (in.token == EXTENDS || in.token == LBRACE) + template(constr, isEnum) + else + Template(constr, Nil, EmptyValDef, Nil) + } /** TemplateBody ::= [nl] `{' TemplateStatSeq `}' */ diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index ec1c3c28106f..1394c88766fe 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -250,6 +250,11 @@ object Scanners { isScala2Mode } + /** A migration warning if in Scala-2 mode, an error otherwise */ + def errorOrMigrationWarning(msg: String, pos: Position = Position(offset)): Unit = + if (isScala2Mode) ctx.migrationWarning(msg, source.atPos(pos)) + else ctx.error(msg, source.atPos(pos)) + // Get next token ------------------------------------------------------------ /** Are we directly in a string interpolation expression? diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 06da1afd9f4c..1900e615976b 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -186,7 +186,7 @@ PostfixExpr ::= InfixExpr [id] InfixExpr ::= PrefixExpr | InfixExpr id [nl] InfixExpr InfixOp(expr, op, expr) PrefixExpr ::= [‘-’ | ‘+’ | ‘~’ | ‘!’] SimpleExpr PrefixOp(expr, op) -SimpleExpr ::= ‘new’ Template New(templ) +SimpleExpr ::= ‘new’ (ConstrApp [TemplateBody] | TemplateBody) New(constr | templ) | BlockExpr | ''{’ BlockExprContents ‘}’ | ‘'(’ ExprsInParens ‘)’ @@ -341,14 +341,14 @@ DefDef ::= DefSig [(‘:’ | ‘<:’) Type] ‘=’ Expr TmplDef ::= ([‘case’] ‘class’ | ‘trait’) ClassDef | [‘case’] ‘object’ ObjectDef | ‘enum’ EnumDef -ClassDef ::= id ClassConstr TemplateOpt ClassDef(mods, name, tparams, templ) -ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses with DefDef(_, , Nil, vparamss, EmptyTree, EmptyTree) as first stat +ClassDef ::= id ClassConstr [Template] ClassDef(mods, name, tparams, templ) +ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses with DefDef(_, , Nil, vparamss, EmptyTree, EmptyTree) as first stat ConstrMods ::= {Annotation} [AccessModifier] -ObjectDef ::= id TemplateOpt ModuleDef(mods, name, template) // no constructor -EnumDef ::= id ClassConstr [‘extends’ [ConstrApps]] EnumBody EnumDef(mods, name, tparams, template) -TemplateOpt ::= [‘extends’ Template | [nl] TemplateBody] +ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor +EnumDef ::= id ClassConstr [‘extends’ ConstrApps] EnumBody EnumDef(mods, name, tparams, template) Template ::= ConstrApps [TemplateBody] | TemplateBody Template(constr, parents, self, stats) ConstrApps ::= ConstrApp {‘with’ ConstrApp} + | ConstrApp {‘,’ ConstrApp} ConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args) ConstrExpr ::= SelfInvocation | ConstrBlock diff --git a/tests/invalid/neg/typelevel.scala b/tests/invalid/neg/typelevel.scala index 32d270b965a9..2dcebb19a41d 100644 --- a/tests/invalid/neg/typelevel.scala +++ b/tests/invalid/neg/typelevel.scala @@ -53,7 +53,7 @@ object Test { val rr1 = new Deco1(HCons(1, HNil)) ++ HNil val rr1a: HCons[Int, HNil.type] = rr1 // error (type error because no inline) - class Deco2(val as: HList) extends java.lang.Cloneable with java.lang.Comparable[Deco2] { + class Deco2(val as: HList) extends java.lang.Cloneable, java.lang.Comparable[Deco2] { inline def ++ (bs: HList) = concat(as, bs) } } \ No newline at end of file diff --git a/tests/neg/i2770.scala b/tests/neg/i2770.scala index 4e7c6cb0a8cd..8680f6c94318 100644 --- a/tests/neg/i2770.scala +++ b/tests/neg/i2770.scala @@ -8,7 +8,7 @@ trait C2 extends B2 { type L[X, Y] <: String } // error: illegal override trait D { type I } trait E extends D { type I <: String } trait F extends D { type I >: String } -trait G extends E with F // ok +trait G extends E, F // ok trait H extends D { type I >: Int } -trait H2 extends E with H // error: illegal override +trait H2 extends E, H // error: illegal override diff --git a/tests/pos/Iter2.scala b/tests/pos/Iter2.scala index b9dc5d625611..ff4ab32e0c21 100644 --- a/tests/pos/Iter2.scala +++ b/tests/pos/Iter2.scala @@ -58,16 +58,16 @@ object Iter2 { def fromIterator[B](it: Iterator[B]): C[B] } - trait Iterable[+IA] extends IterableOnce[IA] with FromIterator[Iterable] { + trait Iterable[+IA] extends IterableOnce[IA], FromIterator[Iterable] { def view: View[IA] = new View(iterator) } - trait Seq[+AA] extends Iterable[AA] with FromIterator[Seq] { + trait Seq[+AA] extends Iterable[AA], FromIterator[Seq] { def apply(i: Int): AA def length: Int } - sealed trait List[+A] extends Seq[A] with FromIterator[List] { + sealed trait List[+A] extends Seq[A], FromIterator[List] { def isEmpty: Boolean def head: A def tail: List[A] @@ -84,7 +84,7 @@ object Iter2 { if (isEmpty) 0 else 1 + tail.length } - class View[+A](it: Iterator[A]) extends Iterable[A] with FromIterator[View] { + class View[+A](it: Iterator[A]) extends Iterable[A], FromIterator[View] { def iterator: Iterator[A] = it.copy def fromIterator[B](it: Iterator[B]): View[B] = new View(it) } @@ -101,7 +101,7 @@ object Iter2 { def tail = ??? } - class ArrayBuffer[A] private (initElems: Array[AnyRef], initLen: Int) extends Seq[A] with FromIterator[ArrayBuffer] { + class ArrayBuffer[A] private (initElems: Array[AnyRef], initLen: Int) extends Seq[A], FromIterator[ArrayBuffer] { def this() = this(new Array[AnyRef](16), 0) def this(it: ArrayIterator[A]) = this(it.elems, it.len) private var elems: Array[AnyRef] = initElems @@ -116,7 +116,7 @@ object Iter2 { def length = len } /* - class SeqView[A](itf: () => Iterator) extends Seq[A] with FromIterator[SeqView] { + class SeqView[A](itf: () => Iterator) extends Seq[A], FromIterator[SeqView] { def iterator = it def buildIterator = it def fromIterator[B](it: Iterator[B]) = it match { diff --git a/tests/pos/this-types.scala b/tests/pos/this-types.scala index d75de54b3ffa..ddbc0f9c1b8a 100644 --- a/tests/pos/this-types.scala +++ b/tests/pos/this-types.scala @@ -9,7 +9,7 @@ trait C extends A { type A_This = C_This type C_This <: C } -trait D extends B with C { +trait D extends B, C { type B_This = D_This type C_This = D_This type D_This <: D diff --git a/tests/pos/traits_1.scala b/tests/pos/traits_1.scala index 3c6f9437a5fa..bd597149e268 100644 --- a/tests/pos/traits_1.scala +++ b/tests/pos/traits_1.scala @@ -17,7 +17,7 @@ object Test { case _ => false } } - trait BorderedColoredShape extends Shape with Bordered with Colored { + trait BorderedColoredShape extends Shape, Bordered, Colored { override def equals(other: Any) = other match { case that: BorderedColoredShape => ( super.equals(that) && diff --git a/tests/pos/typeclass-encoding.scala b/tests/pos/typeclass-encoding.scala index 69679e218ee3..12ae16e208a6 100644 --- a/tests/pos/typeclass-encoding.scala +++ b/tests/pos/typeclass-encoding.scala @@ -65,7 +65,7 @@ object semiGroups { type StaticPart[X] = MonoidStatic[X] } - implicit object extend_Int_Monoid extends MonoidStatic[Int] with Implementation[Int] { + implicit object extend_Int_Monoid extends MonoidStatic[Int], Implementation[Int] { type Implemented = Monoid def unit: Int = 0 def inject($this: Int) = new Monoid { @@ -74,7 +74,7 @@ object semiGroups { } } - implicit object extend_String_Monoid extends MonoidStatic[String] with Implementation[String] { + implicit object extend_String_Monoid extends MonoidStatic[String], Implementation[String] { type Implemented = Monoid def unit = "" def inject($this: String): Monoid { type This = String } = diff --git a/tests/run/generic/SearchResult.scala b/tests/run/generic/SearchResult.scala index d4380a072b9f..96ec7e3f2536 100644 --- a/tests/run/generic/SearchResult.scala +++ b/tests/run/generic/SearchResult.scala @@ -11,7 +11,7 @@ import Shapes._ */ sealed trait SearchResult extends Enum -object SearchResult extends { +object SearchResult { private val $values = new runtime.EnumValues[SearchResult] def valueOf = $values.fromInt diff --git a/tests/run/t6090.scala b/tests/run/t6090.scala index e7dbb36a05ae..51999a917e38 100644 --- a/tests/run/t6090.scala +++ b/tests/run/t6090.scala @@ -1,6 +1,6 @@ class X { def ==(other: X) = true } class V(val x: X) extends AnyVal -object Test extends { +object Test { def main(args: Array[String]) = assert((new V(new X) == new V(new X))) } diff --git a/tests/run/tasty-positioned.check b/tests/run/tasty-positioned.check index 6730ef470015..3005121fb971 100644 --- a/tests/run/tasty-positioned.check +++ b/tests/run/tasty-positioned.check @@ -5,4 +5,4 @@ acbvasdfa columns:13-36 lines:12-12 acbvasdfa columns:13-24 lines:13-13 a b columns:6-25 lines:15-16 -Foo columns:16-19 lines:17-17 +Foo columns:12-19 lines:17-17 From 6bf25d2a9b22d9d00673ac88a3845620c2b61cf0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 26 Nov 2018 15:49:54 +0100 Subject: [PATCH 02/56] Syntax and parsing for `derives` clauses --- .../dotty/tools/dotc/parsing/Parsers.scala | 33 ++++++++++++++----- .../dotty/tools/dotc/parsing/Scanners.scala | 3 +- .../src/dotty/tools/dotc/parsing/Tokens.scala | 4 ++- docs/docs/internals/syntax.md | 5 +-- tests/run/typeclass-derivation2.scala | 5 ++- 5 files changed, 35 insertions(+), 15 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 6628fb8b0b20..d4db597bd27e 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -649,8 +649,8 @@ object Parsers { /** QualId ::= id {`.' id} */ - def qualId(): Tree = - dotSelectors(termIdent()) + val qualId: () => Tree = + () => dotSelectors(termIdent()) /** SimpleExpr ::= literal * | symbol @@ -2437,7 +2437,7 @@ object Parsers { finalizeDef(ModuleDef(name, templ), mods, start) } - /** EnumDef ::= id ClassConstr [`extends' ConstrApps] EnumBody + /** EnumDef ::= id ClassConstr InheritClauses EnumBody */ def enumDef(start: Offset, mods: Modifiers, enumMod: Mod): TypeDef = atSpan(start, nameStart) { val modName = ident() @@ -2518,15 +2518,32 @@ object Parsers { t :: ts } - /** Template ::= [‘extends’ ConstrApps] [TemplateBody] + /** InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] */ - def template(constr: DefDef, isEnum: Boolean = false): Template = { - val parents = + def inheritClauses(): (List[Tree], List[Tree]) = { + val extended = if (in.token == EXTENDS) { in.nextToken() - constrApps() + if (in.token == LBRACE) { + in.errorOrMigrationWarning("`extends' must be followed by at least one parent") + Nil + } + else constrApps() + } + else Nil + val derived = + if (in.token == DERIVES) { + in.nextToken() + tokenSeparated(COMMA, qualId) } else Nil + (extended, derived) + } + + /** Template ::= InheritClauses [TemplateBody] + */ + def template(constr: DefDef, isEnum: Boolean = false): Template = { + val (parents, deriveds) = inheritClauses() newLineOptWhenFollowedBy(LBRACE) if (isEnum && in.token != LBRACE) syntaxErrorOrIncomplete(ExpectedTokenButFound(LBRACE, in.token)) @@ -2537,7 +2554,7 @@ object Parsers { */ def templateOpt(constr: DefDef, isEnum: Boolean = false): Template = { newLineOptWhenFollowedBy(LBRACE) - if (in.token == EXTENDS || in.token == LBRACE) + if (in.token == EXTENDS || in.token == DERIVES || in.token == LBRACE) template(constr, isEnum) else Template(constr, Nil, EmptyValDef, Nil) diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 1394c88766fe..e39c2ef2f7ed 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -205,7 +205,8 @@ object Scanners { private def handleMigration(keyword: Token): Token = if (!isScala2Mode) keyword else if ( keyword == ENUM - || keyword == ERASED) treatAsIdent() + || keyword == ERASED + || keyword == DERIVES) treatAsIdent() else keyword diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala index b9afcf03aa71..c8eb83d1483c 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala @@ -94,6 +94,7 @@ abstract class TokensCommon { //final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate //final val ENUM = 62; enter(ENUM, "enum") //final val ERASED = 63; enter(ERASED, "erased") + //final val DERIVES = 64; enter(DERIVES, "derives") /** special symbols */ final val COMMA = 70; enter(COMMA, "','") @@ -178,6 +179,7 @@ object Tokens extends TokensCommon { final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate final val ENUM = 62; enter(ENUM, "enum") final val ERASED = 63; enter(ERASED, "erased") + final val DERIVES = 64; enter(DERIVES, "derives") /** special symbols */ final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line") @@ -198,7 +200,7 @@ object Tokens extends TokensCommon { /** XML mode */ final val XMLSTART = 96; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate - final val alphaKeywords: TokenSet = tokenRange(IF, ERASED) + final val alphaKeywords: TokenSet = tokenRange(IF, DERIVES) final val symbolicKeywords: TokenSet = tokenRange(USCORE, VIEWBOUND) final val symbolicTokens: TokenSet = tokenRange(COMMA, VIEWBOUND) final val keywords: TokenSet = alphaKeywords | symbolicKeywords diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 1900e615976b..ed07258e00da 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -345,8 +345,9 @@ ClassDef ::= id ClassConstr [Template] ClassConstr ::= [ClsTypeParamClause] [ConstrMods] ClsParamClauses with DefDef(_, , Nil, vparamss, EmptyTree, EmptyTree) as first stat ConstrMods ::= {Annotation} [AccessModifier] ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor -EnumDef ::= id ClassConstr [‘extends’ ConstrApps] EnumBody EnumDef(mods, name, tparams, template) -Template ::= ConstrApps [TemplateBody] | TemplateBody Template(constr, parents, self, stats) +EnumDef ::= id ClassConstr InheritClauses EnumBody EnumDef(mods, name, tparams, template) +Template ::= InheritClauses [TemplateBody] Template(constr, parents, self, stats) +InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] ConstrApps ::= ConstrApp {‘with’ ConstrApp} | ConstrApp {‘,’ ConstrApp} ConstrApp ::= AnnotType {ArgumentExprs} Apply(tp, args) diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index 69863715976f..278a4babafbc 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -108,8 +108,7 @@ object Deriving { } // An algebraic datatype -enum Lst[+T] // derives Eq, Pickler -{ +enum Lst[+T] derives Eq, Pickler, Show { case Cons(hd: T, tl: Lst[T]) case Nil } @@ -146,7 +145,7 @@ object Lst extends Deriving { } // A simple product type -case class Pair[T](x: T, y: T) // derives Eq, Pickler +case class Pair[T](x: T, y: T) derives Eq, Pickler object Pair extends Deriving { // common compiler-generated infrastructure From 2e72be446df2ab1549c0a175e61b07e71af8618e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 26 Nov 2018 18:39:44 +0100 Subject: [PATCH 03/56] Make `derives` a soft keyword --- compiler/src/dotty/tools/dotc/core/StdNames.scala | 1 + compiler/src/dotty/tools/dotc/parsing/Parsers.scala | 4 ++-- compiler/src/dotty/tools/dotc/parsing/Scanners.scala | 3 +-- compiler/src/dotty/tools/dotc/parsing/Tokens.scala | 4 +--- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index f618a91271c6..c449c9979198 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -398,6 +398,7 @@ object StdNames { val definitions: N = "definitions" val delayedInit: N = "delayedInit" val delayedInitArg: N = "delayedInit$body" + val derives: N = "derives" val drop: N = "drop" val dynamics: N = "dynamics" val elem: N = "elem" diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index d4db597bd27e..3be24beee17a 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -2532,7 +2532,7 @@ object Parsers { } else Nil val derived = - if (in.token == DERIVES) { + if (isIdent(nme.derives)) { in.nextToken() tokenSeparated(COMMA, qualId) } @@ -2554,7 +2554,7 @@ object Parsers { */ def templateOpt(constr: DefDef, isEnum: Boolean = false): Template = { newLineOptWhenFollowedBy(LBRACE) - if (in.token == EXTENDS || in.token == DERIVES || in.token == LBRACE) + if (in.token == EXTENDS || isIdent(nme.derives) || in.token == LBRACE) template(constr, isEnum) else Template(constr, Nil, EmptyValDef, Nil) diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index e39c2ef2f7ed..1394c88766fe 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -205,8 +205,7 @@ object Scanners { private def handleMigration(keyword: Token): Token = if (!isScala2Mode) keyword else if ( keyword == ENUM - || keyword == ERASED - || keyword == DERIVES) treatAsIdent() + || keyword == ERASED) treatAsIdent() else keyword diff --git a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala index c8eb83d1483c..b9afcf03aa71 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Tokens.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Tokens.scala @@ -94,7 +94,6 @@ abstract class TokensCommon { //final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate //final val ENUM = 62; enter(ENUM, "enum") //final val ERASED = 63; enter(ERASED, "erased") - //final val DERIVES = 64; enter(DERIVES, "derives") /** special symbols */ final val COMMA = 70; enter(COMMA, "','") @@ -179,7 +178,6 @@ object Tokens extends TokensCommon { final val FORSOME = 61; enter(FORSOME, "forSome") // TODO: deprecate final val ENUM = 62; enter(ENUM, "enum") final val ERASED = 63; enter(ERASED, "erased") - final val DERIVES = 64; enter(DERIVES, "derives") /** special symbols */ final val NEWLINE = 78; enter(NEWLINE, "end of statement", "new line") @@ -200,7 +198,7 @@ object Tokens extends TokensCommon { /** XML mode */ final val XMLSTART = 96; enter(XMLSTART, "$XMLSTART$<") // TODO: deprecate - final val alphaKeywords: TokenSet = tokenRange(IF, DERIVES) + final val alphaKeywords: TokenSet = tokenRange(IF, ERASED) final val symbolicKeywords: TokenSet = tokenRange(USCORE, VIEWBOUND) final val symbolicTokens: TokenSet = tokenRange(COMMA, VIEWBOUND) final val keywords: TokenSet = alphaKeywords | symbolicKeywords From ae79a8a88165ce52df447a4713e8a43ef4bc40b0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 26 Nov 2018 19:11:04 +0100 Subject: [PATCH 04/56] Clarify regular and soft keywords keywords in syntax.md --- docs/docs/internals/syntax.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index ed07258e00da..e2a22dd53040 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -86,6 +86,23 @@ nl ::= “new line character” semi ::= ‘;’ | nl {nl} ``` +## Keywords + +### Regular keywords + +``` +abstract case catch class def do else enum +erased extends false final finally for if implicit +import lazy match new null object package private +protected override return super sealed then throw trait +true try type val var while with yield +: = <- => <: :> # @ +``` + +### Soft keywords + +derives inline opaque +~ * | & + - ## Context-free Syntax From fb09e828e2a9cd1320d036e3916555b08fbde52b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 28 Nov 2018 10:05:24 +0100 Subject: [PATCH 05/56] Represent derives clauses in Template trees with implementation of their desugaring --- .../src/dotty/tools/dotc/ast/Desugar.scala | 23 +- .../dotty/tools/dotc/ast/DesugarEnums.scala | 4 +- compiler/src/dotty/tools/dotc/ast/Trees.scala | 414 +++++++++--------- compiler/src/dotty/tools/dotc/ast/tpd.scala | 2 +- compiler/src/dotty/tools/dotc/ast/untpd.scala | 25 +- .../tools/dotc/core/tasty/TreeUnpickler.scala | 2 +- .../core/unpickleScala2/Scala2Unpickler.scala | 2 +- .../tools/dotc/parsing/JavaParsers.scala | 6 +- .../dotty/tools/dotc/parsing/Parsers.scala | 19 +- .../dotc/printing/DecompilerPrinter.scala | 2 +- .../tools/dotc/printing/RefinedPrinter.scala | 10 +- .../tools/dotc/transform/Constructors.scala | 4 +- .../tools/dotc/transform/MacroTransform.scala | 1 + .../tools/dotc/transform/MegaPhase.scala | 2 +- .../tools/dotc/transform/MoveStatics.scala | 2 +- .../src/dotty/tools/dotc/typer/Namer.scala | 38 +- .../src/dotty/tools/dotc/typer/Typer.scala | 6 +- .../src/dotty/tools/repl/ReplCompiler.scala | 5 +- .../tools/dotc/parsing/DeSugarTest.scala | 2 +- 19 files changed, 315 insertions(+), 254 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index f0221b47ad7b..5c17b164c7b4 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -297,7 +297,8 @@ object desugar { /** The expansion of a class definition. See inline comments for what is involved */ def classDef(cdef: TypeDef)(implicit ctx: Context): Tree = { val className = checkNotReservedName(cdef).asTypeName - val impl @ Template(constr0, parents, self, _) = cdef.rhs + val impl @ Template(_, _, self, _) = cdef.rhs + val parents = impl.parents val mods = cdef.mods val companionMods = mods .withFlags((mods.flags & (AccessFlags | Final)).toCommonFlags) @@ -312,7 +313,7 @@ object desugar { meth } - val constr1 = decompose(defDef(constr0, isPrimaryConstructor = true)) + val constr1 = decompose(defDef(impl.constr, isPrimaryConstructor = true)) // The original type and value parameters in the constructor already have the flags // needed to be type members (i.e. param, and possibly also private and local unless @@ -557,12 +558,16 @@ object desugar { } def eqInstances = if (isEnum) eqInstance :: Nil else Nil + // derived type classes of non-module classes go to their companions + val (clsDerived, companionDerived) = + if (mods.is(Module)) (impl.derived, Nil) else (Nil, impl.derived) + // The thicket which is the desugared version of the companion object - // synthetic object C extends parentTpt { defs } + // synthetic object C extends parentTpt derives class-derived { defs } def companionDefs(parentTpt: Tree, defs: List[Tree]) = moduleDef( ModuleDef( - className.toTermName, Template(emptyConstructor, parentTpt :: Nil, EmptyValDef, defs)) + className.toTermName, Template(emptyConstructor, parentTpt :: Nil, companionDerived, EmptyValDef, defs)) .withMods(companionMods | Synthetic)) .withSpan(cdef.span).toList @@ -613,10 +618,10 @@ object desugar { } companionDefs(companionParent, applyMeths ::: unapplyMeth :: companionMembers) } - else if (companionMembers.nonEmpty) + else if (companionMembers.nonEmpty || companionDerived.nonEmpty) companionDefs(anyRef, companionMembers) else if (isValueClass) { - constr0.vparamss match { + impl.constr.vparamss match { case (_ :: Nil) :: _ => companionDefs(anyRef, Nil) case _ => Nil // error will be emitted in typer } @@ -675,7 +680,7 @@ object desugar { } cpy.TypeDef(cdef: TypeDef)( name = className, - rhs = cpy.Template(impl)(constr, parents1, self1, + rhs = cpy.Template(impl)(constr, parents1, clsDerived, self1, tparamAccessors ::: vparamAccessors ::: normalizedBody ::: caseClassMeths)): TypeDef } @@ -772,7 +777,7 @@ object desugar { val localType = tdef.withMods(Modifiers(Synthetic | Opaque).withPrivateWithin(tdef.name)) val companions = moduleDef(ModuleDef( - moduleName, Template(emptyConstructor, Nil, EmptyValDef, localType :: Nil)) + moduleName, Template(emptyConstructor, Nil, Nil, EmptyValDef, localType :: Nil)) .withFlags(Synthetic | Opaque)) Thicket(aliasType :: companions.toList) } @@ -1335,7 +1340,7 @@ object desugar { val (classParents, self) = if (parentCores.length == 1 && (parent.tpe eq parentCores.head)) (untpdParent :: Nil, EmptyValDef) else (parentCores map TypeTree, ValDef(nme.WILDCARD, untpdParent, EmptyTree)) - val impl = Template(emptyConstructor, classParents, self, refinements) + val impl = Template(emptyConstructor, classParents, Nil, self, refinements) TypeDef(tpnme.REFINE_CLASS, impl).withFlags(Trait) } diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 6e280e892671..f909efff6ba3 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -115,7 +115,7 @@ object DesugarEnums { val toStringDef = DefDef(nme.toString_, Nil, Nil, TypeTree(), Ident(nme.name)) .withFlags(Override) - def creator = New(Template(emptyConstructor, enumClassRef :: Nil, EmptyValDef, + def creator = New(Template(emptyConstructor, enumClassRef :: Nil, Nil, EmptyValDef, List(enumTagDef, toStringDef) ++ registerCall)) DefDef(nme.DOLLAR_NEW, Nil, List(List(param(nme.tag, defn.IntType), param(nme.name, defn.StringType))), @@ -216,7 +216,7 @@ object DesugarEnums { if (!enumClass.exists) EmptyTree else if (enumClass.typeParams.nonEmpty) { val parent = interpolatedEnumParent(span) - val impl = Template(emptyConstructor, parent :: Nil, EmptyValDef, Nil) + val impl = Template(emptyConstructor, parent :: Nil, Nil, EmptyValDef, Nil) expandEnumModule(name, impl, mods, span) } else { diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index af9415dca57d..187399bdaaf6 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -739,16 +739,24 @@ object Trees { def isClassDef: Boolean = rhs.isInstanceOf[Template[_]] } - /** extends parents { self => body } */ - case class Template[-T >: Untyped] private[ast] (constr: DefDef[T], parents: List[Tree[T]], self: ValDef[T], private var preBody: LazyTreeList)(implicit @constructorOnly src: SourceFile) + /** extends parents { self => body } + * @param parentsOrDerived A list of parents followed by a list of derived classes, + * if this is of class untpd.DerivingTemplate. + * Typed templates only have parents. + */ + case class Template[-T >: Untyped] private[ast] (constr: DefDef[T], parentsOrDerived: List[Tree[T]], self: ValDef[T], private var preBody: LazyTreeList)(implicit @constructorOnly src: SourceFile) extends DefTree[T] with WithLazyField[List[Tree[T]]] { type ThisTree[-T >: Untyped] = Template[T] def unforcedBody: LazyTreeList = unforced def unforced: LazyTreeList = preBody protected def force(x: AnyRef): Unit = preBody = x def body(implicit ctx: Context): List[Tree[T]] = forceIfLazy + + def parents: List[Tree[T]] = parentsOrDerived // overridden by DerivingTemplate + def derived: List[untpd.Tree] = Nil // overridden by DerivingTemplate } + /** import expr.selectors * where a selector is either an untyped `Ident`, `name` or * an untyped thicket consisting of `name` and `rename`. @@ -1143,9 +1151,9 @@ object Trees { case tree: TypeDef if (name == tree.name) && (rhs eq tree.rhs) => tree case _ => finalize(tree, untpd.TypeDef(name, rhs)(tree.source)) } - def Template(tree: Tree)(constr: DefDef, parents: List[Tree], self: ValDef, body: LazyTreeList)(implicit ctx: Context): Template = tree match { - case tree: Template if (constr eq tree.constr) && (parents eq tree.parents) && (self eq tree.self) && (body eq tree.unforcedBody) => tree - case _ => finalize(tree, untpd.Template(constr, parents, self, body)(tree.source)) + def Template(tree: Tree)(constr: DefDef, parents: List[Tree], derived: List[untpd.Tree], self: ValDef, body: LazyTreeList)(implicit ctx: Context): Template = tree match { + case tree: Template if (constr eq tree.constr) && (parents eq tree.parents) && (derived eq tree.derived) && (self eq tree.self) && (body eq tree.unforcedBody) => tree + case tree => finalize(tree, untpd.Template(constr, parents, derived, self, body)(tree.source)) } def Import(tree: Tree)(expr: Tree, selectors: List[untpd.Tree])(implicit ctx: Context): Import = tree match { case tree: Import if (expr eq tree.expr) && (selectors eq tree.selectors) => tree @@ -1182,8 +1190,8 @@ object Trees { DefDef(tree: Tree)(name, tparams, vparamss, tpt, rhs) def TypeDef(tree: TypeDef)(name: TypeName = tree.name, rhs: Tree = tree.rhs)(implicit ctx: Context): TypeDef = TypeDef(tree: Tree)(name, rhs) - def Template(tree: Template)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody)(implicit ctx: Context): Template = - Template(tree: Tree)(constr, parents, self, body) + def Template(tree: Template)(constr: DefDef = tree.constr, parents: List[Tree] = tree.parents, derived: List[untpd.Tree] = tree.derived, self: ValDef = tree.self, body: LazyTreeList = tree.unforcedBody)(implicit ctx: Context): Template = + Template(tree: Tree)(constr, parents, derived, self, body) } /** Hook to indicate that a transform of some subtree should be skipped */ @@ -1206,106 +1214,106 @@ object Trees { def localCtx = if (tree.hasType && tree.symbol.exists) ctx.withOwner(tree.symbol) else ctx - if (skipTransform(tree)) tree - else tree match { - case Ident(name) => - tree - case Select(qualifier, name) => - cpy.Select(tree)(transform(qualifier), name) - case This(qual) => - tree - case Super(qual, mix) => - cpy.Super(tree)(transform(qual), mix) - case Apply(fun, args) => - cpy.Apply(tree)(transform(fun), transform(args)) - case TypeApply(fun, args) => - cpy.TypeApply(tree)(transform(fun), transform(args)) - case Literal(const) => - tree - case New(tpt) => - cpy.New(tree)(transform(tpt)) - case Typed(expr, tpt) => - cpy.Typed(tree)(transform(expr), transform(tpt)) - case NamedArg(name, arg) => - cpy.NamedArg(tree)(name, transform(arg)) - case Assign(lhs, rhs) => - cpy.Assign(tree)(transform(lhs), transform(rhs)) - case Block(stats, expr) => - cpy.Block(tree)(transformStats(stats), transform(expr)) - case If(cond, thenp, elsep) => - cpy.If(tree)(transform(cond), transform(thenp), transform(elsep)) - case Closure(env, meth, tpt) => - cpy.Closure(tree)(transform(env), transform(meth), transform(tpt)) - case Match(selector, cases) => - cpy.Match(tree)(transform(selector), transformSub(cases)) - case CaseDef(pat, guard, body) => - cpy.CaseDef(tree)(transform(pat), transform(guard), transform(body)) - case Labeled(bind, expr) => - cpy.Labeled(tree)(transformSub(bind), transform(expr)) - case Return(expr, from) => - cpy.Return(tree)(transform(expr), transformSub(from)) - case WhileDo(cond, body) => - cpy.WhileDo(tree)(transform(cond), transform(body)) - case Try(block, cases, finalizer) => - cpy.Try(tree)(transform(block), transformSub(cases), transform(finalizer)) - case SeqLiteral(elems, elemtpt) => - cpy.SeqLiteral(tree)(transform(elems), transform(elemtpt)) - case Inlined(call, bindings, expansion) => - cpy.Inlined(tree)(call, transformSub(bindings), transform(expansion)(inlineContext(call))) - case TypeTree() => - tree - case SingletonTypeTree(ref) => - cpy.SingletonTypeTree(tree)(transform(ref)) - case AndTypeTree(left, right) => - cpy.AndTypeTree(tree)(transform(left), transform(right)) - case OrTypeTree(left, right) => - cpy.OrTypeTree(tree)(transform(left), transform(right)) - case RefinedTypeTree(tpt, refinements) => - cpy.RefinedTypeTree(tree)(transform(tpt), transformSub(refinements)) - case AppliedTypeTree(tpt, args) => - cpy.AppliedTypeTree(tree)(transform(tpt), transform(args)) - case LambdaTypeTree(tparams, body) => - implicit val ctx = localCtx - cpy.LambdaTypeTree(tree)(transformSub(tparams), transform(body)) - case MatchTypeTree(bound, selector, cases) => - cpy.MatchTypeTree(tree)(transform(bound), transform(selector), transformSub(cases)) - case ByNameTypeTree(result) => - cpy.ByNameTypeTree(tree)(transform(result)) - case TypeBoundsTree(lo, hi) => - cpy.TypeBoundsTree(tree)(transform(lo), transform(hi)) - case Bind(name, body) => - cpy.Bind(tree)(name, transform(body)) - case Alternative(trees) => - cpy.Alternative(tree)(transform(trees)) - case UnApply(fun, implicits, patterns) => - cpy.UnApply(tree)(transform(fun), transform(implicits), transform(patterns)) - case EmptyValDef => - tree - case tree @ ValDef(name, tpt, _) => - implicit val ctx = localCtx - val tpt1 = transform(tpt) - val rhs1 = transform(tree.rhs) - cpy.ValDef(tree)(name, tpt1, rhs1) - case tree @ DefDef(name, tparams, vparamss, tpt, _) => - implicit val ctx = localCtx - cpy.DefDef(tree)(name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt), transform(tree.rhs)) - case tree @ TypeDef(name, rhs) => - implicit val ctx = localCtx - cpy.TypeDef(tree)(name, transform(rhs)) - case tree @ Template(constr, parents, self, _) => - cpy.Template(tree)(transformSub(constr), transform(parents), transformSub(self), transformStats(tree.body)) - case Import(expr, selectors) => - cpy.Import(tree)(transform(expr), selectors) - case PackageDef(pid, stats) => - cpy.PackageDef(tree)(transformSub(pid), transformStats(stats)(localCtx)) - case Annotated(arg, annot) => - cpy.Annotated(tree)(transform(arg), transform(annot)) - case Thicket(trees) => - val trees1 = transform(trees) - if (trees1 eq trees) tree else Thicket(trees1) - case _ => - transformMoreCases(tree) - } + if (skipTransform(tree)) tree + else tree match { + case Ident(name) => + tree + case Select(qualifier, name) => + cpy.Select(tree)(transform(qualifier), name) + case This(qual) => + tree + case Super(qual, mix) => + cpy.Super(tree)(transform(qual), mix) + case Apply(fun, args) => + cpy.Apply(tree)(transform(fun), transform(args)) + case TypeApply(fun, args) => + cpy.TypeApply(tree)(transform(fun), transform(args)) + case Literal(const) => + tree + case New(tpt) => + cpy.New(tree)(transform(tpt)) + case Typed(expr, tpt) => + cpy.Typed(tree)(transform(expr), transform(tpt)) + case NamedArg(name, arg) => + cpy.NamedArg(tree)(name, transform(arg)) + case Assign(lhs, rhs) => + cpy.Assign(tree)(transform(lhs), transform(rhs)) + case Block(stats, expr) => + cpy.Block(tree)(transformStats(stats), transform(expr)) + case If(cond, thenp, elsep) => + cpy.If(tree)(transform(cond), transform(thenp), transform(elsep)) + case Closure(env, meth, tpt) => + cpy.Closure(tree)(transform(env), transform(meth), transform(tpt)) + case Match(selector, cases) => + cpy.Match(tree)(transform(selector), transformSub(cases)) + case CaseDef(pat, guard, body) => + cpy.CaseDef(tree)(transform(pat), transform(guard), transform(body)) + case Labeled(bind, expr) => + cpy.Labeled(tree)(transformSub(bind), transform(expr)) + case Return(expr, from) => + cpy.Return(tree)(transform(expr), transformSub(from)) + case WhileDo(cond, body) => + cpy.WhileDo(tree)(transform(cond), transform(body)) + case Try(block, cases, finalizer) => + cpy.Try(tree)(transform(block), transformSub(cases), transform(finalizer)) + case SeqLiteral(elems, elemtpt) => + cpy.SeqLiteral(tree)(transform(elems), transform(elemtpt)) + case Inlined(call, bindings, expansion) => + cpy.Inlined(tree)(call, transformSub(bindings), transform(expansion)(inlineContext(call))) + case TypeTree() => + tree + case SingletonTypeTree(ref) => + cpy.SingletonTypeTree(tree)(transform(ref)) + case AndTypeTree(left, right) => + cpy.AndTypeTree(tree)(transform(left), transform(right)) + case OrTypeTree(left, right) => + cpy.OrTypeTree(tree)(transform(left), transform(right)) + case RefinedTypeTree(tpt, refinements) => + cpy.RefinedTypeTree(tree)(transform(tpt), transformSub(refinements)) + case AppliedTypeTree(tpt, args) => + cpy.AppliedTypeTree(tree)(transform(tpt), transform(args)) + case LambdaTypeTree(tparams, body) => + implicit val ctx = localCtx + cpy.LambdaTypeTree(tree)(transformSub(tparams), transform(body)) + case MatchTypeTree(bound, selector, cases) => + cpy.MatchTypeTree(tree)(transform(bound), transform(selector), transformSub(cases)) + case ByNameTypeTree(result) => + cpy.ByNameTypeTree(tree)(transform(result)) + case TypeBoundsTree(lo, hi) => + cpy.TypeBoundsTree(tree)(transform(lo), transform(hi)) + case Bind(name, body) => + cpy.Bind(tree)(name, transform(body)) + case Alternative(trees) => + cpy.Alternative(tree)(transform(trees)) + case UnApply(fun, implicits, patterns) => + cpy.UnApply(tree)(transform(fun), transform(implicits), transform(patterns)) + case EmptyValDef => + tree + case tree @ ValDef(name, tpt, _) => + implicit val ctx = localCtx + val tpt1 = transform(tpt) + val rhs1 = transform(tree.rhs) + cpy.ValDef(tree)(name, tpt1, rhs1) + case tree @ DefDef(name, tparams, vparamss, tpt, _) => + implicit val ctx = localCtx + cpy.DefDef(tree)(name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt), transform(tree.rhs)) + case tree @ TypeDef(name, rhs) => + implicit val ctx = localCtx + cpy.TypeDef(tree)(name, transform(rhs)) + case tree @ Template(constr, parents, self, _) if tree.derived.isEmpty => + cpy.Template(tree)(transformSub(constr), transform(tree.parents), Nil, transformSub(self), transformStats(tree.body)) + case Import(expr, selectors) => + cpy.Import(tree)(transform(expr), selectors) + case PackageDef(pid, stats) => + cpy.PackageDef(tree)(transformSub(pid), transformStats(stats)(localCtx)) + case Annotated(arg, annot) => + cpy.Annotated(tree)(transform(arg), transform(annot)) + case Thicket(trees) => + val trees1 = transform(trees) + if (trees1 eq trees) tree else Thicket(trees1) + case _ => + transformMoreCases(tree) + } } def transformStats(trees: List[Tree])(implicit ctx: Context): List[Tree] = @@ -1333,102 +1341,102 @@ object Trees { foldOver(x, tree)(ctx.withSource(tree.source)) else { Stats.record(s"TreeAccumulator.foldOver/$getClass") - def localCtx = - if (tree.hasType && tree.symbol.exists) ctx.withOwner(tree.symbol) else ctx - tree match { - case Ident(name) => - x - case Select(qualifier, name) => - this(x, qualifier) - case This(qual) => - x - case Super(qual, mix) => - this(x, qual) - case Apply(fun, args) => - this(this(x, fun), args) - case TypeApply(fun, args) => - this(this(x, fun), args) - case Literal(const) => - x - case New(tpt) => - this(x, tpt) - case Typed(expr, tpt) => - this(this(x, expr), tpt) - case NamedArg(name, arg) => - this(x, arg) - case Assign(lhs, rhs) => - this(this(x, lhs), rhs) - case Block(stats, expr) => - this(this(x, stats), expr) - case If(cond, thenp, elsep) => - this(this(this(x, cond), thenp), elsep) - case Closure(env, meth, tpt) => - this(this(this(x, env), meth), tpt) - case Match(selector, cases) => - this(this(x, selector), cases) - case CaseDef(pat, guard, body) => - this(this(this(x, pat), guard), body) - case Labeled(bind, expr) => - this(this(x, bind), expr) - case Return(expr, from) => - this(this(x, expr), from) - case WhileDo(cond, body) => - this(this(x, cond), body) - case Try(block, handler, finalizer) => - this(this(this(x, block), handler), finalizer) - case SeqLiteral(elems, elemtpt) => - this(this(x, elems), elemtpt) - case Inlined(call, bindings, expansion) => - this(this(x, bindings), expansion)(inlineContext(call)) - case TypeTree() => - x - case SingletonTypeTree(ref) => - this(x, ref) - case AndTypeTree(left, right) => - this(this(x, left), right) - case OrTypeTree(left, right) => - this(this(x, left), right) - case RefinedTypeTree(tpt, refinements) => - this(this(x, tpt), refinements) - case AppliedTypeTree(tpt, args) => - this(this(x, tpt), args) - case LambdaTypeTree(tparams, body) => - implicit val ctx = localCtx - this(this(x, tparams), body) - case MatchTypeTree(bound, selector, cases) => - this(this(this(x, bound), selector), cases) - case ByNameTypeTree(result) => - this(x, result) - case TypeBoundsTree(lo, hi) => - this(this(x, lo), hi) - case Bind(name, body) => - this(x, body) - case Alternative(trees) => - this(x, trees) - case UnApply(fun, implicits, patterns) => - this(this(this(x, fun), implicits), patterns) - case tree @ ValDef(name, tpt, _) => - implicit val ctx = localCtx - this(this(x, tpt), tree.rhs) - case tree @ DefDef(name, tparams, vparamss, tpt, _) => - implicit val ctx = localCtx - this(this((this(x, tparams) /: vparamss)(apply), tpt), tree.rhs) - case TypeDef(name, rhs) => - implicit val ctx = localCtx - this(x, rhs) - case tree @ Template(constr, parents, self, _) => - this(this(this(this(x, constr), parents), self), tree.body) - case Import(expr, selectors) => - this(x, expr) - case PackageDef(pid, stats) => - this(this(x, pid), stats)(localCtx) - case Annotated(arg, annot) => - this(this(x, arg), annot) - case Thicket(ts) => - this(x, ts) - case _ => - foldMoreCases(x, tree) - } + def localCtx = + if (tree.hasType && tree.symbol.exists) ctx.withOwner(tree.symbol) else ctx + tree match { + case Ident(name) => + x + case Select(qualifier, name) => + this(x, qualifier) + case This(qual) => + x + case Super(qual, mix) => + this(x, qual) + case Apply(fun, args) => + this(this(x, fun), args) + case TypeApply(fun, args) => + this(this(x, fun), args) + case Literal(const) => + x + case New(tpt) => + this(x, tpt) + case Typed(expr, tpt) => + this(this(x, expr), tpt) + case NamedArg(name, arg) => + this(x, arg) + case Assign(lhs, rhs) => + this(this(x, lhs), rhs) + case Block(stats, expr) => + this(this(x, stats), expr) + case If(cond, thenp, elsep) => + this(this(this(x, cond), thenp), elsep) + case Closure(env, meth, tpt) => + this(this(this(x, env), meth), tpt) + case Match(selector, cases) => + this(this(x, selector), cases) + case CaseDef(pat, guard, body) => + this(this(this(x, pat), guard), body) + case Labeled(bind, expr) => + this(this(x, bind), expr) + case Return(expr, from) => + this(this(x, expr), from) + case WhileDo(cond, body) => + this(this(x, cond), body) + case Try(block, handler, finalizer) => + this(this(this(x, block), handler), finalizer) + case SeqLiteral(elems, elemtpt) => + this(this(x, elems), elemtpt) + case Inlined(call, bindings, expansion) => + this(this(x, bindings), expansion)(inlineContext(call)) + case TypeTree() => + x + case SingletonTypeTree(ref) => + this(x, ref) + case AndTypeTree(left, right) => + this(this(x, left), right) + case OrTypeTree(left, right) => + this(this(x, left), right) + case RefinedTypeTree(tpt, refinements) => + this(this(x, tpt), refinements) + case AppliedTypeTree(tpt, args) => + this(this(x, tpt), args) + case LambdaTypeTree(tparams, body) => + implicit val ctx = localCtx + this(this(x, tparams), body) + case MatchTypeTree(bound, selector, cases) => + this(this(this(x, bound), selector), cases) + case ByNameTypeTree(result) => + this(x, result) + case TypeBoundsTree(lo, hi) => + this(this(x, lo), hi) + case Bind(name, body) => + this(x, body) + case Alternative(trees) => + this(x, trees) + case UnApply(fun, implicits, patterns) => + this(this(this(x, fun), implicits), patterns) + case tree @ ValDef(name, tpt, _) => + implicit val ctx = localCtx + this(this(x, tpt), tree.rhs) + case tree @ DefDef(name, tparams, vparamss, tpt, _) => + implicit val ctx = localCtx + this(this((this(x, tparams) /: vparamss)(apply), tpt), tree.rhs) + case TypeDef(name, rhs) => + implicit val ctx = localCtx + this(x, rhs) + case tree @ Template(constr, parents, self, _) if tree.derived.isEmpty => + this(this(this(this(x, constr), parents), self), tree.body) + case Import(expr, selectors) => + this(x, expr) + case PackageDef(pid, stats) => + this(this(x, pid), stats)(localCtx) + case Annotated(arg, annot) => + this(this(x, arg), annot) + case Thicket(ts) => + this(x, ts) + case _ => + foldMoreCases(x, tree) + } } def foldMoreCases(x: X, tree: Tree)(implicit ctx: Context): X = { diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index 4bec4ee635d8..4edaee7b7e40 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -293,7 +293,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { val findLocalDummy = new FindLocalDummyAccumulator(cls) val localDummy = ((NoSymbol: Symbol) /: body)(findLocalDummy.apply) .orElse(ctx.newLocalDummy(cls)) - val impl = untpd.Template(constr, parents, selfType, newTypeParams ++ body) + val impl = untpd.Template(constr, parents, Nil, selfType, newTypeParams ++ body) .withType(localDummy.termRef) ta.assignType(untpd.TypeDef(cls.name, impl), cls) } diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index 26d95104a1ac..ddb6f6ab121b 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -39,7 +39,19 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def withName(name: Name)(implicit ctx: Context): ModuleDef = cpy.ModuleDef(this)(name.toTermName, impl) } - case class ParsedTry(expr: Tree, handler: Tree, finalizer: Tree)(implicit @constructorOnly src: SourceFile) extends Tree with TermTree + /** An untyped template with a derives clause. Derived parents are added to the end + * of the `parents` list. `derivedCount` keeps track of how many there are. + * This representation was chosen because it balances two concerns: + * - maximize overlap between DerivingTemplate and Template for code streamlining + * - keep invariant that elements of untyped trees align with source positions + */ + class DerivingTemplate(constr: DefDef, parentsOrDerived: List[Tree], self: ValDef, preBody: LazyTreeList, derivedCount: Int)(implicit @constructorOnly src: SourceFile) + extends Template(constr, parentsOrDerived, self, preBody) { + override val parents = parentsOrDerived.dropRight(derivedCount) + override val derived = parentsOrDerived.takeRight(derivedCount) + } + + case class ParsedTry(expr: Tree, handler: Tree, finalizer: Tree)(implicit @constructorOnly src: SourceFile) extends TermTree case class SymbolLit(str: String)(implicit @constructorOnly src: SourceFile) extends TermTree @@ -303,7 +315,9 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def ValDef(name: TermName, tpt: Tree, rhs: LazyTree)(implicit src: SourceFile): ValDef = new ValDef(name, tpt, rhs) def DefDef(name: TermName, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: LazyTree)(implicit src: SourceFile): DefDef = new DefDef(name, tparams, vparamss, tpt, rhs) def TypeDef(name: TypeName, rhs: Tree)(implicit src: SourceFile): TypeDef = new TypeDef(name, rhs) - def Template(constr: DefDef, parents: List[Tree], self: ValDef, body: LazyTreeList)(implicit src: SourceFile): Template = new Template(constr, parents, self, body) + def Template(constr: DefDef, parents: List[Tree], self: ValDef, body: LazyTreeList)(implicit src: SourceFile): Template = + if (derived.isEmpty) new Template(constr, parents, self, body) + else new DerivingTemplate(constr, parents ++ derived, self, body, derived.length) def Import(expr: Tree, selectors: List[Tree])(implicit src: SourceFile): Import = new Import(expr, selectors) def PackageDef(pid: RefTree, stats: List[Tree])(implicit src: SourceFile): PackageDef = new PackageDef(pid, stats) def Annotated(arg: Tree, annot: Tree)(implicit src: SourceFile): Annotated = new Annotated(arg, annot) @@ -431,8 +445,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { case _ => finalize(tree, untpd.ModuleDef(name, impl)(tree.source)) } def ParsedTry(tree: Tree)(expr: Tree, handler: Tree, finalizer: Tree)(implicit ctx: Context): TermTree = tree match { - case tree: ParsedTry - if (expr eq tree.expr) && (handler eq tree.handler) && (finalizer eq tree.finalizer) => tree + case tree: ParsedTry if (expr eq tree.expr) && (handler eq tree.handler) && (finalizer eq tree.finalizer) => tree case _ => finalize(tree, untpd.ParsedTry(expr, handler, finalizer)(tree.source)) } def SymbolLit(tree: Tree)(str: String)(implicit ctx: Context): TermTree = tree match { @@ -513,6 +526,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { override def transformMoreCases(tree: Tree)(implicit ctx: Context): Tree = tree match { case ModuleDef(name, impl) => cpy.ModuleDef(tree)(name, transformSub(impl)) + case tree: DerivingTemplate => + cpy.Template(tree)(transformSub(tree.constr), transform(tree.parents), transform(tree.derived), transformSub(tree.self), transformStats(tree.body)) case ParsedTry(expr, handler, finalizer) => cpy.ParsedTry(tree)(transform(expr), transform(handler), transform(finalizer)) case SymbolLit(str) => @@ -560,6 +575,8 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { override def foldMoreCases(x: X, tree: Tree)(implicit ctx: Context): X = tree match { case ModuleDef(name, impl) => this(x, impl) + case tree: DerivingTemplate => + this(this(this(this(this(x, tree.constr), tree.parents), tree.derived), tree.self), tree.body) case ParsedTry(expr, handler, finalizer) => this(this(this(x, expr), handler), finalizer) case SymbolLit(str) => diff --git a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala index f90ff942ff61..381ac0839e19 100644 --- a/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/tasty/TreeUnpickler.scala @@ -912,7 +912,7 @@ class TreeUnpickler(reader: TastyReader, tparams ++ vparams ++ stats }) setSpan(start, - untpd.Template(constr, mappedParents, self, lazyStats) + untpd.Template(constr, mappedParents, Nil, self, lazyStats) .withType(localDummy.termRef)) } diff --git a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index fd02618c3111..ed0c57340cac 100644 --- a/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/compiler/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -1057,7 +1057,7 @@ class Scala2Unpickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClas val parents = times(readNat(), () => readTreeRef()) val self = readValDefRef() val body = until(end, () => readTreeRef()) - untpd.Template(???, parents, self, body) // !!! TODO: pull out primary constructor + untpd.Template(???, parents, Nil, self, body) // !!! TODO: pull out primary constructor .withType(symbol.namedType) case BLOCKtree => diff --git a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala index d1ad08e251ff..88fcedee0677 100644 --- a/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala @@ -125,7 +125,7 @@ object JavaParsers { stats1 = constr1 :: stats1 constr1 = makeConstructor(List(scalaDot(tpnme.Unit)), tparams, Flags.JavaDefined | Flags.PrivateLocal) } - Template(constr1.asInstanceOf[DefDef], parents, EmptyValDef, stats1) + Template(constr1.asInstanceOf[DefDef], parents, Nil, EmptyValDef, stats1) } def makeSyntheticParam(count: Int, tpt: Tree): ValDef = @@ -623,8 +623,8 @@ object JavaParsers { else { val template = cdef.rhs.asInstanceOf[Template] cpy.TypeDef(cdef)(cdef.name, - cpy.Template(template)(template.constr, template.parents, template.self, - importCompanionObject(cdef) :: template.body)).withMods(cdef.mods) + cpy.Template(template)(body = importCompanionObject(cdef) :: template.body)) + .withMods(cdef.mods) } List(makeCompanionObject(cdefNew, statics), cdefNew) diff --git a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala index 3be24beee17a..04ddc52c2e47 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Parsers.scala @@ -649,8 +649,7 @@ object Parsers { /** QualId ::= id {`.' id} */ - val qualId: () => Tree = - () => dotSelectors(termIdent()) + def qualId(): Tree = dotSelectors(termIdent()) /** SimpleExpr ::= literal * | symbol @@ -1504,7 +1503,7 @@ object Parsers { case parent :: Nil if in.token != LBRACE => reposition(if (parent.isType) ensureApplied(wrapNew(parent)) else parent) case _ => - New(reposition(templateBodyOpt(emptyConstructor, parents, isEnum = false))) + New(reposition(templateBodyOpt(emptyConstructor, parents, Nil, isEnum = false))) } } @@ -2486,7 +2485,7 @@ object Parsers { tokenSeparated(WITH, constrApp) } else Nil - Template(constr, parents, EmptyValDef, Nil) + Template(constr, parents, Nil, EmptyValDef, Nil) } /* -------- TEMPLATES ------------------------------------------- */ @@ -2534,7 +2533,7 @@ object Parsers { val derived = if (isIdent(nme.derives)) { in.nextToken() - tokenSeparated(COMMA, qualId) + tokenSeparated(COMMA, () => convertToTypeId(qualId())) } else Nil (extended, derived) @@ -2543,11 +2542,11 @@ object Parsers { /** Template ::= InheritClauses [TemplateBody] */ def template(constr: DefDef, isEnum: Boolean = false): Template = { - val (parents, deriveds) = inheritClauses() + val (parents, derived) = inheritClauses() newLineOptWhenFollowedBy(LBRACE) if (isEnum && in.token != LBRACE) syntaxErrorOrIncomplete(ExpectedTokenButFound(LBRACE, in.token)) - templateBodyOpt(constr, parents, isEnum) + templateBodyOpt(constr, parents, derived, isEnum) } /** TemplateOpt = [Template] @@ -2557,15 +2556,15 @@ object Parsers { if (in.token == EXTENDS || isIdent(nme.derives) || in.token == LBRACE) template(constr, isEnum) else - Template(constr, Nil, EmptyValDef, Nil) + Template(constr, Nil, Nil, EmptyValDef, Nil) } /** TemplateBody ::= [nl] `{' TemplateStatSeq `}' */ - def templateBodyOpt(constr: DefDef, parents: List[Tree], isEnum: Boolean): Template = { + def templateBodyOpt(constr: DefDef, parents: List[Tree], derived: List[Tree], isEnum: Boolean): Template = { val (self, stats) = if (in.token == LBRACE) withinEnum(isEnum)(templateBody()) else (EmptyValDef, Nil) - Template(constr, parents, self, stats) + Template(constr, parents, derived, self, stats) } def templateBody(): (ValDef, List[Tree]) = { diff --git a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala index 9630bdc98c87..7b51af00b378 100644 --- a/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/DecompilerPrinter.scala @@ -69,7 +69,7 @@ class DecompilerPrinter(_ctx: Context) extends RefinedPrinter(_ctx) { val bodyText = " {" ~~ toTextGlobal(impl.body, "\n") ~ "}" parentsText.provided(parents.nonEmpty) ~ bodyText } - else super.toTextTemplate(impl.copy(parents = parents, preBody = body), ofNew) + else super.toTextTemplate(impl.copy(parentsOrDerived = parents, preBody = body), ofNew) } override protected def typeApplyText[T >: Untyped](tree: TypeApply[T]): Text = { diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 5344e738e79c..ed90e482a9c2 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -700,7 +700,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { } protected def toTextTemplate(impl: Template, ofNew: Boolean = false): Text = { - val Template(constr @ DefDef(_, tparams, vparamss, _, _), parents, self, _) = impl + val Template(constr @ DefDef(_, tparams, vparamss, _, _), _, self, _) = impl val tparamsTxt = withEnclosingDef(constr) { tparamsText(tparams) } val primaryConstrs = if (constr.rhs.isEmpty) Nil else constr :: Nil val prefix: Text = @@ -711,7 +711,8 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { if (constr.mods.hasAnnotations && !constr.mods.hasFlags) modsText = modsText ~~ " this" withEnclosingDef(constr) { addVparamssText(tparamsTxt ~~ modsText, vparamss) } } - val parentsText = Text(parents map constrText, keywordStr(" with ")) + val parentsText = Text(impl.parents.map(constrText), if (ofNew) keywordStr(" with ") else ", ") + val derivedText = Text(impl.derived.map(toText(_)), ", ") val selfText = { val selfName = if (self.name == nme.WILDCARD) keywordStr("this") else self.name.toString (selfName ~ optText(self.tpt)(": " ~ _) ~ " =>").close @@ -729,7 +730,10 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val bodyText = " {" ~~ selfText ~~ toTextGlobal(primaryConstrs ::: body, "\n") ~ "}" - prefix ~ keywordText(" extends").provided(!ofNew && parents.nonEmpty) ~~ parentsText ~ bodyText + prefix ~ + keywordText(" extends").provided(!ofNew && impl.parents.nonEmpty) ~~ parentsText ~ + keywordText(" derives").provided(impl.derived.nonEmpty) ~~ derivedText ~ + bodyText } protected def templateText(tree: TypeDef, impl: Template): Text = { diff --git a/compiler/src/dotty/tools/dotc/transform/Constructors.scala b/compiler/src/dotty/tools/dotc/transform/Constructors.scala index 93045ba44686..f8448696f1b0 100644 --- a/compiler/src/dotty/tools/dotc/transform/Constructors.scala +++ b/compiler/src/dotty/tools/dotc/transform/Constructors.scala @@ -281,8 +281,6 @@ class Constructors extends MiniPhase with IdentityDenotTransformer { thisPhase = } else cpy.DefDef(constr)(rhs = Block(finalConstrStats, unitLiteral)) - cpy.Template(tree)( - constr = expandedConstr, - body = clsStats.toList) + cpy.Template(tree)(constr = expandedConstr, body = clsStats.toList) } } diff --git a/compiler/src/dotty/tools/dotc/transform/MacroTransform.scala b/compiler/src/dotty/tools/dotc/transform/MacroTransform.scala index 042e400bc3d0..f0053b3abfe9 100644 --- a/compiler/src/dotty/tools/dotc/transform/MacroTransform.scala +++ b/compiler/src/dotty/tools/dotc/transform/MacroTransform.scala @@ -56,6 +56,7 @@ abstract class MacroTransform extends Phase { cpy.Template(tree)( transformSub(constr), transform(parents)(ctx.superCallContext), + Nil, transformSelf(self), transformStats(impl.body, tree.symbol)) case _ => diff --git a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala index 58e88b54acef..c42e6e1b7132 100644 --- a/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala +++ b/compiler/src/dotty/tools/dotc/transform/MegaPhase.scala @@ -337,7 +337,7 @@ class MegaPhase(val miniPhases: Array[MiniPhase]) extends Phase { val parents = transformTrees(tree.parents, start)(ctx.superCallContext) val self = transformSpecificTree(tree.self, start) val body = transformStats(tree.body, tree.symbol, start) - goTemplate(cpy.Template(tree)(constr, parents, self, body), start) + goTemplate(cpy.Template(tree)(constr, parents, Nil, self, body), start) case tree: Match => implicit val ctx = prepMatch(tree, start)(outerCtx) val selector = transformTree(tree.selector, start) diff --git a/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala b/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala index 8b06551fb1d4..059ab19165cf 100644 --- a/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala +++ b/compiler/src/dotty/tools/dotc/transform/MoveStatics.scala @@ -54,7 +54,7 @@ class MoveStatics extends MiniPhase with SymTransformer { } else newBody val oldTemplate = orig.rhs.asInstanceOf[Template] - cpy.TypeDef(orig)(rhs = cpy.Template(orig.rhs)(oldTemplate.constr, oldTemplate.parents, oldTemplate.self, newBodyWithStaticConstr)) + cpy.TypeDef(orig)(rhs = cpy.Template(oldTemplate)(body = newBodyWithStaticConstr)) } def move(module: TypeDef, companion: TypeDef): List[Tree] = { diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index c107b091d4b5..e6936536454c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -531,14 +531,20 @@ class Namer { typer: Typer => to.putAttachment(References, fromRefs ++ toRefs) } - /** Merge the module class `modCls` in the expanded tree of `mdef` with the given stats */ - def mergeModuleClass(mdef: Tree, modCls: TypeDef, stats: List[Tree]): TypeDef = { + /** Merge the module class `modCls` in the expanded tree of `mdef` with the + * body and derived clause of the syntehtic module class `fromCls`. + */ + def mergeModuleClass(mdef: Tree, modCls: TypeDef, fromCls: TypeDef): TypeDef = { var res: TypeDef = null val Thicket(trees) = expanded(mdef) val merged = trees.map { tree => if (tree == modCls) { - val impl = modCls.rhs.asInstanceOf[Template] - res = cpy.TypeDef(modCls)(rhs = cpy.Template(impl)(body = stats ++ impl.body)) + val fromTempl = fromCls.rhs.asInstanceOf[Template] + val modTempl = modCls.rhs.asInstanceOf[Template] + res = cpy.TypeDef(modCls)( + rhs = cpy.Template(modTempl)( + derived = fromTempl.derived ++ modTempl.derived, + body = fromTempl.body ++ modTempl.body)) res } else tree @@ -559,7 +565,7 @@ class Namer { typer: Typer => def mergeIfSynthetic(fromStat: Tree, fromCls: TypeDef, toStat: Tree, toCls: TypeDef): Unit = if (fromCls.mods.is(Synthetic) && !toCls.mods.is(Synthetic)) { removeInExpanded(fromStat, fromCls) - val mcls = mergeModuleClass(toStat, toCls, fromCls.rhs.asInstanceOf[Template].body) + val mcls = mergeModuleClass(toStat, toCls, fromCls) mcls.setMods(toCls.mods | (fromCls.mods.flags & RetainedSyntheticCompanionFlags)) moduleClsDef(fromCls.name) = (toStat, mcls) } @@ -838,7 +844,7 @@ class Namer { typer: Typer => protected implicit val ctx: Context = localContext(cls).setMode(ictx.mode &~ Mode.InSuperCall) - val TypeDef(name, impl @ Template(constr, parents, self, _)) = original + val TypeDef(name, impl @ Template(constr, _, self, _)) = original private val (params, rest): (List[Tree], List[Tree]) = impl.body.span { case td: TypeDef => td.mods is Param @@ -850,6 +856,7 @@ class Namer { typer: Typer => /** The type signature of a ClassDef with given symbol */ override def completeInCreationContext(denot: SymDenotation): Unit = { + val parents = impl.parents /* The type of a parent constructor. Types constructor arguments * only if parent type contains uninstantiated type parameters. @@ -909,6 +916,23 @@ class Namer { typer: Typer => } } + /* Check derived type tree `derived` for the following well-formedness conditions: + * (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix) + * (2) It must have exactly one type parameter + * If it passes the checks, enter a typeclass instance for it in the class scope. + */ + def addDerivedInstance(derived: untpd.Tree): Unit = { + val uncheckedType = typedAheadType(derived, AnyTypeConstructorProto).tpe.dealiasKeepAnnots + val derivedType = checkClassType(uncheckedType, derived.pos, traitReq = false, stablePrefixReq = true) + val nparams = derivedType.typeSymbol.typeParams.length + if (nparams == 1) + println(i"todo: add derived instance $derived") + else + ctx.error( + i"derived class $derivedType should have one type paramater but has $nparams", + derived.pos) + } + addAnnotations(denot.symbol) val selfInfo: TypeOrSymbol = @@ -939,6 +963,8 @@ class Namer { typer: Typer => ensureFirstIsClass(parents.map(checkedParentType(_)), cls.span)) typr.println(i"completing $denot, parents = $parents%, %, parentTypes = $parentTypes%, %") + impl.derived.foreach(addDerivedInstance) + val finalSelfInfo: TypeOrSymbol = if (cls.isOpaqueCompanion) { // The self type of an opaque companion is refined with the type-alias of the original opaque type diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 0b726256c0b3..15d8a595af9a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -1556,7 +1556,8 @@ class Typer extends Namer def typedClassDef(cdef: untpd.TypeDef, cls: ClassSymbol)(implicit ctx: Context): Tree = track("typedClassDef") { if (!cls.info.isInstanceOf[ClassInfo]) return EmptyTree.assertingErrorsReported - val TypeDef(name, impl @ Template(constr, parents, self, _)) = cdef + val TypeDef(name, impl @ Template(constr, _, self, _)) = cdef + val parents = impl.parents val superCtx = ctx.superCallContext /** If `ref` is an implicitly parameterized trait, pass an implicit argument list. @@ -1625,6 +1626,7 @@ class Typer extends Namer val constr1 = typed(constr).asInstanceOf[DefDef] val parentsWithClass = ensureFirstTreeIsClass(parents mapconserve typedParent, cdef.nameSpan) val parents1 = ensureConstrCall(cls, parentsWithClass)(superCtx) + var self1 = typed(self)(ctx.outer).asInstanceOf[ValDef] // outer context where class members are not visible if (cls.isOpaqueCompanion && !ctx.isAfterTyper) { // this is necessary to ensure selftype is correctly pickled @@ -1639,7 +1641,7 @@ class Typer extends Namer typedStats(impl.body, dummy)(ctx.inClassContext(self1.symbol))) checkNoDoubleDeclaration(cls) - val impl1 = cpy.Template(impl)(constr1, parents1, self1, body1) + val impl1 = cpy.Template(impl)(constr1, parents1, Nil, self1, body1) .withType(dummy.termRef) if (!cls.is(AbstractOrTrait) && !ctx.isAfterTyper) checkRealizableBounds(cls, cdef.sourcePos.withSpan(cdef.nameSpan)) diff --git a/compiler/src/dotty/tools/repl/ReplCompiler.scala b/compiler/src/dotty/tools/repl/ReplCompiler.scala index 6093f91d277e..61d7c244fb70 100644 --- a/compiler/src/dotty/tools/repl/ReplCompiler.scala +++ b/compiler/src/dotty/tools/repl/ReplCompiler.scala @@ -130,7 +130,7 @@ class ReplCompiler extends Compiler { implicit val ctx: Context = defs.state.context - val tmpl = Template(emptyConstructor, Nil, EmptyValDef, defs.stats) + val tmpl = Template(emptyConstructor, Nil, Nil, EmptyValDef, defs.stats) val module = ModuleDef(objectTermName, tmpl) .withSpan(Span(0, defs.stats.last.span.end)) @@ -229,8 +229,9 @@ class ReplCompiler extends Compiler { def wrapped(expr: String, sourceFile: SourceFile, state: State)(implicit ctx: Context): Result[untpd.PackageDef] = { def wrap(trees: List[untpd.Tree]): untpd.PackageDef = { import untpd._ + val valdef = ValDef("expr".toTermName, TypeTree(), Block(trees, unitLiteral).withSpan(Span(0, expr.length))) - val tmpl = Template(emptyConstructor, Nil, EmptyValDef, List(valdef)) + val tmpl = Template(emptyConstructor, Nil, Nil, EmptyValDef, List(valdef)) val wrapper = TypeDef("$wrapper".toTypeName, tmpl) .withMods(Modifiers(Final)) .withSpan(Span(0, expr.length)) diff --git a/compiler/test/dotty/tools/dotc/parsing/DeSugarTest.scala b/compiler/test/dotty/tools/dotc/parsing/DeSugarTest.scala index 4624b39d3e5e..e8c9412a49c4 100644 --- a/compiler/test/dotty/tools/dotc/parsing/DeSugarTest.scala +++ b/compiler/test/dotty/tools/dotc/parsing/DeSugarTest.scala @@ -64,7 +64,7 @@ class DeSugarTest extends ParserTest { case tree1 @ TypeDef(name, rhs) => cpy.TypeDef(tree1)(name, transform(rhs, Type)) case impl @ Template(constr, parents, self, _) => - cpy.Template(tree1)(transformSub(constr), transform(parents), transformSub(self), transform(impl.body, Expr)) + cpy.Template(tree1)(transformSub(constr), transform(parents), Nil, transformSub(self), transform(impl.body, Expr)) case Thicket(trees) => Thicket(flatten(trees mapConserve super.transform)) case tree1 => From c8357b2efd6e30bad7acaabb989036550d9c2185 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 29 Nov 2018 15:38:50 +0100 Subject: [PATCH 06/56] Make Case a FromStartFlag --- compiler/src/dotty/tools/dotc/core/Flags.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index 26a71b41747b..1f834c79ee54 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -417,6 +417,9 @@ object Flags { final val Scala2ExistentialCommon: FlagSet = commonFlag(55, "") final val Scala2Existential: FlagSet = Scala2ExistentialCommon.toTypeFlags + /** Children were queried on this class */ + final val ChildrenQueried = typeFlag(56, "") + /** A module variable (Scala 2.x only) */ final val Scala2ModuleVar: FlagSet = termFlag(57, "") @@ -476,7 +479,7 @@ object Flags { /** Flags that are not (re)set when completing the denotation */ final val FromStartFlags: FlagSet = - Module | Package | Deferred | Method.toCommonFlags | + Module | Package | Deferred | Method.toCommonFlags | Case | HigherKinded.toCommonFlags | Param | ParamAccessor.toCommonFlags | Scala2ExistentialCommon | MutableOrOpaque | Touched | JavaStatic | CovariantOrOuter | ContravariantOrLabel | CaseAccessor.toCommonFlags | From b3c48d3bd1f504bd6a101bd503f45ef4503bbfaf Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 29 Nov 2018 15:40:11 +0100 Subject: [PATCH 07/56] Reorganize children handling - set Child annotations during completion instead of in PostTyper - report an error if children are added after a `children` query. The purpose of these changes is to make it possible to query children safely during typer. --- .../tools/dotc/transform/PostTyper.scala | 1 - .../dotty/tools/dotc/transform/SymUtils.scala | 25 ++---------- .../src/dotty/tools/dotc/typer/Namer.scala | 40 ++++++++++++++++++- 3 files changed, 42 insertions(+), 24 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 979ccfa70584..41d131eda79f 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -105,7 +105,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase private def processMemberDef(tree: Tree)(implicit ctx: Context): tree.type = { val sym = tree.symbol - sym.registerIfChild() sym.transformAnnotations(transformAnnot) sym.defTree = tree tree diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index 630233484ad4..f836975e0132 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -122,27 +122,6 @@ class SymUtils(val self: Symbol) extends AnyVal { self } - /** If this symbol is an enum value or a named class, register it as a child - * in all direct parent classes which are sealed. - */ - def registerIfChild()(implicit ctx: Context): Unit = { - def register(child: Symbol, parent: Type) = { - val cls = parent.classSymbol - if (cls.is(Sealed)) { - if ((child.isInaccessibleChildOf(cls) || child.isAnonymousClass) && !self.hasAnonymousChild) - cls.addAnnotation(Annotation.Child(cls)) - else cls.addAnnotation(Annotation.Child(child)) - } - } - if (self.isClass && !self.isEnumAnonymClass) - self.asClass.classParents.foreach { parent => - val child = if (self.is(Module)) self.sourceModule else self - register(child, parent) - } - else if (self.is(CaseVal, butNot = Method | Module)) - register(self, self.info) - } - /** Does this symbol refer to anonymous classes synthesized by enum desugaring? */ def isEnumAnonymClass(implicit ctx: Context): Boolean = self.isAnonymousClass && (self.owner.name.eq(nme.DOLLAR_NEW) || self.owner.is(CaseVal)) @@ -155,10 +134,12 @@ class SymUtils(val self: Symbol) extends AnyVal { self.isLocal && !cls.topLevelClass.isLinkedWith(self.topLevelClass) /** If this is a sealed class, its known children */ - def children(implicit ctx: Context): List[Symbol] = + def children(implicit ctx: Context): List[Symbol] = { + if (self.isType) self.setFlag(ChildrenQueried) self.annotations.collect { case Annotation.Child(child) => child } + } def hasAnonymousChild(implicit ctx: Context): Boolean = children.exists(_ `eq` self) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index e6936536454c..91a7938f5726 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -18,6 +18,8 @@ import config.Printers.typr import Annotations._ import Inferencing._ import transform.ValueClasses._ +import transform.TypeUtils._ +import transform.SymUtils._ import reporting.diagnostic.messages._ trait NamerContextOps { this: Context => @@ -745,7 +747,10 @@ class Namer { typer: Typer => assert(ctx.mode.is(Mode.Interactive), s"completing $denot in wrong run ${ctx.runId}, was created in ${creationContext.runId}") denot.info = UnspecifiedErrorType } - else completeInCreationContext(denot) + else { + completeInCreationContext(denot) + if (denot.isCompleted) registerIfChild(denot) + } } protected def addAnnotations(sym: Symbol): Unit = original match { @@ -793,6 +798,39 @@ class Namer { typer: Typer => } } + /** If completed symbol is an enum value or a named class, register it as a child + * in all direct parent classes which are sealed. + */ + def registerIfChild(denot: SymDenotation)(implicit ctx: Context): Unit = { + val sym = denot.symbol + + def register(child: Symbol, parent: Type) = { + val cls = parent.classSymbol + if (cls.is(Sealed)) { + if ((child.isInaccessibleChildOf(cls) || child.isAnonymousClass) && !sym.hasAnonymousChild) { + cls.addAnnotation(Annotation.Child(cls)) + } + else { + if (cls.is(ChildrenQueried)) + ctx.error(em"""children of ${cls} were already queried before $sym was discovered. + |As a remedy, you could move $sym on the same nesting level as $cls.""") + else { + //println(i"child $child of $cls") + cls.addAnnotation(Annotation.Child(child)) + } + } + } + } + + if (denot.isClass && !sym.isEnumAnonymClass && !sym.isRefinementClass) + denot.asClass.classParents.foreach { parent => + val child = if (denot.is(Module)) denot.sourceModule else denot.symbol + register(child, parent) + } + else if (denot.is(CaseVal, butNot = Method | Module)) + register(denot.symbol, denot.info) + } + /** Intentionally left without `implicit ctx` parameter. We need * to pick up the context at the point where the completer was created. */ From fe9f40ae2383992bc2cae745c0f120b9151ec7f4 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 29 Nov 2018 18:49:09 +0100 Subject: [PATCH 08/56] Improve error diagnostics for implicits not found More work was needed to exactly pinpoint what implicit was missing. --- .../tools/dotc/core/OrderingConstraint.scala | 2 + .../dotty/tools/dotc/core/TyperState.scala | 2 +- .../tools/dotc/printing/RefinedPrinter.scala | 2 +- .../dotty/tools/dotc/typer/Implicits.scala | 36 ++- tests/neg/typeclass-derivation2.scala | 270 ++++++++++++++++++ 5 files changed, 306 insertions(+), 6 deletions(-) create mode 100644 tests/neg/typeclass-derivation2.scala diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index c86ce2a271bc..b163028115ca 100644 --- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -102,6 +102,8 @@ object OrderingConstraint { newConstraint(c.boundsMap, c.lowerMap, c.upperMap.updated(poly, entries)) def initial = Nil } + + val empty = new OrderingConstraint(SimpleIdentityMap.Empty, SimpleIdentityMap.Empty, SimpleIdentityMap.Empty) } import OrderingConstraint._ diff --git a/compiler/src/dotty/tools/dotc/core/TyperState.scala b/compiler/src/dotty/tools/dotc/core/TyperState.scala index 83efc1329350..cfdd88489d49 100644 --- a/compiler/src/dotty/tools/dotc/core/TyperState.scala +++ b/compiler/src/dotty/tools/dotc/core/TyperState.scala @@ -30,7 +30,7 @@ class TyperState(previous: TyperState /* | Null */) { def setReporter(reporter: Reporter): this.type = { myReporter = reporter; this } private[this] var myConstraint: Constraint = - if (previous == null) new OrderingConstraint(SimpleIdentityMap.Empty, SimpleIdentityMap.Empty, SimpleIdentityMap.Empty) + if (previous == null) OrderingConstraint.empty else previous.constraint def constraint: Constraint = myConstraint diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index ed90e482a9c2..8fb3f98531e2 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -303,7 +303,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { case id: Trees.SearchFailureIdent[_] => tree.typeOpt match { case reason: Implicits.SearchFailureType => - toText(id.name) ~ "implicitly[" ~ toText(reason.expectedType) ~ "]" + toText(id.name) ~ "implicitly[" ~ toText(reason.clarify(reason.expectedType)) ~ "]" case _ => toText(id.name) } diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 44dcfdd15f3e..f9463690872e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -360,10 +360,13 @@ object Implicits { def expectedType: Type protected def argument: Tree + /** A "massaging" function for displayed types to give better info in error diagnostics */ + def clarify(tp: Type)(implicit ctx: Context): Type = tp + final protected def qualify(implicit ctx: Context): String = if (expectedType.exists) - if (argument.isEmpty) em"match type $expectedType" - else em"convert from ${argument.tpe} to $expectedType" + if (argument.isEmpty) em"match type ${clarify(expectedType)}" + else em"convert from ${argument.tpe} to ${clarify(expectedType)}" else if (argument.isEmpty) em"match expected type" else em"convert from ${argument.tpe} to expected type" @@ -379,12 +382,35 @@ object Implicits { def whyNoConversion(implicit ctx: Context): String = "" } - class NoMatchingImplicits(val expectedType: Type, val argument: Tree) extends SearchFailureType { + class NoMatchingImplicits(val expectedType: Type, val argument: Tree, constraint: Constraint = OrderingConstraint.empty) extends SearchFailureType { + + /** Replace all type parameters in constraint by their bounds, to make it clearer + * what was expected + */ + override def clarify(tp: Type)(implicit ctx: Context) = { + val map = new TypeMap { + def apply(t: Type): Type = t match { + case t: TypeParamRef => + constraint.entry(t) match { + case NoType => t + case bounds: TypeBounds => constraint.fullBounds(t) + case t1 => t1 + } + case t: TypeVar => + t.instanceOpt.orElse(apply(t.origin)) + case _ => + mapOver(t) + } + } + map(tp) + } + def explanation(implicit ctx: Context): String = em"no implicit values were found that $qualify" + override def toString = s"NoMatchingImplicits($expectedType, $argument)" } - @sharable object NoMatchingImplicits extends NoMatchingImplicits(NoType, EmptyTree) + @sharable object NoMatchingImplicits extends NoMatchingImplicits(NoType, EmptyTree, OrderingConstraint.empty) @sharable val NoMatchingImplicitsFailure: SearchFailure = SearchFailure(NoMatchingImplicits)(NoSource) @@ -916,6 +942,8 @@ trait Implicits { self: Typer => } } else result + case NoMatchingImplicitsFailure => + SearchFailure(new NoMatchingImplicits(pt, argument, ctx.typerState.constraint)) case _ => result0 } diff --git a/tests/neg/typeclass-derivation2.scala b/tests/neg/typeclass-derivation2.scala new file mode 100644 index 000000000000..514f56d2f103 --- /dev/null +++ b/tests/neg/typeclass-derivation2.scala @@ -0,0 +1,270 @@ +import scala.collection.mutable +import scala.annotation.tailrec + +trait Deriving { + import Deriving._ + + /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ + def mirror(ordinal: Int, product: Product): Mirror = + new Mirror(this, ordinal, product) + + /** A mirror with elements given as an array */ + def mirror(ordinal: Int, elems: Array[AnyRef]): Mirror = + mirror(ordinal, new ArrayProduct(elems)) + + /** A mirror with an initial empty array of `numElems` elements, to be filled in. */ + def mirror(ordinal: Int, numElems: Int): Mirror = + mirror(ordinal, new Array[AnyRef](numElems)) + + /** A mirror of a case with no elements */ + def mirror(ordinal: Int): Mirror = + mirror(ordinal, EmptyProduct) + + /** The case and element labels of the described ADT as encoded strings. */ + protected def caseLabels: Array[String] + + private final val separator = '\000' + + private def label(ordinal: Int, idx: Int): String = { + val labels = caseLabels(ordinal) + @tailrec def separatorPos(from: Int): Int = + if (from == labels.length || labels(from) == separator) from + else separatorPos(from + 1) + @tailrec def findLabel(count: Int, idx: Int): String = + if (idx == labels.length) "" + else if (count == 0) labels.substring(idx, separatorPos(idx)) + else findLabel(if (labels(idx) == separator) count - 1 else count, idx + 1) + findLabel(idx, 0) + } +} + +// Generic deriving infrastructure +object Deriving { + + /** A generic representation of a case in an ADT + * @param deriving The companion object of the ADT + * @param ordinal The ordinal value of the case in the list of the ADT's cases + * @param elems The elements of the case + */ + class Mirror(val deriving: Deriving, val ordinal: Int, val elems: Product) { + + /** The `n`'th element of this generic case */ + def apply(n: Int): Any = elems.productElement(n) + + /** The name of the constructor of the case reflected by this mirror */ + def caseLabel: String = deriving.label(ordinal, 0) + + /** The label of the `n`'th element of the case reflected by this mirror */ + def elementLabel(n: Int) = deriving.label(ordinal, n + 1) + } + + /** A class for mapping between an ADT value and + * the case mirror that represents the value. + */ + abstract class Reflected[T] { + + /** The case mirror corresponding to ADT instance `x` */ + def reflect(x: T): Mirror + + /** The ADT instance corresponding to given `mirror` */ + def reify(mirror: Mirror): T + + /** The companion object of the ADT */ + def deriving: Deriving + } + + /** The shape of an ADT. + * This is eithe a product (`Case`) or a sum (`Cases`) of products. + */ + enum Shape { + + /** A sum with alternative types `Alts` */ + case Cases[Alts <: Tuple] + + /** A product type `T` with element types `Elems` */ + case Case[T, Elems <: Tuple] + } + + /** Every generic derivation starts with a typeclass instance of this type. + * It informs that type `T` has shape `S` and also implements runtime reflection on `T`. + */ + abstract class Shaped[T, S <: Shape] extends Reflected[T] + + /** Helper class to turn arrays into products */ + private class ArrayProduct(val elems: Array[AnyRef]) extends Product { + def canEqual(that: Any): Boolean = true + def productElement(n: Int) = elems(n) + def productArity = elems.length + override def productIterator: Iterator[Any] = elems.iterator + def update(n: Int, x: Any) = elems(n) = x.asInstanceOf[AnyRef] + } + + /** Helper object */ + private object EmptyProduct extends Product { + def canEqual(that: Any): Boolean = true + def productElement(n: Int) = throw new IndexOutOfBoundsException + def productArity = 0 + } +} + +// An algebraic datatype +enum Lst[+T] { + case Cons(hd: T, tl: Lst[T]) + case Nil +} + +object Lst extends Deriving { + // common compiler-generated infrastructure + import Deriving._ + + type Shape[T] = Shape.Cases[( + Shape.Case[Cons[T], (T, Lst[T])], + Shape.Case[Nil.type, Unit] + )] + + val NilMirror = mirror(1) + + implicit def lstShape[T]: Shaped[Lst[T], Shape[T]] = new { + def reflect(xs: Lst[T]): Mirror = xs match { + case xs: Cons[T] => mirror(0, xs) + case Nil => NilMirror + } + def reify(c: Mirror): Lst[T] = c.ordinal match { + case 0 => Cons[T](c(0).asInstanceOf, c(1).asInstanceOf) + case 1 => Nil + } + def deriving = Lst + } + + protected val caseLabels = Array("Cons\000hd\000tl", "Nil") + + // three clauses that could be generated from a `derives` clause + implicit def LstShow[T: Show]: Show[Lst[T]] = Show.derived +} + +// A simple product type +case class Pair[T](x: T, y: T) + +object Pair extends Deriving { + // common compiler-generated infrastructure + import Deriving._ + + type Shape[T] = Shape.Case[Pair[T], (T, T)] + + implicit def pairShape[T]: Shaped[Pair[T], Shape[T]] = new { + def reflect(xy: Pair[T]) = + mirror(0, xy) + def reify(c: Mirror): Pair[T] = + Pair(c(0).asInstanceOf, c(1).asInstanceOf) + def deriving = Pair + } + + protected val caseLabels = Array("Pair\000x\000y") +} + +sealed trait Either[+L, +R] extends Product derives Eq, Pickler +case class Left[L](x: L) extends Either[L, Nothing] +case class Right[R](x: R) extends Either[Nothing, R] + +object Either extends Deriving { + import Deriving._ + + type Shape[L, R] = Shape.Cases[( + Shape.Case[Left[L], L *: Unit], + Shape.Case[Right[R], R *: Unit] + )] + + implicit def eitherShape[L, R]: Shaped[Either[L, R], Shape[L, R]] = new { + def reflect(e: Either[L, R]): Mirror = e match { + case e: Left[L] => mirror(0, e) + case e: Right[R] => mirror(1, e) + } + def reify(c: Mirror): Either[L, R] = c.ordinal match { + case 0 => Left[L](c(0).asInstanceOf) + case 1 => Right[R](c(0).asInstanceOf) + } + def deriving = Either + } + + protected val caseLabels = Array("Left\000x", "Right\000x") + + implicit def EitherShow[L: Show, R: Show]: Show[Either[L, R]] = Show.derived +} + +trait Show[T] { + def show(x: T): String +} +object Show { + import scala.typelevel._ + import Deriving._ + + inline def tryShow[T](x: T): String = implicit match { + case s: Show[T] => s.show(x) + } + + inline def showElems[Elems <: Tuple](elems: Mirror, n: Int): List[String] = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + val formal = elems.elementLabel(n) + val actual = tryShow[elem](elems(n).asInstanceOf) + s"$formal = $actual" :: showElems[elems1](elems, n + 1) + case _: Unit => + Nil + } + + inline def showCase[T, Elems <: Tuple](r: Reflected[T], x: T): String = { + val mirror = r.reflect(x) + val args = showElems[Elems](mirror, 0).mkString(", ") + s"${mirror.caseLabel}($args)" + } + + inline def showCases[T, Alts <: Tuple](r: Reflected[T], x: T): String = + inline erasedValue[Alts] match { + case _: (Shape.Case[alt, elems] *: alts1) => + x match { + case x: `alt` => showCase[T, elems](r, x) + case _ => showCases[T, alts1](r, x) + } + case _: Unit => + throw new MatchError(x) + } + + inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Show[T] = new { + def show(x: T): String = inline erasedValue[S] match { + case _: Shape.Cases[alts] => + showCases[T, alts](ev, x) + case _: Shape.Case[_, elems] => + showCase[T, elems](ev, x) + } + } + + implicit object IntShow extends Show[Int] { + def show(x: Int): String = x.toString + } +} + +// Tests +object Test extends App { + import Deriving._ + + def showPrintln[T: Show](x: T): Unit = + println(implicitly[Show[T]].show(x)) + + val zs = Lst.Cons(Left(1), Lst.Cons(Right(Pair(2, 3)), Lst.Nil)) + showPrintln(zs) // error +/* This should print as follows: +-- Error: typeclass-derivation2.scala:254:17 ----------------------------------- +254 | showPrintln(zs) // error + | ^ + |no implicit argument of type Show[Lst[Either[Int, Pair[Int]]]] was found for parameter evidence$5 of method showPrintln in object Test. + |I found: + | + | Lst.LstShow[T]( + | Either.EitherShow[Int, Pair[Int]](Show.IntShow, + | /* missing */implicitly[Show[Pair[Int]]] + | ) + | ) + | + |But no implicit values were found that match type Show[Pair[Int]]. +*/ +} \ No newline at end of file From 4642070c545b60e6734df46adcf9944c85ff707c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 29 Nov 2018 19:32:39 +0100 Subject: [PATCH 09/56] Flesh out test New data type: Either Test structures combining several types. --- .../src/dotty/tools/dotc/typer/Namer.scala | 17 ++---- tests/run/typeclass-derivation2.check | 2 + tests/run/typeclass-derivation2.scala | 55 ++++++++++++++++++- 3 files changed, 62 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 91a7938f5726..4e2c11c54dbc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -807,18 +807,13 @@ class Namer { typer: Typer => def register(child: Symbol, parent: Type) = { val cls = parent.classSymbol if (cls.is(Sealed)) { - if ((child.isInaccessibleChildOf(cls) || child.isAnonymousClass) && !sym.hasAnonymousChild) { + if ((child.isInaccessibleChildOf(cls) || child.isAnonymousClass) && !sym.hasAnonymousChild) cls.addAnnotation(Annotation.Child(cls)) - } - else { - if (cls.is(ChildrenQueried)) - ctx.error(em"""children of ${cls} were already queried before $sym was discovered. - |As a remedy, you could move $sym on the same nesting level as $cls.""") - else { - //println(i"child $child of $cls") - cls.addAnnotation(Annotation.Child(child)) - } - } + else if (!cls.is(ChildrenQueried)) + cls.addAnnotation(Annotation.Child(child)) + else + ctx.error(em"""children of ${cls} were already queried before $sym was discovered. + |As a remedy, you could move $sym on the same nesting level as $cls.""") } } diff --git a/tests/run/typeclass-derivation2.check b/tests/run/typeclass-derivation2.check index 4ec92ef1a1e0..fd3681599ffc 100644 --- a/tests/run/typeclass-derivation2.check +++ b/tests/run/typeclass-derivation2.check @@ -6,3 +6,5 @@ ListBuffer(1, 2) Pair(1,2) Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil()))) Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil()))), tl = Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Nil())), tl = Nil())) +Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil())) +Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil())) diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index 278a4babafbc..4a917924adf8 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -166,6 +166,38 @@ object Pair extends Deriving { // two clauses that could be generated from a `derives` clause implicit def PairEq[T: Eq]: Eq[Pair[T]] = Eq.derived implicit def PairPickler[T: Pickler]: Pickler[Pair[T]] = Pickler.derived + implicit def PairShow[T: Show]: Show[Pair[T]] = Show.derived +} + +sealed trait Either[+L, +R] extends Product derives Eq, Pickler +case class Left[L](x: L) extends Either[L, Nothing] +case class Right[R](x: R) extends Either[Nothing, R] + +object Either extends Deriving { + import Deriving._ + + type Shape[L, R] = Shape.Cases[( + Shape.Case[Left[L], L *: Unit], + Shape.Case[Right[R], R *: Unit] + )] + + implicit def eitherShape[L, R]: Shaped[Either[L, R], Shape[L, R]] = new { + def reflect(e: Either[L, R]): Mirror = e match { + case e: Left[L] => mirror(0, e) + case e: Right[R] => mirror(1, e) + } + def reify(c: Mirror): Either[L, R] = c.ordinal match { + case 0 => Left[L](c(0).asInstanceOf) + case 1 => Right[R](c(0).asInstanceOf) + } + def deriving = Either + } + + protected val caseLabels = Array("Left\000x", "Right\000x") + + implicit def EitherEq[L: Eq, R: Eq]: Eq[Either[L, R]] = Eq.derived + implicit def EitherPickler[L: Pickler, R: Pickler]: Pickler[Either[L, R]] = Pickler.derived + implicit def EitherShow[L: Show, R: Show]: Show[Either[L, R]] = Show.derived } // A typeclass @@ -174,7 +206,7 @@ trait Eq[T] { } object Eq { - import scala.typelevel._ + import _root_.scala.typelevel._ import Deriving._ inline def tryEql[T](x: T, y: T) = implicit match { @@ -423,4 +455,25 @@ object Test extends App { println(implicitly[Show[T]].show(x)) showPrintln(xs) showPrintln(xss) + + val zs = Lst.Cons(Left(1), Lst.Cons(Right(Pair(2, 3)), Lst.Nil)) + showPrintln(zs) + + def pickle[T: Pickler](buf: mutable.ListBuffer[Int], x: T): Unit = + implicitly[Pickler[T]].pickle(buf, x) + + def unpickle[T: Pickler](buf: mutable.ListBuffer[Int]): T = + implicitly[Pickler[T]].unpickle(buf) + + def copy[T: Pickler](x: T): T = { + val buf = new mutable.ListBuffer[Int] + pickle(buf, x) + unpickle[T](buf) + } + + def eql[T: Eq](x: T, y: T) = implicitly[Eq[T]].eql(x, y) + + val zs1 = copy(zs) + showPrintln(zs1) + assert(eql(zs, zs1)) } \ No newline at end of file From 1d588884e77b98ffaf409b06a865ca1539845307 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 29 Nov 2018 19:34:59 +0100 Subject: [PATCH 10/56] Fix tests --- compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala | 2 ++ tests/neg/typeclass-derivation2.scala | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index b163028115ca..90dd999e25eb 100644 --- a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala +++ b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala @@ -10,6 +10,7 @@ import printing.Texts._ import config.Config import reflect.ClassTag import annotation.tailrec +import annotation.internal.sharable object OrderingConstraint { @@ -103,6 +104,7 @@ object OrderingConstraint { def initial = Nil } + @sharable val empty = new OrderingConstraint(SimpleIdentityMap.Empty, SimpleIdentityMap.Empty, SimpleIdentityMap.Empty) } diff --git a/tests/neg/typeclass-derivation2.scala b/tests/neg/typeclass-derivation2.scala index 514f56d2f103..90c409e7922e 100644 --- a/tests/neg/typeclass-derivation2.scala +++ b/tests/neg/typeclass-derivation2.scala @@ -162,7 +162,7 @@ object Pair extends Deriving { protected val caseLabels = Array("Pair\000x\000y") } -sealed trait Either[+L, +R] extends Product derives Eq, Pickler +sealed trait Either[+L, +R] extends Product case class Left[L](x: L) extends Either[L, Nothing] case class Right[R](x: R) extends Either[Nothing, R] @@ -254,7 +254,7 @@ object Test extends App { showPrintln(zs) // error /* This should print as follows: -- Error: typeclass-derivation2.scala:254:17 ----------------------------------- -254 | showPrintln(zs) // error +254 | showPrintln(zs) | ^ |no implicit argument of type Show[Lst[Either[Int, Pair[Int]]]] was found for parameter evidence$5 of method showPrintln in object Test. |I found: From 7eda566285c7f5fb1008515f7e749bc40c274fc2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 30 Nov 2018 13:42:57 +0100 Subject: [PATCH 11/56] Deriving infrastructure in typelevel --- .../dotty/tools/dotc/core/Definitions.scala | 10 + .../src/dotty/tools/dotc/core/StdNames.scala | 1 + .../scala/typelevel/package.scala | 2 - .../src-scala3/scala/typelevel/Mirror.scala | 18 ++ .../scala/typelevel/Reflected.scala | 16 ++ .../scala/typelevel/ReflectedClass.scala | 57 ++++++ .../src-scala3/scala/typelevel/Shape.scala | 16 ++ .../src-scala3/scala/typelevel/Shaped.scala | 6 + tests/run/typeclass-derivation2.scala | 180 +++++++++--------- 9 files changed, 216 insertions(+), 90 deletions(-) create mode 100644 library/src-scala3/scala/typelevel/Mirror.scala create mode 100644 library/src-scala3/scala/typelevel/Reflected.scala create mode 100644 library/src-scala3/scala/typelevel/ReflectedClass.scala create mode 100644 library/src-scala3/scala/typelevel/Shape.scala create mode 100644 library/src-scala3/scala/typelevel/Shaped.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index e44b02107266..719afc73d228 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -648,6 +648,16 @@ class Definitions { def Product_productElement(implicit ctx: Context): Symbol = Product_productElementR.symbol lazy val Product_productPrefixR: TermRef = ProductClass.requiredMethodRef(nme.productPrefix) def Product_productPrefix(implicit ctx: Context): Symbol = Product_productPrefixR.symbol + + lazy val ShapedType: TypeRef = ctx.requiredClassRef("scala.typelevel.Shaped") + def ShapedClass(implicit ctx: Context): ClassSymbol = ShapedType.symbol.asClass + lazy val ShapeType: TypeRef = ctx.requiredClassRef("scala.typelevel.Shape") + def ShapeClass(implicit ctx: Context): ClassSymbol = ShapeType.symbol.asClass + lazy val ShapeCaseType: TypeRef = ctx.requiredClassRef("scala.typelevel.Shape.Case") + def ShapeCaseClass(implicit ctx: Context): ClassSymbol = ShapeCaseType.symbol.asClass + lazy val ShapeCasesType: TypeRef = ctx.requiredClassRef("scala.typelevel.Shape.Cases") + def ShapeCasesClass(implicit ctx: Context): ClassSymbol = ShapeCasesType.symbol.asClass + lazy val LanguageModuleRef: TermSymbol = ctx.requiredModule("scala.language") def LanguageModuleClass(implicit ctx: Context): ClassSymbol = LanguageModuleRef.moduleClass.asClass lazy val NonLocalReturnControlType: TypeRef = ctx.requiredClassRef("scala.runtime.NonLocalReturnControl") diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index c449c9979198..f7d3471cd82a 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -347,6 +347,7 @@ object StdNames { val RootClass: N = "RootClass" val Scala2: N = "Scala2" val Select: N = "Select" + val Shape: N = "Shape" val StringContext: N = "StringContext" val This: N = "This" val ThisType: N = "ThisType" diff --git a/library/src-bootstrapped/scala/typelevel/package.scala b/library/src-bootstrapped/scala/typelevel/package.scala index 6b083dd81b6b..c48cc40ca0fe 100644 --- a/library/src-bootstrapped/scala/typelevel/package.scala +++ b/library/src-bootstrapped/scala/typelevel/package.scala @@ -4,8 +4,6 @@ package object typelevel { erased def erasedValue[T]: T = ??? - case class Typed[T](val value: T) { type Type = T } - inline def error(inline msg: String, objs: Any*): Nothing = ??? inline def constValueOpt[T]: Option[T] = ??? diff --git a/library/src-scala3/scala/typelevel/Mirror.scala b/library/src-scala3/scala/typelevel/Mirror.scala new file mode 100644 index 000000000000..44b29c48010b --- /dev/null +++ b/library/src-scala3/scala/typelevel/Mirror.scala @@ -0,0 +1,18 @@ +package scala.typelevel + +/** A generic representation of a case in an ADT + * @param deriving The companion object of the ADT + * @param ordinal The ordinal value of the case in the list of the ADT's cases + * @param elems The elements of the case + */ +class Mirror(val reflected: ReflectedClass, val ordinal: Int, val elems: Product) { + + /** The `n`'th element of this generic case */ + def apply(n: Int): Any = elems.productElement(n) + + /** The name of the constructor of the case reflected by this mirror */ + def caseLabel: String = reflected.label(ordinal, 0) + + /** The label of the `n`'th element of the case reflected by this mirror */ + def elementLabel(n: Int) = reflected.label(ordinal, n + 1) +} diff --git a/library/src-scala3/scala/typelevel/Reflected.scala b/library/src-scala3/scala/typelevel/Reflected.scala new file mode 100644 index 000000000000..8febfe69ad80 --- /dev/null +++ b/library/src-scala3/scala/typelevel/Reflected.scala @@ -0,0 +1,16 @@ +package scala.typelevel + +/** A class for mapping between an ADT value and + * the case mirror that represents the value. + */ +abstract class Reflected[T] { + + /** The case mirror corresponding to ADT instance `x` */ + def reflect(x: T): Mirror + + /** The ADT instance corresponding to given `mirror` */ + def reify(mirror: Mirror): T + + /** The companion object of the ADT */ + def common: ReflectedClass +} diff --git a/library/src-scala3/scala/typelevel/ReflectedClass.scala b/library/src-scala3/scala/typelevel/ReflectedClass.scala new file mode 100644 index 000000000000..0fc896ff678e --- /dev/null +++ b/library/src-scala3/scala/typelevel/ReflectedClass.scala @@ -0,0 +1,57 @@ +package scala.typelevel +import annotation.tailrec + +/** @param caseLabels The case and element labels of the described ADT as encoded strings. +*/ +class ReflectedClass(caseLabels: Array[String]) { + import ReflectedClass._ + + /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ + def mirror(ordinal: Int, product: Product): Mirror = + new Mirror(this, ordinal, product) + + /** A mirror with elements given as an array */ + def mirror(ordinal: Int, elems: Array[AnyRef]): Mirror = + mirror(ordinal, new ArrayProduct(elems)) + + /** A mirror with an initial empty array of `numElems` elements, to be filled in. */ + def mirror(ordinal: Int, numElems: Int): Mirror = + mirror(ordinal, new Array[AnyRef](numElems)) + + /** A mirror of a case with no elements */ + def mirror(ordinal: Int): Mirror = + mirror(ordinal, EmptyProduct) + + private[typelevel] def label(ordinal: Int, idx: Int): String = { + val labels = caseLabels(ordinal) + @tailrec def separatorPos(from: Int): Int = + if (from == labels.length || labels(from) == separator) from + else separatorPos(from + 1) + @tailrec def findLabel(count: Int, idx: Int): String = + if (idx == labels.length) "" + else if (count == 0) labels.substring(idx, separatorPos(idx)) + else findLabel(if (labels(idx) == separator) count - 1 else count, idx + 1) + findLabel(idx, 0) + } +} + +object ReflectedClass { + + private final val separator = '\000' + + /** Helper class to turn arrays into products */ + private class ArrayProduct(val elems: Array[AnyRef]) extends Product { + def canEqual(that: Any): Boolean = true + def productElement(n: Int) = elems(n) + def productArity = elems.length + override def productIterator: Iterator[Any] = elems.iterator + def update(n: Int, x: Any) = elems(n) = x.asInstanceOf[AnyRef] + } + + /** Helper object */ + private object EmptyProduct extends Product { + def canEqual(that: Any): Boolean = true + def productElement(n: Int) = throw new IndexOutOfBoundsException + def productArity = 0 + } +} \ No newline at end of file diff --git a/library/src-scala3/scala/typelevel/Shape.scala b/library/src-scala3/scala/typelevel/Shape.scala new file mode 100644 index 000000000000..cd1f4596479a --- /dev/null +++ b/library/src-scala3/scala/typelevel/Shape.scala @@ -0,0 +1,16 @@ +package scala.typelevel + +/** The shape of an ADT. + * This is eithe a product (`Case`) or a sum (`Cases`) of products. + */ +sealed abstract class Shape + +object Shape { + + /** A sum with alternative types `Alts` */ + case class Cases[Alts <: Tuple] extends Shape + + /** A product type `T` with element types `Elems` */ + case class Case[T, Elems <: Tuple] extends Shape +} + diff --git a/library/src-scala3/scala/typelevel/Shaped.scala b/library/src-scala3/scala/typelevel/Shaped.scala new file mode 100644 index 000000000000..1e0b6c1913ab --- /dev/null +++ b/library/src-scala3/scala/typelevel/Shaped.scala @@ -0,0 +1,6 @@ +package scala.typelevel + +/** Every generic derivation starts with a typeclass instance of this type. + * It informs that type `T` has shape `S` and also implements runtime reflection on `T`. + */ +abstract class Shaped[T, S <: Shape] extends Reflected[T] diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index 4a917924adf8..b939b953111b 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -1,66 +1,81 @@ import scala.collection.mutable import scala.annotation.tailrec -trait Deriving { - import Deriving._ - - /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ - def mirror(ordinal: Int, product: Product): Mirror = - new Mirror(this, ordinal, product) - - /** A mirror with elements given as an array */ - def mirror(ordinal: Int, elems: Array[AnyRef]): Mirror = - mirror(ordinal, new ArrayProduct(elems)) - - /** A mirror with an initial empty array of `numElems` elements, to be filled in. */ - def mirror(ordinal: Int, numElems: Int): Mirror = - mirror(ordinal, new Array[AnyRef](numElems)) - - /** A mirror of a case with no elements */ - def mirror(ordinal: Int): Mirror = - mirror(ordinal, EmptyProduct) - - /** The case and element labels of the described ADT as encoded strings. */ - protected def caseLabels: Array[String] - - private final val separator = '\000' - - private def label(ordinal: Int, idx: Int): String = { - val labels = caseLabels(ordinal) - @tailrec def separatorPos(from: Int): Int = - if (from == labels.length || labels(from) == separator) from - else separatorPos(from + 1) - @tailrec def findLabel(count: Int, idx: Int): String = - if (idx == labels.length) "" - else if (count == 0) labels.substring(idx, separatorPos(idx)) - else findLabel(if (labels(idx) == separator) count - 1 else count, idx + 1) - findLabel(idx, 0) +object TypeLevel { + /** @param caseLabels The case and element labels of the described ADT as encoded strings. + */ + class ReflectedClass(caseLabels: Array[String]) { + import ReflectedClass._ + + /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ + def mirror(ordinal: Int, product: Product): Mirror = + new Mirror(this, ordinal, product) + + /** A mirror with elements given as an array */ + def mirror(ordinal: Int, elems: Array[AnyRef]): Mirror = + mirror(ordinal, new ArrayProduct(elems)) + + /** A mirror with an initial empty array of `numElems` elements, to be filled in. */ + def mirror(ordinal: Int, numElems: Int): Mirror = + mirror(ordinal, new Array[AnyRef](numElems)) + + /** A mirror of a case with no elements */ + def mirror(ordinal: Int): Mirror = + mirror(ordinal, EmptyProduct) + + private final val separator = '\000' + + private[TypeLevel] def label(ordinal: Int, idx: Int): String = { + val labels = caseLabels(ordinal) + @tailrec def separatorPos(from: Int): Int = + if (from == labels.length || labels(from) == separator) from + else separatorPos(from + 1) + @tailrec def findLabel(count: Int, idx: Int): String = + if (idx == labels.length) "" + else if (count == 0) labels.substring(idx, separatorPos(idx)) + else findLabel(if (labels(idx) == separator) count - 1 else count, idx + 1) + findLabel(idx, 0) + } } -} -// Generic deriving infrastructure -object Deriving { + object ReflectedClass { + /** Helper class to turn arrays into products */ + private class ArrayProduct(val elems: Array[AnyRef]) extends Product { + def canEqual(that: Any): Boolean = true + def productElement(n: Int) = elems(n) + def productArity = elems.length + override def productIterator: Iterator[Any] = elems.iterator + def update(n: Int, x: Any) = elems(n) = x.asInstanceOf[AnyRef] + } + + /** Helper object */ + private object EmptyProduct extends Product { + def canEqual(that: Any): Boolean = true + def productElement(n: Int) = throw new IndexOutOfBoundsException + def productArity = 0 + } + } /** A generic representation of a case in an ADT - * @param deriving The companion object of the ADT - * @param ordinal The ordinal value of the case in the list of the ADT's cases - * @param elems The elements of the case - */ - class Mirror(val deriving: Deriving, val ordinal: Int, val elems: Product) { + * @param deriving The companion object of the ADT + * @param ordinal The ordinal value of the case in the list of the ADT's cases + * @param elems The elements of the case + */ + class Mirror(val reflected: ReflectedClass, val ordinal: Int, val elems: Product) { /** The `n`'th element of this generic case */ def apply(n: Int): Any = elems.productElement(n) /** The name of the constructor of the case reflected by this mirror */ - def caseLabel: String = deriving.label(ordinal, 0) + def caseLabel: String = reflected.label(ordinal, 0) /** The label of the `n`'th element of the case reflected by this mirror */ - def elementLabel(n: Int) = deriving.label(ordinal, n + 1) + def elementLabel(n: Int) = reflected.label(ordinal, n + 1) } /** A class for mapping between an ADT value and - * the case mirror that represents the value. - */ + * the case mirror that represents the value. + */ abstract class Reflected[T] { /** The case mirror corresponding to ADT instance `x` */ @@ -70,7 +85,7 @@ object Deriving { def reify(mirror: Mirror): T /** The companion object of the ADT */ - def deriving: Deriving + def common: ReflectedClass } /** The shape of an ADT. @@ -89,39 +104,26 @@ object Deriving { * It informs that type `T` has shape `S` and also implements runtime reflection on `T`. */ abstract class Shaped[T, S <: Shape] extends Reflected[T] - - /** Helper class to turn arrays into products */ - private class ArrayProduct(val elems: Array[AnyRef]) extends Product { - def canEqual(that: Any): Boolean = true - def productElement(n: Int) = elems(n) - def productArity = elems.length - override def productIterator: Iterator[Any] = elems.iterator - def update(n: Int, x: Any) = elems(n) = x.asInstanceOf[AnyRef] - } - - /** Helper object */ - private object EmptyProduct extends Product { - def canEqual(that: Any): Boolean = true - def productElement(n: Int) = throw new IndexOutOfBoundsException - def productArity = 0 - } } // An algebraic datatype -enum Lst[+T] derives Eq, Pickler, Show { +enum Lst[+T] { // derives Eq, Pickler, Show case Cons(hd: T, tl: Lst[T]) case Nil } -object Lst extends Deriving { +object Lst { // common compiler-generated infrastructure - import Deriving._ + import TypeLevel._ type Shape[T] = Shape.Cases[( Shape.Case[Cons[T], (T, Lst[T])], Shape.Case[Nil.type, Unit] )] + val reflectedClass = new ReflectedClass(Array("Cons\000hd\000tl", "Nil")) + import reflectedClass.mirror + val NilMirror = mirror(1) implicit def lstShape[T]: Shaped[Lst[T], Shape[T]] = new { @@ -133,7 +135,7 @@ object Lst extends Deriving { case 0 => Cons[T](c(0).asInstanceOf, c(1).asInstanceOf) case 1 => Nil } - def deriving = Lst + def common = reflectedClass } protected val caseLabels = Array("Cons\000hd\000tl", "Nil") @@ -145,42 +147,46 @@ object Lst extends Deriving { } // A simple product type -case class Pair[T](x: T, y: T) derives Eq, Pickler +case class Pair[T](x: T, y: T) // derives Eq, Pickler, Show -object Pair extends Deriving { +object Pair { // common compiler-generated infrastructure - import Deriving._ + import TypeLevel._ type Shape[T] = Shape.Case[Pair[T], (T, T)] + val reflectedClass = new ReflectedClass(Array("Pair\000x\000y")) + import reflectedClass.mirror + implicit def pairShape[T]: Shaped[Pair[T], Shape[T]] = new { def reflect(xy: Pair[T]) = mirror(0, xy) def reify(c: Mirror): Pair[T] = Pair(c(0).asInstanceOf, c(1).asInstanceOf) - def deriving = Pair + def common = reflectedClass } - protected val caseLabels = Array("Pair\000x\000y") - // two clauses that could be generated from a `derives` clause implicit def PairEq[T: Eq]: Eq[Pair[T]] = Eq.derived implicit def PairPickler[T: Pickler]: Pickler[Pair[T]] = Pickler.derived implicit def PairShow[T: Show]: Show[Pair[T]] = Show.derived } -sealed trait Either[+L, +R] extends Product derives Eq, Pickler +sealed trait Either[+L, +R] extends Product // derives Eq, Pickler, Show case class Left[L](x: L) extends Either[L, Nothing] case class Right[R](x: R) extends Either[Nothing, R] -object Either extends Deriving { - import Deriving._ +object Either { + import TypeLevel._ type Shape[L, R] = Shape.Cases[( Shape.Case[Left[L], L *: Unit], Shape.Case[Right[R], R *: Unit] )] + val reflectedClass = new ReflectedClass(Array("Left\000x", "Right\000x")) + import reflectedClass.mirror + implicit def eitherShape[L, R]: Shaped[Either[L, R], Shape[L, R]] = new { def reflect(e: Either[L, R]): Mirror = e match { case e: Left[L] => mirror(0, e) @@ -190,11 +196,9 @@ object Either extends Deriving { case 0 => Left[L](c(0).asInstanceOf) case 1 => Right[R](c(0).asInstanceOf) } - def deriving = Either + def common = reflectedClass } - protected val caseLabels = Array("Left\000x", "Right\000x") - implicit def EitherEq[L: Eq, R: Eq]: Eq[Either[L, R]] = Eq.derived implicit def EitherPickler[L: Pickler, R: Pickler]: Pickler[Either[L, R]] = Pickler.derived implicit def EitherShow[L: Show, R: Show]: Show[Either[L, R]] = Show.derived @@ -206,8 +210,8 @@ trait Eq[T] { } object Eq { - import _root_.scala.typelevel._ - import Deriving._ + import scala.typelevel.erasedValue + import TypeLevel._ inline def tryEql[T](x: T, y: T) = implicit match { case eq: Eq[T] => eq.eql(x, y) @@ -261,8 +265,8 @@ trait Pickler[T] { } object Pickler { - import scala.typelevel._ - import Deriving._ + import scala.typelevel.{erasedValue, constValue} + import TypeLevel._ def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) @@ -309,11 +313,11 @@ object Pickler { inline def unpickleCase[T, Elems <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { inline val size = constValue[Tuple.Size[Elems]] inline if (size == 0) - r.reify(r.deriving.mirror(ordinal)) + r.reify(r.common.mirror(ordinal)) else { val elems = new Array[Object](size) unpickleElems[Elems](buf, elems, 0) - r.reify(r.deriving.mirror(ordinal, elems)) + r.reify(r.common.mirror(ordinal, elems)) } } @@ -352,8 +356,8 @@ trait Show[T] { def show(x: T): String } object Show { - import scala.typelevel._ - import Deriving._ + import scala.typelevel.erasedValue + import TypeLevel._ inline def tryShow[T](x: T): String = implicit match { case s: Show[T] => s.show(x) @@ -402,7 +406,7 @@ object Show { // Tests object Test extends App { - import Deriving._ + import TypeLevel._ val eq = implicitly[Eq[Lst[Int]]] val xs = Lst.Cons(11, Lst.Cons(22, Lst.Cons(33, Lst.Nil))) val ys = Lst.Cons(11, Lst.Cons(22, Lst.Nil)) From 7adc4c4f39e90666b1a9073fa70db74f8fab01ca Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 30 Nov 2018 15:36:47 +0100 Subject: [PATCH 12/56] Partial Implementation of Deriving functionality --- .../dotty/tools/dotc/transform/SymUtils.scala | 3 +- .../src/dotty/tools/dotc/typer/Checking.scala | 15 +- .../src/dotty/tools/dotc/typer/Deriving.scala | 197 +++++++++ .../src/dotty/tools/dotc/typer/Namer.scala | 44 +- .../dotty/tools/dotc/typer/ProtoTypes.scala | 2 +- .../src/dotty/tools/dotc/typer/Typer.scala | 19 +- tests/run/typeclass-derivation3.check | 10 + tests/run/typeclass-derivation3.scala | 383 ++++++++++++++++++ 8 files changed, 634 insertions(+), 39 deletions(-) create mode 100644 compiler/src/dotty/tools/dotc/typer/Deriving.scala create mode 100644 tests/run/typeclass-derivation3.check create mode 100644 tests/run/typeclass-derivation3.scala diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index f836975e0132..feaa7e5e340a 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -135,7 +135,8 @@ class SymUtils(val self: Symbol) extends AnyVal { /** If this is a sealed class, its known children */ def children(implicit ctx: Context): List[Symbol] = { - if (self.isType) self.setFlag(ChildrenQueried) + if (self.isType) + self.setFlag(ChildrenQueried) self.annotations.collect { case Annotation.Child(child) => child } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index 766ce1f7f5e0..c815fd9b4c17 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -916,7 +916,7 @@ trait Checking { * @param cdef the enum companion object class * @param enumCtx the context immediately enclosing the corresponding enum */ - private def checkEnumCaseRefsLegal(cdef: TypeDef, enumCtx: Context)(implicit ctx: Context): Unit = { + def checkEnumCaseRefsLegal(cdef: TypeDef, enumCtx: Context)(implicit ctx: Context): Unit = { def checkCaseOrDefault(stat: Tree, caseCtx: Context) = { @@ -971,24 +971,13 @@ trait Checking { case _ => } } - - /** Check all enum cases in all enum companions in `stats` for legal accesses. - * @param enumContexts a map from`enum` symbols to the contexts enclosing their definitions - */ - def checkEnumCompanions(stats: List[Tree], enumContexts: collection.Map[Symbol, Context])(implicit ctx: Context): List[Tree] = { - for (stat @ TypeDef(_, _) <- stats) - if (stat.symbol.is(Module)) - for (enumContext <- enumContexts.get(stat.symbol.linkedClass)) - checkEnumCaseRefsLegal(stat, enumContext) - stats - } } trait ReChecking extends Checking { import tpd._ override def checkEnum(cdef: untpd.TypeDef, cls: Symbol, parent: Symbol)(implicit ctx: Context): Unit = () override def checkRefsLegal(tree: tpd.Tree, badOwner: Symbol, allowed: (Name, Symbol) => Boolean, where: String)(implicit ctx: Context): Unit = () - override def checkEnumCompanions(stats: List[Tree], enumContexts: collection.Map[Symbol, Context])(implicit ctx: Context): List[Tree] = stats + override def checkEnumCaseRefsLegal(cdef: TypeDef, enumCtx: Context)(implicit ctx: Context): Unit = () } trait NoChecking extends ReChecking { diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala new file mode 100644 index 000000000000..95df73f2ceb2 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -0,0 +1,197 @@ +package dotty.tools +package dotc +package typer + +import core._ +import ast._ +import ast.Trees._ +import StdNames._ +import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._, Decorators._ +import NameKinds.DefaultGetterName +import ast.desugar, ast.desugar._ +import ProtoTypes._ +import util.Positions._ +import util.Property +import collection.mutable +import tpd.ListOfTreeDecorator +import config.Config +import config.Printers.typr +import Annotations._ +import Inferencing._ +import transform.ValueClasses._ +import transform.TypeUtils._ +import transform.SymUtils._ +import reporting.diagnostic.messages._ + +trait Deriving { this: Typer => + + class Deriver(cls: ClassSymbol)(implicit ctx: Context) { + + private var synthetics = new mutable.ListBuffer[Symbol] + + private def caseShape(sym: Symbol): Type = { + val (constr, elems) = + sym match { + case caseClass: ClassSymbol => + caseClass.primaryConstructor.info match { + case info: PolyType => + def instantiate(implicit ctx: Context) = { + val poly = constrained(info, untpd.EmptyTree, alwaysAddTypeVars = true)._1 + val mono @ MethodType(_) = poly.resultType + val resType = mono.finalResultType + resType <:< cls.appliedRef + val tparams = poly.paramRefs + val variances = caseClass.typeParams.map(_.paramVariance) + val instanceTypes = (tparams, variances).zipped.map((tparam, variance) => + ctx.typeComparer.instanceType(tparam, fromBelow = variance < 0)) + (resType.substParams(poly, instanceTypes), + mono.paramInfos.map(_.substParams(poly, instanceTypes))) + } + instantiate(ctx.fresh.setNewTyperState().setOwner(caseClass)) + case info: MethodType => + (cls.typeRef, info.paramInfos) + case _ => + (cls.typeRef, Nil) + } + case _ => + (sym.termRef, Nil) + } + val elemShape = (elems :\ (defn.UnitType: Type))(defn.PairType.appliedTo(_, _)) + defn.ShapeCaseType.appliedTo(constr, elemShape) + } + + lazy val children = cls.children.sortBy(_.pos.start) + + private def sealedShape: Type = { + val cases = children.map(caseShape) + val casesShape = (cases :\ (defn.UnitType: Type))(defn.PairType.appliedTo(_, _)) + defn.ShapeCasesType.appliedTo(casesShape) + } + + lazy val shapeWithClassParams: Type = + if (cls.is(Case)) caseShape(cls) + else if (cls.is(Sealed)) sealedShape + else NoType + + lazy val shape: Type = shapeWithClassParams match { + case delayed: LazyRef => HKTypeLambda.fromParams(cls.typeParams, delayed.ref) + case NoType => NoType + } + + lazy val lazyShape: Type = shapeWithClassParams match { + case delayed: LazyRef => HKTypeLambda.fromParams(cls.typeParams, delayed) + case NoType => NoType + } + + class ShapeCompleter extends TypeParamsCompleter { + + override def completerTypeParams(sym: Symbol)(implicit ctx: Context) = cls.typeParams + + def completeInCreationContext(denot: SymDenotation) = { + val shape0 = shapeWithClassParams + val tparams = cls.typeParams + val abstractedShape = + if (!shape0.exists) { + ctx.error(em"Cannot derive for $cls; it is neither sealed nor a case class or object", cls.pos) + UnspecifiedErrorType + } + else if (tparams.isEmpty) + shape0 + else + HKTypeLambda(tparams.map(_.name.withVariance(0)))( + tl => tparams.map(tparam => tl.integrate(tparams, tparam.info).bounds), + tl => tl.integrate(tparams, shape0)) + denot.info = TypeAlias(abstractedShape) + } + + def complete(denot: SymDenotation)(implicit ctx: Context) = + completeInCreationContext(denot) + } + + private def add(sym: Symbol): sym.type = { + ctx.enter(sym) + synthetics += sym + sym + } + + /** Enter type class instance with given name and info in current scope, provided + * an instance woth the same name does not exist already. + */ + private def addDerivedInstance(clsName: Name, info: Type, reportErrors: Boolean) = { + val instanceName = s"derived$$$clsName".toTermName + if (ctx.denotNamed(instanceName).exists) { + if (reportErrors) ctx.error(i"duplicate typeclass derivation for $clsName") + } + else + add(ctx.newSymbol(ctx.owner, instanceName, Synthetic | Method, info, coord = cls.pos)) + } + + /* Check derived type tree `derived` for the following well-formedness conditions: + * (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix) + * (2) It must have exactly one type parameter + * If it passes the checks, enter a typeclass instance for it in the current scope. + */ + private def processDerivedInstance(derived: untpd.Tree): Unit = { + val uncheckedType = typedAheadType(derived, AnyTypeConstructorProto).tpe.dealiasKeepAnnots + val derivedType = checkClassType(uncheckedType, derived.pos, traitReq = false, stablePrefixReq = true) + val nparams = derivedType.classSymbol.typeParams.length + if (nparams == 1) { + val typeClass = derivedType.classSymbol + val firstKindedParams = cls.typeParams.filterNot(_.info.isLambdaSub) + val evidenceParamInfos = + for (param <- firstKindedParams) yield derivedType.appliedTo(param.typeRef) + val resultType = derivedType.appliedTo(cls.appliedRef) + val instanceInfo = + if (evidenceParamInfos.isEmpty) ExprType(resultType) + else PolyType.fromParams(firstKindedParams, ImplicitMethodType(evidenceParamInfos, resultType)) + addDerivedInstance(derivedType.typeSymbol.name, instanceInfo, reportErrors = true) + } + else + ctx.error( + i"derived class $derivedType should have one type paramater but has $nparams", + derived.pos) + } + + private def addShape(): Unit = + if (!ctx.denotNamed(tpnme.Shape).exists) { + val shapeSym = add(ctx.newSymbol(ctx.owner, tpnme.Shape, EmptyFlags, new ShapeCompleter)) + val shapedCls = defn.ShapedClass + val lazyShapedInfo = new LazyType { + def complete(denot: SymDenotation)(implicit ctx: Context) = { + val tparams = cls.typeParams + val shapedType = shapedCls.typeRef.appliedTo( + cls.appliedRef, + shapeSym.typeRef.appliedTo(tparams.map(_.typeRef))) + denot.info = PolyType.fromParams(tparams, shapedType).ensureMethodic + } + } + addDerivedInstance(shapedCls.name, lazyShapedInfo, reportErrors = false) + } + + def enterDerived(derived: List[untpd.Tree]) = { + derived.foreach(processDerivedInstance(_)) + addShape() + } + + def implementedClass(instance: Symbol) = + instance.info.stripPoly.finalResultType.classSymbol + + def typeclassInstance(sym: Symbol)(tparamRefs: List[Type])(paramRefss: List[List[tpd.Tree]]): tpd.Tree = { + val tparams = tparamRefs.map(_.typeSymbol.asType) + val params = if (paramRefss.isEmpty) Nil else paramRefss.head.map(_.symbol.asTerm) + val typeCls = implementedClass(sym) + tpd.ref(defn.Predef_undefinedR) // TODO: flesh out + } + + def syntheticDef(sym: Symbol): tpd.Tree = + if (sym.isType) tpd.TypeDef(sym.asType) + else tpd.polyDefDef(sym.asTerm, typeclassInstance(sym)) + + def finalize(stat: tpd.TypeDef): tpd.Tree = { + val templ @ Template(_, _, _, _) = stat.rhs + val newDefs = synthetics.map(syntheticDef) + tpd.cpy.TypeDef(stat)( + rhs = tpd.cpy.Template(templ)(body = templ.body ++ newDefs)) + } + } +} diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 4e2c11c54dbc..e14ce1005a7f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -196,6 +196,8 @@ class Namer { typer: Typer => val TypedAhead: Property.Key[tpd.Tree] = new Property.Key val ExpandedTree: Property.Key[untpd.Tree] = new Property.Key val SymOfTree: Property.Key[Symbol] = new Property.Key + val DerivingCompanion: Property.Key[Unit] = new Property.Key + val Deriver: Property.Key[typer.Deriver] = new Property.Key /** A partial map from unexpanded member and pattern defs and to their expansions. * Populated during enterSyms, emptied during typer. @@ -534,7 +536,7 @@ class Namer { typer: Typer => } /** Merge the module class `modCls` in the expanded tree of `mdef` with the - * body and derived clause of the syntehtic module class `fromCls`. + * body and derived clause of the synthetic module class `fromCls`. */ def mergeModuleClass(mdef: Tree, modCls: TypeDef, fromCls: TypeDef): TypeDef = { var res: TypeDef = null @@ -545,8 +547,13 @@ class Namer { typer: Typer => val modTempl = modCls.rhs.asInstanceOf[Template] res = cpy.TypeDef(modCls)( rhs = cpy.Template(modTempl)( - derived = fromTempl.derived ++ modTempl.derived, + derived = if (fromTempl.derived.nonEmpty) fromTempl.derived else modTempl.derived, body = fromTempl.body ++ modTempl.body)) + if (fromTempl.derived.nonEmpty) { + if (modTempl.derived.nonEmpty) + ctx.error(em"a class and its companion cannot both have `derives' clauses", mdef.pos) + res.putAttachment(DerivingCompanion, ()) + } res } else tree @@ -813,7 +820,8 @@ class Namer { typer: Typer => cls.addAnnotation(Annotation.Child(child)) else ctx.error(em"""children of ${cls} were already queried before $sym was discovered. - |As a remedy, you could move $sym on the same nesting level as $cls.""") + |As a remedy, you could move $sym on the same nesting level as $cls.""", + child.pos) } } @@ -949,23 +957,6 @@ class Namer { typer: Typer => } } - /* Check derived type tree `derived` for the following well-formedness conditions: - * (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix) - * (2) It must have exactly one type parameter - * If it passes the checks, enter a typeclass instance for it in the class scope. - */ - def addDerivedInstance(derived: untpd.Tree): Unit = { - val uncheckedType = typedAheadType(derived, AnyTypeConstructorProto).tpe.dealiasKeepAnnots - val derivedType = checkClassType(uncheckedType, derived.pos, traitReq = false, stablePrefixReq = true) - val nparams = derivedType.typeSymbol.typeParams.length - if (nparams == 1) - println(i"todo: add derived instance $derived") - else - ctx.error( - i"derived class $derivedType should have one type paramater but has $nparams", - derived.pos) - } - addAnnotations(denot.symbol) val selfInfo: TypeOrSymbol = @@ -983,20 +974,29 @@ class Namer { typer: Typer => val tempInfo = new TempClassInfo(cls.owner.thisType, cls, decls, selfInfo) denot.info = tempInfo + val localCtx = ctx.inClassContext(selfInfo) + // Ensure constructor is completed so that any parameter accessors // which have type trees deriving from its parameters can be // completed in turn. Note that parent types access such parameter // accessors, that's why the constructor needs to be completed before // the parent types are elaborated. index(constr) - index(rest)(ctx.inClassContext(selfInfo)) + index(rest)(localCtx) symbolOfTree(constr).ensureCompleted() val parentTypes = defn.adjustForTuple(cls, cls.typeParams, ensureFirstIsClass(parents.map(checkedParentType(_)), cls.span)) typr.println(i"completing $denot, parents = $parents%, %, parentTypes = $parentTypes%, %") - impl.derived.foreach(addDerivedInstance) + if (impl.derived.nonEmpty) { + val derivingClass = + if (original.removeAttachment(DerivingCompanion).isDefined) cls.companionClass.asClass + else cls + val deriver = new Deriver(derivingClass)(localCtx) + deriver.enterDerived(impl.derived) + original.putAttachment(Deriver, deriver) + } val finalSelfInfo: TypeOrSymbol = if (cls.isOpaqueCompanion) { diff --git a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala index f68793b41f2f..8d40cbbae9aa 100644 --- a/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/compiler/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -457,7 +457,7 @@ object ProtoTypes { * If the constraint contains already some of these parameters in its domain, * make a copy of the type lambda and add the copy's type parameters instead. * Return either the original type lambda, or the copy, if one was made. - * Also, if `owningTree` is non-empty ot `alwaysAddTypeVars` is true, add a type variable + * Also, if `owningTree` is non-empty or `alwaysAddTypeVars` is true, add a type variable * for each parameter. * @return The added type lambda, and the list of created type variables. */ diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 15d8a595af9a..4db9a4f4086d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -83,7 +83,8 @@ class Typer extends Namer with Implicits with Inferencing with Dynamic - with Checking { + with Checking + with Deriving { import Typer._ import tpd.{cpy => _, _} @@ -1674,6 +1675,9 @@ class Typer extends Namer if (ctx.mode.is(Mode.Interactive) && ctx.settings.YretainTrees.value) cls.rootTreeOrProvider = cdef1 + for (deriver <- cdef.removeAttachment(Deriver)) + cdef1.putAttachment(Deriver, deriver) + cdef1 // todo later: check that @@ -2091,7 +2095,18 @@ class Typer extends Namer val exprOwnerOpt = if (exprOwner == ctx.owner) None else Some(exprOwner) ctx.withProperty(ExprOwner, exprOwnerOpt) } - checkEnumCompanions(traverse(stats)(localCtx), enumContexts) + def finalize(stat: Tree)(implicit ctx: Context): Tree = stat match { + case stat: TypeDef if stat.symbol.is(Module) => + for (enumContext <- enumContexts.get(stat.symbol.linkedClass)) + checkEnumCaseRefsLegal(stat, enumContext) + stat.removeAttachment(Deriver) match { + case Some(deriver) => deriver.finalize(stat) + case None => stat + } + case _ => + stat + } + traverse(stats)(localCtx).mapConserve(finalize) } /** Given an inline method `mdef`, the method rewritten so that its body diff --git a/tests/run/typeclass-derivation3.check b/tests/run/typeclass-derivation3.check new file mode 100644 index 000000000000..fd3681599ffc --- /dev/null +++ b/tests/run/typeclass-derivation3.check @@ -0,0 +1,10 @@ +ListBuffer(0, 11, 0, 22, 0, 33, 1) +Cons(11,Cons(22,Cons(33,Nil))) +ListBuffer(0, 0, 11, 0, 22, 0, 33, 1, 0, 0, 11, 0, 22, 1, 1) +Cons(Cons(11,Cons(22,Cons(33,Nil))),Cons(Cons(11,Cons(22,Nil)),Nil)) +ListBuffer(1, 2) +Pair(1,2) +Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil()))) +Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil()))), tl = Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Nil())), tl = Nil())) +Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil())) +Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil())) diff --git a/tests/run/typeclass-derivation3.scala b/tests/run/typeclass-derivation3.scala new file mode 100644 index 000000000000..d292199ef895 --- /dev/null +++ b/tests/run/typeclass-derivation3.scala @@ -0,0 +1,383 @@ +import scala.collection.mutable +import scala.annotation.tailrec + +object datatypes { + import typeclasses._ + // An algebraic datatype + enum Lst[+T] derives Eq, Pickler, Show { + case Cons(hd: T, tl: Lst[T]) + case Nil + } + + object Lst { + // common compiler-generated infrastructure + import typelevel._ +/* + type Shape[T] = Shape.Cases[( + Shape.Case[Cons[T], (T, Lst[T])], + Shape.Case[Nil.type, Unit] + )] +*/ + val reflectedClass = new ReflectedClass(Array("Cons\000hd\000tl", "Nil")) + import reflectedClass.mirror + + val NilMirror = mirror(1) + + implicit def lstShape[T]: Shaped[Lst[T], Shape[T]] = new { + def reflect(xs: Lst[T]): Mirror = xs match { + case xs: Cons[T] => mirror(0, xs) + case Nil => NilMirror + } + def reify(c: Mirror): Lst[T] = c.ordinal match { + case 0 => Cons[T](c(0).asInstanceOf, c(1).asInstanceOf) + case 1 => Nil + } + def common = reflectedClass + } + + // three clauses that could be generated from a `derives` clause + implicit def LstEq[T: Eq]: Eq[Lst[T]] = Eq.derived + implicit def LstPickler[T: Pickler]: Pickler[Lst[T]] = Pickler.derived + implicit def LstShow[T: Show]: Show[Lst[T]] = Show.derived + } + + // A simple product type + case class Pair[T](x: T, y: T) derives Eq, Pickler, Show + + object Pair { + // common compiler-generated infrastructure + import typelevel._ + + // type Shape[T] = Shape.Case[Pair[T], (T, T)] + + val reflectedClass = new ReflectedClass(Array("Pair\000x\000y")) + import reflectedClass.mirror + + implicit def pairShape[T]: Shaped[Pair[T], Shape[T]] = new { + def reflect(xy: Pair[T]) = + mirror(0, xy) + def reify(c: Mirror): Pair[T] = + Pair(c(0).asInstanceOf, c(1).asInstanceOf) + def common = reflectedClass + } + + // two clauses that could be generated from a `derives` clause + implicit def PairEq[T: Eq]: Eq[Pair[T]] = Eq.derived + implicit def PairPickler[T: Pickler]: Pickler[Pair[T]] = Pickler.derived + implicit def PairShow[T: Show]: Show[Pair[T]] = Show.derived + } + + sealed trait Either[+L, +R] extends Product derives Eq, Pickler, Show + case class Left[L](x: L) extends Either[L, Nothing] + case class Right[R](x: R) extends Either[Nothing, R] + + object Either { + import typelevel._ + + /* + type Shape[L, R] = Shape.Cases[( + Shape.Case[Left[L], L *: Unit], + Shape.Case[Right[R], R *: Unit] + )] +*/ + val reflectedClass = new ReflectedClass(Array("Left\000x", "Right\000x")) + import reflectedClass.mirror + + implicit def eitherShape[L, R]: Shaped[Either[L, R], Shape[L, R]] = new { + def reflect(e: Either[L, R]): Mirror = e match { + case e: Left[L] => mirror(0, e) + case e: Right[R] => mirror(1, e) + } + def reify(c: Mirror): Either[L, R] = c.ordinal match { + case 0 => Left[L](c(0).asInstanceOf) + case 1 => Right[R](c(0).asInstanceOf) + } + def common = reflectedClass + } + + implicit def EitherEq[L: Eq, R: Eq]: Eq[Either[L, R]] = Eq.derived + implicit def EitherPickler[L: Pickler, R: Pickler]: Pickler[Either[L, R]] = Pickler.derived + implicit def EitherShow[L: Show, R: Show]: Show[Either[L, R]] = Show.derived + } +} + +object typeclasses { + // A typeclass + trait Eq[T] { + def eql(x: T, y: T): Boolean + } + + object Eq { + import scala.typelevel.erasedValue + import typelevel._ + + inline def tryEql[T](x: T, y: T) = implicit match { + case eq: Eq[T] => eq.eql(x, y) + } + + inline def eqlElems[Elems <: Tuple](xs: Mirror, ys: Mirror, n: Int): Boolean = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + tryEql[elem](xs(n).asInstanceOf, ys(n).asInstanceOf) && + eqlElems[elems1](xs, ys, n + 1) + case _: Unit => + true + } + + inline def eqlCase[T, Elems <: Tuple](r: Reflected[T], x: T, y: T) = + eqlElems[Elems](r.reflect(x), r.reflect(y), 0) + + inline def eqlCases[T, Alts <: Tuple](r: Reflected[T], x: T, y: T): Boolean = + inline erasedValue[Alts] match { + case _: (Shape.Case[alt, elems] *: alts1) => + x match { + case x: `alt` => + y match { + case y: `alt` => eqlCase[T, elems](r, x, y) + case _ => false + } + case _ => eqlCases[T, alts1](r, x, y) + } + case _: Unit => + false + } + + inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Eq[T] = new { + def eql(x: T, y: T): Boolean = inline erasedValue[S] match { + case _: Shape.Cases[alts] => + eqlCases[T, alts](ev, x, y) + case _: Shape.Case[_, elems] => + eqlCase[T, elems](ev, x, y) + } + } + + implicit object IntEq extends Eq[Int] { + def eql(x: Int, y: Int) = x == y + } + } + + // Another typeclass + trait Pickler[T] { + def pickle(buf: mutable.ListBuffer[Int], x: T): Unit + def unpickle(buf: mutable.ListBuffer[Int]): T + } + + object Pickler { + import scala.typelevel.{erasedValue, constValue} + import typelevel._ + + def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) + + inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = implicit match { + case pkl: Pickler[T] => pkl.pickle(buf, x) + } + + inline def pickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Mirror, n: Int): Unit = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + tryPickle[elem](buf, elems(n).asInstanceOf[elem]) + pickleElems[elems1](buf, elems, n + 1) + case _: Unit => + } + + inline def pickleCase[T, Elems <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], x: T): Unit = + pickleElems[Elems](buf, r.reflect(x), 0) + + inline def pickleCases[T, Alts <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], x: T, n: Int): Unit = + inline erasedValue[Alts] match { + case _: (Shape.Case[alt, elems] *: alts1) => + x match { + case x: `alt` => + buf += n + pickleCase[T, elems](r, buf, x) + case _ => + pickleCases[T, alts1](r, buf, x, n + 1) + } + case _: Unit => + } + + inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = implicit match { + case pkl: Pickler[T] => pkl.unpickle(buf) + } + + inline def unpickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Array[AnyRef], n: Int): Unit = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + elems(n) = tryUnpickle[elem](buf).asInstanceOf[AnyRef] + unpickleElems[elems1](buf, elems, n + 1) + case _: Unit => + } + + inline def unpickleCase[T, Elems <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { + inline val size = constValue[Tuple.Size[Elems]] + inline if (size == 0) + r.reify(r.common.mirror(ordinal)) + else { + val elems = new Array[Object](size) + unpickleElems[Elems](buf, elems, 0) + r.reify(r.common.mirror(ordinal, elems)) + } + } + + inline def unpickleCases[T, Alts <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = + inline erasedValue[Alts] match { + case _: (Shape.Case[_, elems] *: alts1) => + if (n == ordinal) unpickleCase[T, elems](r, buf, ordinal) + else unpickleCases[T, alts1](r, buf, ordinal, n + 1) + case _ => + throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ordinal") + } + + inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Pickler[T] = new { + def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = inline erasedValue[S] match { + case _: Shape.Cases[alts] => + pickleCases[T, alts](ev, buf, x, 0) + case _: Shape.Case[_, elems] => + pickleCase[T, elems](ev, buf, x) + } + def unpickle(buf: mutable.ListBuffer[Int]): T = inline erasedValue[S] match { + case _: Shape.Cases[alts] => + unpickleCases[T, alts](ev, buf, nextInt(buf), 0) + case _: Shape.Case[_, elems] => + unpickleCase[T, elems](ev, buf, 0) + } + } + + implicit object IntPickler extends Pickler[Int] { + def pickle(buf: mutable.ListBuffer[Int], x: Int): Unit = buf += x + def unpickle(buf: mutable.ListBuffer[Int]): Int = nextInt(buf) + } + } + + // A third typeclass, making use of labels + trait Show[T] { + def show(x: T): String + } + object Show { + import scala.typelevel.erasedValue + import typelevel._ + + inline def tryShow[T](x: T): String = implicit match { + case s: Show[T] => s.show(x) + } + + inline def showElems[Elems <: Tuple](elems: Mirror, n: Int): List[String] = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + val formal = elems.elementLabel(n) + val actual = tryShow[elem](elems(n).asInstanceOf) + s"$formal = $actual" :: showElems[elems1](elems, n + 1) + case _: Unit => + Nil + } + + inline def showCase[T, Elems <: Tuple](r: Reflected[T], x: T): String = { + val mirror = r.reflect(x) + val args = showElems[Elems](mirror, 0).mkString(", ") + s"${mirror.caseLabel}($args)" + } + + inline def showCases[T, Alts <: Tuple](r: Reflected[T], x: T): String = + inline erasedValue[Alts] match { + case _: (Shape.Case[alt, elems] *: alts1) => + x match { + case x: `alt` => showCase[T, elems](r, x) + case _ => showCases[T, alts1](r, x) + } + case _: Unit => + throw new MatchError(x) + } + + inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Show[T] = new { + def show(x: T): String = inline erasedValue[S] match { + case _: Shape.Cases[alts] => + showCases[T, alts](ev, x) + case _: Shape.Case[_, elems] => + showCase[T, elems](ev, x) + } + } + + implicit object IntShow extends Show[Int] { + def show(x: Int): String = x.toString + } + } +} +import datatypes._ +import typeclasses._ + +// Tests +object Test extends App { + val eq = implicitly[Eq[Lst[Int]]] + val xs = Lst.Cons(11, Lst.Cons(22, Lst.Cons(33, Lst.Nil))) + val ys = Lst.Cons(11, Lst.Cons(22, Lst.Nil)) + assert(eq.eql(xs, xs)) + assert(!eq.eql(xs, ys)) + assert(!eq.eql(ys, xs)) + assert(eq.eql(ys, ys)) + + val eq2 = implicitly[Eq[Lst[Lst[Int]]]] + val xss = Lst.Cons(xs, Lst.Cons(ys, Lst.Nil)) + val yss = Lst.Cons(xs, Lst.Nil) + assert(eq2.eql(xss, xss)) + assert(!eq2.eql(xss, yss)) + assert(!eq2.eql(yss, xss)) + assert(eq2.eql(yss, yss)) + + val buf = new mutable.ListBuffer[Int] + val pkl = implicitly[Pickler[Lst[Int]]] + pkl.pickle(buf, xs) + println(buf) + val xs1 = pkl.unpickle(buf) + println(xs1) + assert(xs1 == xs) + assert(eq.eql(xs1, xs)) + + val pkl2 = implicitly[Pickler[Lst[Lst[Int]]]] + pkl2.pickle(buf, xss) + println(buf) + val xss1 = pkl2.unpickle(buf) + println(xss1) + assert(xss == xss1) + assert(eq2.eql(xss, xss1)) + + val p1 = Pair(1, 2) + val p2 = Pair(1, 2) + val p3 = Pair(2, 1) + val eqp = implicitly[Eq[Pair[Int]]] + assert(eqp.eql(p1, p2)) + assert(!eqp.eql(p2, p3)) + + val pklp = implicitly[Pickler[Pair[Int]]] + pklp.pickle(buf, p1) + println(buf) + val p1a = pklp.unpickle(buf) + println(p1a) + assert(p1 == p1a) + assert(eqp.eql(p1, p1a)) + + def showPrintln[T: Show](x: T): Unit = + println(implicitly[Show[T]].show(x)) + showPrintln(xs) + showPrintln(xss) + + val zs = Lst.Cons(Left(1), Lst.Cons(Right(Pair(2, 3)), Lst.Nil)) + showPrintln(zs) + + def pickle[T: Pickler](buf: mutable.ListBuffer[Int], x: T): Unit = + implicitly[Pickler[T]].pickle(buf, x) + + def unpickle[T: Pickler](buf: mutable.ListBuffer[Int]): T = + implicitly[Pickler[T]].unpickle(buf) + + def copy[T: Pickler](x: T): T = { + val buf = new mutable.ListBuffer[Int] + pickle(buf, x) + unpickle[T](buf) + } + + def eql[T: Eq](x: T, y: T) = implicitly[Eq[T]].eql(x, y) + + val zs1 = copy(zs) + showPrintln(zs1) + assert(eql(zs, zs1)) +} \ No newline at end of file From a1fbe5f6fa4d9dfb68b9581cf216c1fdeb67e055 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 30 Nov 2018 19:07:59 +0100 Subject: [PATCH 13/56] Auto-generated derived typeclass instances ... except for Shaped, which is still a todo. --- .../src/dotty/tools/dotc/core/StdNames.scala | 1 + .../src/dotty/tools/dotc/typer/Deriving.scala | 37 ++++++++++++++----- tests/run/typeclass-derivation3.scala | 18 ++++----- 3 files changed, 37 insertions(+), 19 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index f7d3471cd82a..3f9d36e6b5c3 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -399,6 +399,7 @@ object StdNames { val definitions: N = "definitions" val delayedInit: N = "delayedInit" val delayedInitArg: N = "delayedInit$body" + val derived: N = "derived" val derives: N = "derives" val drop: N = "drop" val dynamics: N = "dynamics" diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index 95df73f2ceb2..a624b8575f0f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -117,13 +117,15 @@ trait Deriving { this: Typer => /** Enter type class instance with given name and info in current scope, provided * an instance woth the same name does not exist already. */ - private def addDerivedInstance(clsName: Name, info: Type, reportErrors: Boolean) = { + private def addDerivedInstance(clsName: Name, info: Type, reportErrors: Boolean, implicitFlag: FlagSet = Implicit) = { val instanceName = s"derived$$$clsName".toTermName if (ctx.denotNamed(instanceName).exists) { if (reportErrors) ctx.error(i"duplicate typeclass derivation for $clsName") } - else - add(ctx.newSymbol(ctx.owner, instanceName, Synthetic | Method, info, coord = cls.pos)) + else { + val implFlag = if (reportErrors) Implicit else EmptyFlags // for now + add(ctx.newSymbol(ctx.owner, instanceName, Synthetic | Method | implFlag, info, coord = cls.pos.startPos)) + } } /* Check derived type tree `derived` for the following well-formedness conditions: @@ -176,16 +178,31 @@ trait Deriving { this: Typer => def implementedClass(instance: Symbol) = instance.info.stripPoly.finalResultType.classSymbol - def typeclassInstance(sym: Symbol)(tparamRefs: List[Type])(paramRefss: List[List[tpd.Tree]]): tpd.Tree = { - val tparams = tparamRefs.map(_.typeSymbol.asType) - val params = if (paramRefss.isEmpty) Nil else paramRefss.head.map(_.symbol.asTerm) - val typeCls = implementedClass(sym) - tpd.ref(defn.Predef_undefinedR) // TODO: flesh out - } + private def typeclassInstance(sym: Symbol)(implicit ctx: Context) = + (tparamRefs: List[Type]) => (paramRefss: List[List[tpd.Tree]]) => { + val tparams = tparamRefs.map(_.typeSymbol.asType) + val params = if (paramRefss.isEmpty) Nil else paramRefss.head.map(_.symbol.asTerm) + tparams.foreach(ctx.enter) + params.foreach(ctx.enter) + def instantiated(info: Type): Type = info match { + case info: PolyType => instantiated(info.instantiate(tparamRefs)) + case info: MethodType => info.instantiate(params.map(_.termRef)) + case info => info + } + val resultType = instantiated(sym.info) + val typeCls = resultType.classSymbol + if (typeCls == defn.ShapedClass) + tpd.ref(defn.Predef_undefinedR) // TODO: flesh out + else { + val module = untpd.ref(typeCls.companionModule.termRef).withPos(sym.pos) + val rhs = untpd.Select(module, nme.derived) + typed(rhs, resultType) + } + } def syntheticDef(sym: Symbol): tpd.Tree = if (sym.isType) tpd.TypeDef(sym.asType) - else tpd.polyDefDef(sym.asTerm, typeclassInstance(sym)) + else tpd.polyDefDef(sym.asTerm, typeclassInstance(sym)(ctx.fresh.setOwner(sym).setNewScope)) def finalize(stat: tpd.TypeDef): tpd.Tree = { val templ @ Template(_, _, _, _) = stat.rhs diff --git a/tests/run/typeclass-derivation3.scala b/tests/run/typeclass-derivation3.scala index d292199ef895..84313706a244 100644 --- a/tests/run/typeclass-derivation3.scala +++ b/tests/run/typeclass-derivation3.scala @@ -36,9 +36,9 @@ object datatypes { } // three clauses that could be generated from a `derives` clause - implicit def LstEq[T: Eq]: Eq[Lst[T]] = Eq.derived - implicit def LstPickler[T: Pickler]: Pickler[Lst[T]] = Pickler.derived - implicit def LstShow[T: Show]: Show[Lst[T]] = Show.derived + //implicit def LstEq[T: Eq]: Eq[Lst[T]] = Eq.derived + //implicit def LstPickler[T: Pickler]: Pickler[Lst[T]] = Pickler.derived + //implicit def LstShow[T: Show]: Show[Lst[T]] = Show.derived } // A simple product type @@ -62,9 +62,9 @@ object datatypes { } // two clauses that could be generated from a `derives` clause - implicit def PairEq[T: Eq]: Eq[Pair[T]] = Eq.derived - implicit def PairPickler[T: Pickler]: Pickler[Pair[T]] = Pickler.derived - implicit def PairShow[T: Show]: Show[Pair[T]] = Show.derived + //implicit def PairEq[T: Eq]: Eq[Pair[T]] = Eq.derived + //implicit def PairPickler[T: Pickler]: Pickler[Pair[T]] = Pickler.derived + //implicit def PairShow[T: Show]: Show[Pair[T]] = Show.derived } sealed trait Either[+L, +R] extends Product derives Eq, Pickler, Show @@ -95,9 +95,9 @@ object datatypes { def common = reflectedClass } - implicit def EitherEq[L: Eq, R: Eq]: Eq[Either[L, R]] = Eq.derived - implicit def EitherPickler[L: Pickler, R: Pickler]: Pickler[Either[L, R]] = Pickler.derived - implicit def EitherShow[L: Show, R: Show]: Show[Either[L, R]] = Show.derived + //implicit def EitherEq[L: Eq, R: Eq]: Eq[Either[L, R]] = Eq.derived + //implicit def EitherPickler[L: Pickler, R: Pickler]: Pickler[Either[L, R]] = Pickler.derived + //implicit def EitherShow[L: Show, R: Show]: Show[Either[L, R]] = Show.derived } } From ceb5f2ea9bfcf9a4bf01331b564ac71da08cf968 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 30 Nov 2018 22:53:26 +0100 Subject: [PATCH 14/56] Flesh out derived$shaped --- .../dotty/tools/dotc/core/Definitions.scala | 2 ++ .../src/dotty/tools/dotc/core/StdNames.scala | 1 + .../src/dotty/tools/dotc/typer/Deriving.scala | 32 ++++++++++++++++--- .../test/dotc/run-test-pickling.blacklist | 1 + 4 files changed, 32 insertions(+), 4 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 719afc73d228..3839a276599c 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -657,6 +657,8 @@ class Definitions { def ShapeCaseClass(implicit ctx: Context): ClassSymbol = ShapeCaseType.symbol.asClass lazy val ShapeCasesType: TypeRef = ctx.requiredClassRef("scala.typelevel.Shape.Cases") def ShapeCasesClass(implicit ctx: Context): ClassSymbol = ShapeCasesType.symbol.asClass + lazy val MirrorType: TypeRef = ctx.requiredClassRef("scala.typelevel.Mirror") + lazy val ReflectedClassType: TypeRef = ctx.requiredClassRef("scala.typelevel.ReflectedClass") lazy val LanguageModuleRef: TermSymbol = ctx.requiredModule("scala.language") def LanguageModuleClass(implicit ctx: Context): ClassSymbol = LanguageModuleRef.moduleClass.asClass diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 3f9d36e6b5c3..3dada9b8a7c1 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -392,6 +392,7 @@ object StdNames { val ClassManifestFactory: N = "ClassManifestFactory" val classOf: N = "classOf" val clone_ : N = "clone" + val common: N = "common" val conforms_ : N = "$conforms" val copy: N = "copy" val currentMirror: N = "currentMirror" diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index a624b8575f0f..d9eb5d199bd3 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -114,6 +114,9 @@ trait Deriving { this: Typer => sym } + private def newMethod(name: TermName, info: Type, flags: FlagSet = EmptyFlags)(implicit ctx: Context) = + ctx.newSymbol(ctx.owner, name, flags | Method | Synthetic, info, coord = cls.pos.startPos) + /** Enter type class instance with given name and info in current scope, provided * an instance woth the same name does not exist already. */ @@ -124,7 +127,7 @@ trait Deriving { this: Typer => } else { val implFlag = if (reportErrors) Implicit else EmptyFlags // for now - add(ctx.newSymbol(ctx.owner, instanceName, Synthetic | Method | implFlag, info, coord = cls.pos.startPos)) + add(newMethod(instanceName, info, implFlag)) } } @@ -175,8 +178,29 @@ trait Deriving { this: Typer => addShape() } - def implementedClass(instance: Symbol) = - instance.info.stripPoly.finalResultType.classSymbol + private def shapedRHS(shapedType: Type, pos: Position)(implicit ctx: Context) = { + import tpd._ + val AppliedType(_, clsArg :: shapeArg :: Nil) = shapedType + val shape = shapeArg.dealias + + val implClassSym = ctx.newNormalizedClassSymbol( + ctx.owner, tpnme.ANON_CLASS, EmptyFlags, shapedType :: Nil, coord = cls.pos.startPos) + val implClassCtx = ctx.withOwner(implClassSym) + val implClassConstr = newMethod(nme.CONSTRUCTOR, MethodType(Nil, implClassSym.typeRef))(implClassCtx).entered + + def implClassStats(implicit ctx: Context): List[Tree] = { + val reflectMeth = newMethod(nme.reflect, MethodType(clsArg :: Nil, defn.MirrorType)).entered + val reifyMeth = newMethod(nme.reify, MethodType(defn.MirrorType :: Nil, clsArg)).entered + val commonMeth = newMethod(nme.common, ExprType(defn.ReflectedClassType)).entered + List( + tpd.DefDef(reflectMeth, tpd.ref(defn.Predef_undefinedR)), // TODO: flesh out + tpd.DefDef(reifyMeth, tpd.ref(defn.Predef_undefinedR)), + tpd.DefDef(commonMeth, tpd.ref(defn.Predef_undefinedR))) + } + + val implClassDef = ClassDef(implClassSym, DefDef(implClassConstr), implClassStats(implClassCtx)) + Block(implClassDef :: Nil, New(implClassSym.typeRef, Nil)) + } private def typeclassInstance(sym: Symbol)(implicit ctx: Context) = (tparamRefs: List[Type]) => (paramRefss: List[List[tpd.Tree]]) => { @@ -192,7 +216,7 @@ trait Deriving { this: Typer => val resultType = instantiated(sym.info) val typeCls = resultType.classSymbol if (typeCls == defn.ShapedClass) - tpd.ref(defn.Predef_undefinedR) // TODO: flesh out + shapedRHS(resultType, sym.pos) else { val module = untpd.ref(typeCls.companionModule.termRef).withPos(sym.pos) val rhs = untpd.Select(module, nme.derived) diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index aa1c42f1a07e..23b18bd7d78c 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -6,3 +6,4 @@ t7374 tuples1.scala tuples1a.scala typeclass-derivation2.scala +typeclass-derivation3.scala From 83bc248d5f3ca5c9c94a8694b44fe5ca16d2badb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 1 Dec 2018 14:41:26 +0100 Subject: [PATCH 15/56] More blacklisting and test fixing --- compiler/test/dotc/run-from-tasty.blacklist | 1 + tests/neg/typeclass-derivation2.scala | 176 ++++++++++---------- tests/run/typeclass-derivation2.scala | 45 ++--- 3 files changed, 118 insertions(+), 104 deletions(-) diff --git a/compiler/test/dotc/run-from-tasty.blacklist b/compiler/test/dotc/run-from-tasty.blacklist index a2fbf589a9c8..70871f46819c 100644 --- a/compiler/test/dotc/run-from-tasty.blacklist +++ b/compiler/test/dotc/run-from-tasty.blacklist @@ -8,4 +8,5 @@ puzzle.scala implicitMatch.scala typeclass-derivation1.scala typeclass-derivation2.scala +typeclass-derivation3.scala diff --git a/tests/neg/typeclass-derivation2.scala b/tests/neg/typeclass-derivation2.scala index 90c409e7922e..dc4bef9493ef 100644 --- a/tests/neg/typeclass-derivation2.scala +++ b/tests/neg/typeclass-derivation2.scala @@ -1,66 +1,88 @@ import scala.collection.mutable import scala.annotation.tailrec -trait Deriving { - import Deriving._ - - /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ - def mirror(ordinal: Int, product: Product): Mirror = - new Mirror(this, ordinal, product) - - /** A mirror with elements given as an array */ - def mirror(ordinal: Int, elems: Array[AnyRef]): Mirror = - mirror(ordinal, new ArrayProduct(elems)) - - /** A mirror with an initial empty array of `numElems` elements, to be filled in. */ - def mirror(ordinal: Int, numElems: Int): Mirror = - mirror(ordinal, new Array[AnyRef](numElems)) - - /** A mirror of a case with no elements */ - def mirror(ordinal: Int): Mirror = - mirror(ordinal, EmptyProduct) - - /** The case and element labels of the described ADT as encoded strings. */ - protected def caseLabels: Array[String] - - private final val separator = '\000' - - private def label(ordinal: Int, idx: Int): String = { - val labels = caseLabels(ordinal) - @tailrec def separatorPos(from: Int): Int = - if (from == labels.length || labels(from) == separator) from - else separatorPos(from + 1) - @tailrec def findLabel(count: Int, idx: Int): String = - if (idx == labels.length) "" - else if (count == 0) labels.substring(idx, separatorPos(idx)) - else findLabel(if (labels(idx) == separator) count - 1 else count, idx + 1) - findLabel(idx, 0) +object TypeLevel { + /** @param caseLabels The case and element labels of the described ADT as encoded strings. + */ + class ReflectedClass(labelsStr: String) { + import ReflectedClass._ + + /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ + def mirror(ordinal: Int, product: Product): Mirror = + new Mirror(this, ordinal, product) + + /** A mirror with elements given as an array */ + def mirror(ordinal: Int, elems: Array[AnyRef]): Mirror = + mirror(ordinal, new ArrayProduct(elems)) + + /** A mirror with an initial empty array of `numElems` elements, to be filled in. */ + def mirror(ordinal: Int, numElems: Int): Mirror = + mirror(ordinal, new Array[AnyRef](numElems)) + + /** A mirror of a case with no elements */ + def mirror(ordinal: Int): Mirror = + mirror(ordinal, EmptyProduct) + + val label: Array[Array[String]] = + initLabels(0, 0, new mutable.ArrayBuffer[String], new mutable.ArrayBuffer[Array[String]]) + + private final val elemSeparator = '\000' + private final val caseSeparator = '\001' + + private def initLabels(start: Int, cur: Int, + elems: mutable.ArrayBuffer[String], + cases: mutable.ArrayBuffer[Array[String]]): Array[Array[String]] = { + def addElem = elems += labelsStr.substring(start, cur) + def addCase = cases += addElem.toArray + if (cur == labelsStr.length) + addCase.toArray + else if (labelsStr(cur) == caseSeparator) + initLabels(cur + 1, cur + 1, new mutable.ArrayBuffer, addCase) + else if (labelsStr(cur) == elemSeparator) + initLabels(cur + 1, cur + 1, addElem, cases) + else + initLabels(start, cur + 1, elems, cases) + } } -} -// Generic deriving infrastructure -object Deriving { + object ReflectedClass { + /** Helper class to turn arrays into products */ + private class ArrayProduct(val elems: Array[AnyRef]) extends Product { + def canEqual(that: Any): Boolean = true + def productElement(n: Int) = elems(n) + def productArity = elems.length + override def productIterator: Iterator[Any] = elems.iterator + def update(n: Int, x: Any) = elems(n) = x.asInstanceOf[AnyRef] + } + + /** Helper object */ + private object EmptyProduct extends Product { + def canEqual(that: Any): Boolean = true + def productElement(n: Int) = throw new IndexOutOfBoundsException + def productArity = 0 + } + } /** A generic representation of a case in an ADT - * @param deriving The companion object of the ADT - * @param ordinal The ordinal value of the case in the list of the ADT's cases - * @param elems The elements of the case - */ - class Mirror(val deriving: Deriving, val ordinal: Int, val elems: Product) { + * @param deriving The companion object of the ADT + * @param ordinal The ordinal value of the case in the list of the ADT's cases + * @param elems The elements of the case + */ + class Mirror(val reflected: ReflectedClass, val ordinal: Int, val elems: Product) { /** The `n`'th element of this generic case */ def apply(n: Int): Any = elems.productElement(n) /** The name of the constructor of the case reflected by this mirror */ - def caseLabel: String = deriving.label(ordinal, 0) + def caseLabel: String = reflected.label(ordinal)(0) /** The label of the `n`'th element of the case reflected by this mirror */ - def elementLabel(n: Int) = deriving.label(ordinal, n + 1) + def elementLabel(n: Int) = reflected.label(ordinal)(n + 1) } /** A class for mapping between an ADT value and - * the case mirror that represents the value. - */ + * the case mirror that represents the value. + */ abstract class Reflected[T] { /** The case mirror corresponding to ADT instance `x` */ @@ -70,7 +92,7 @@ object Deriving { def reify(mirror: Mirror): T /** The companion object of the ADT */ - def deriving: Deriving + def common: ReflectedClass } /** The shape of an ADT. @@ -89,22 +111,6 @@ object Deriving { * It informs that type `T` has shape `S` and also implements runtime reflection on `T`. */ abstract class Shaped[T, S <: Shape] extends Reflected[T] - - /** Helper class to turn arrays into products */ - private class ArrayProduct(val elems: Array[AnyRef]) extends Product { - def canEqual(that: Any): Boolean = true - def productElement(n: Int) = elems(n) - def productArity = elems.length - override def productIterator: Iterator[Any] = elems.iterator - def update(n: Int, x: Any) = elems(n) = x.asInstanceOf[AnyRef] - } - - /** Helper object */ - private object EmptyProduct extends Product { - def canEqual(that: Any): Boolean = true - def productElement(n: Int) = throw new IndexOutOfBoundsException - def productArity = 0 - } } // An algebraic datatype @@ -113,15 +119,18 @@ enum Lst[+T] { case Nil } -object Lst extends Deriving { +object Lst { // common compiler-generated infrastructure - import Deriving._ + import TypeLevel._ type Shape[T] = Shape.Cases[( Shape.Case[Cons[T], (T, Lst[T])], Shape.Case[Nil.type, Unit] )] + val reflectedClass = new ReflectedClass("Cons\000hd\000tl\001Nil") + import reflectedClass.mirror + val NilMirror = mirror(1) implicit def lstShape[T]: Shaped[Lst[T], Shape[T]] = new { @@ -133,47 +142,49 @@ object Lst extends Deriving { case 0 => Cons[T](c(0).asInstanceOf, c(1).asInstanceOf) case 1 => Nil } - def deriving = Lst + def common = reflectedClass } - protected val caseLabels = Array("Cons\000hd\000tl", "Nil") - // three clauses that could be generated from a `derives` clause implicit def LstShow[T: Show]: Show[Lst[T]] = Show.derived } // A simple product type -case class Pair[T](x: T, y: T) +case class Pair[T](x: T, y: T) // derives Eq, Pickler, Show -object Pair extends Deriving { +object Pair { // common compiler-generated infrastructure - import Deriving._ + import TypeLevel._ type Shape[T] = Shape.Case[Pair[T], (T, T)] + val reflectedClass = new ReflectedClass("Pair\000x\000y") + import reflectedClass.mirror + implicit def pairShape[T]: Shaped[Pair[T], Shape[T]] = new { def reflect(xy: Pair[T]) = mirror(0, xy) def reify(c: Mirror): Pair[T] = Pair(c(0).asInstanceOf, c(1).asInstanceOf) - def deriving = Pair + def common = reflectedClass } - - protected val caseLabels = Array("Pair\000x\000y") } -sealed trait Either[+L, +R] extends Product +sealed trait Either[+L, +R] extends Product // derives Eq, Pickler, Show case class Left[L](x: L) extends Either[L, Nothing] case class Right[R](x: R) extends Either[Nothing, R] -object Either extends Deriving { - import Deriving._ +object Either { + import TypeLevel._ type Shape[L, R] = Shape.Cases[( Shape.Case[Left[L], L *: Unit], Shape.Case[Right[R], R *: Unit] )] + val reflectedClass = new ReflectedClass("Left\000x\001Right\000x") + import reflectedClass.mirror + implicit def eitherShape[L, R]: Shaped[Either[L, R], Shape[L, R]] = new { def reflect(e: Either[L, R]): Mirror = e match { case e: Left[L] => mirror(0, e) @@ -183,11 +194,8 @@ object Either extends Deriving { case 0 => Left[L](c(0).asInstanceOf) case 1 => Right[R](c(0).asInstanceOf) } - def deriving = Either + def common = reflectedClass } - - protected val caseLabels = Array("Left\000x", "Right\000x") - implicit def EitherShow[L: Show, R: Show]: Show[Either[L, R]] = Show.derived } @@ -195,8 +203,8 @@ trait Show[T] { def show(x: T): String } object Show { - import scala.typelevel._ - import Deriving._ + import scala.typelevel.erasedValue + import TypeLevel._ inline def tryShow[T](x: T): String = implicit match { case s: Show[T] => s.show(x) @@ -245,7 +253,7 @@ object Show { // Tests object Test extends App { - import Deriving._ + import TypeLevel._ def showPrintln[T: Show](x: T): Unit = println(implicitly[Show[T]].show(x)) diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index b939b953111b..04e9e2004606 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -4,7 +4,7 @@ import scala.annotation.tailrec object TypeLevel { /** @param caseLabels The case and element labels of the described ADT as encoded strings. */ - class ReflectedClass(caseLabels: Array[String]) { + class ReflectedClass(labelsStr: String) { import ReflectedClass._ /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ @@ -23,18 +23,25 @@ object TypeLevel { def mirror(ordinal: Int): Mirror = mirror(ordinal, EmptyProduct) - private final val separator = '\000' - - private[TypeLevel] def label(ordinal: Int, idx: Int): String = { - val labels = caseLabels(ordinal) - @tailrec def separatorPos(from: Int): Int = - if (from == labels.length || labels(from) == separator) from - else separatorPos(from + 1) - @tailrec def findLabel(count: Int, idx: Int): String = - if (idx == labels.length) "" - else if (count == 0) labels.substring(idx, separatorPos(idx)) - else findLabel(if (labels(idx) == separator) count - 1 else count, idx + 1) - findLabel(idx, 0) + val label: Array[Array[String]] = + initLabels(0, 0, new mutable.ArrayBuffer[String], new mutable.ArrayBuffer[Array[String]]) + + private final val elemSeparator = '\000' + private final val caseSeparator = '\001' + + private def initLabels(start: Int, cur: Int, + elems: mutable.ArrayBuffer[String], + cases: mutable.ArrayBuffer[Array[String]]): Array[Array[String]] = { + def addElem = elems += labelsStr.substring(start, cur) + def addCase = cases += addElem.toArray + if (cur == labelsStr.length) + addCase.toArray + else if (labelsStr(cur) == caseSeparator) + initLabels(cur + 1, cur + 1, new mutable.ArrayBuffer, addCase) + else if (labelsStr(cur) == elemSeparator) + initLabels(cur + 1, cur + 1, addElem, cases) + else + initLabels(start, cur + 1, elems, cases) } } @@ -67,10 +74,10 @@ object TypeLevel { def apply(n: Int): Any = elems.productElement(n) /** The name of the constructor of the case reflected by this mirror */ - def caseLabel: String = reflected.label(ordinal, 0) + def caseLabel: String = reflected.label(ordinal)(0) /** The label of the `n`'th element of the case reflected by this mirror */ - def elementLabel(n: Int) = reflected.label(ordinal, n + 1) + def elementLabel(n: Int) = reflected.label(ordinal)(n + 1) } /** A class for mapping between an ADT value and @@ -121,7 +128,7 @@ object Lst { Shape.Case[Nil.type, Unit] )] - val reflectedClass = new ReflectedClass(Array("Cons\000hd\000tl", "Nil")) + val reflectedClass = new ReflectedClass("Cons\000hd\000tl\001Nil") import reflectedClass.mirror val NilMirror = mirror(1) @@ -138,8 +145,6 @@ object Lst { def common = reflectedClass } - protected val caseLabels = Array("Cons\000hd\000tl", "Nil") - // three clauses that could be generated from a `derives` clause implicit def LstEq[T: Eq]: Eq[Lst[T]] = Eq.derived implicit def LstPickler[T: Pickler]: Pickler[Lst[T]] = Pickler.derived @@ -155,7 +160,7 @@ object Pair { type Shape[T] = Shape.Case[Pair[T], (T, T)] - val reflectedClass = new ReflectedClass(Array("Pair\000x\000y")) + val reflectedClass = new ReflectedClass("Pair\000x\000y") import reflectedClass.mirror implicit def pairShape[T]: Shaped[Pair[T], Shape[T]] = new { @@ -184,7 +189,7 @@ object Either { Shape.Case[Right[R], R *: Unit] )] - val reflectedClass = new ReflectedClass(Array("Left\000x", "Right\000x")) + val reflectedClass = new ReflectedClass("Left\000x\001Right\000x") import reflectedClass.mirror implicit def eitherShape[L, R]: Shaped[Either[L, R], Shape[L, R]] = new { From 6e48ee19656b7cfd7d1eaf91d1dc4cea93e5ab03 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Dec 2018 14:21:01 +0100 Subject: [PATCH 16/56] Complete first version of derivation scheme --- .../src/dotty/tools/dotc/config/Config.scala | 2 +- .../src/dotty/tools/dotc/core/StdNames.scala | 4 +- .../src/dotty/tools/dotc/typer/Deriving.scala | 346 +++++++++++++----- .../src/dotty/tools/dotc/typer/Namer.scala | 2 +- .../src-scala3/scala/typelevel/Mirror.scala | 4 +- .../scala/typelevel/ReflectedClass.scala | 44 ++- tests/run/typeclass-derivation2.scala | 28 +- tests/run/typeclass-derivation3.scala | 83 +---- 8 files changed, 312 insertions(+), 201 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala index 4366ac45170f..4cb9369653e2 100644 --- a/compiler/src/dotty/tools/dotc/config/Config.scala +++ b/compiler/src/dotty/tools/dotc/config/Config.scala @@ -161,7 +161,7 @@ object Config { final val showCompletions = false /** If set, enables tracing */ - final val tracingEnabled = false + final val tracingEnabled = true /** Initial capacity of uniques HashMap. * Note: This MUST BE a power of two to work with util.HashSet diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 3dada9b8a7c1..a2888a43ddf5 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -484,6 +484,7 @@ object StdNames { val null_ : N = "null" val ofDim: N = "ofDim" val opaque: N = "opaque" + val ordinal: N = "ordinal" val origin: N = "origin" val prefix : N = "prefix" val productArity: N = "productArity" @@ -493,7 +494,8 @@ object StdNames { val productPrefix: N = "productPrefix" val raw_ : N = "raw" val readResolve: N = "readResolve" - val reflect : N = "reflect" + val reflect: N = "reflect" + val reflectedClass: N = "reflectedClass" val reflectiveSelectable: N = "reflectiveSelectable" val reify : N = "reify" val rootMirror : N = "rootMirror" diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index d9eb5d199bd3..fe4d4ce26328 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -7,28 +7,41 @@ import ast._ import ast.Trees._ import StdNames._ import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._, Decorators._ -import NameKinds.DefaultGetterName -import ast.desugar, ast.desugar._ import ProtoTypes._ import util.Positions._ -import util.Property import collection.mutable -import tpd.ListOfTreeDecorator -import config.Config +import Constants.Constant import config.Printers.typr -import Annotations._ import Inferencing._ -import transform.ValueClasses._ import transform.TypeUtils._ import transform.SymUtils._ -import reporting.diagnostic.messages._ + +// TODOs: +// - handle case where there's no companion object +// - check that derived instances are stable +// - reference derived instances with correct prefix instead of just the symbol + +/** A typer mixin that implements typeclass derivation functionality */ trait Deriving { this: Typer => - class Deriver(cls: ClassSymbol)(implicit ctx: Context) { + /** A helper class to derive type class instances for one class or object + * @param cls The class symbol of the class or object with a `derives` clause + * @param templateStartPos The default position that should be given to generic + * synthesized infrastructure code that is not connected with a + * `derives` instance. + */ + class Deriver(cls: ClassSymbol, templateStartPos: Position)(implicit ctx: Context) { + /** A buffer for synthesized symbols */ private var synthetics = new mutable.ListBuffer[Symbol] + /** the children of `cls` ordered by textual occurrence */ + lazy val children = cls.children.sortBy(_.pos.start) + + /** The shape (of type Shape.Case) of a case given by `sym`. `sym` is either `cls` + * itself, or a subclass of `cls`, or an instance of `cls`. + */ private def caseShape(sym: Symbol): Type = { val (constr, elems) = sym match { @@ -36,7 +49,7 @@ trait Deriving { this: Typer => caseClass.primaryConstructor.info match { case info: PolyType => def instantiate(implicit ctx: Context) = { - val poly = constrained(info, untpd.EmptyTree, alwaysAddTypeVars = true)._1 + val poly = constrained(info, untpd.EmptyTree)._1 val mono @ MethodType(_) = poly.resultType val resType = mono.finalResultType resType <:< cls.appliedRef @@ -47,7 +60,7 @@ trait Deriving { this: Typer => (resType.substParams(poly, instanceTypes), mono.paramInfos.map(_.substParams(poly, instanceTypes))) } - instantiate(ctx.fresh.setNewTyperState().setOwner(caseClass)) + instantiate(ctx.fresh.setExploreTyperState().setOwner(caseClass)) case info: MethodType => (cls.typeRef, info.paramInfos) case _ => @@ -60,29 +73,23 @@ trait Deriving { this: Typer => defn.ShapeCaseType.appliedTo(constr, elemShape) } - lazy val children = cls.children.sortBy(_.pos.start) - + /** The shape of `cls` if `cls` is sealed */ private def sealedShape: Type = { val cases = children.map(caseShape) val casesShape = (cases :\ (defn.UnitType: Type))(defn.PairType.appliedTo(_, _)) defn.ShapeCasesType.appliedTo(casesShape) } + /** The shape of `cls`, referring directly to the type parameters of `cls` instead + * of abstracting over them. + * Returns NoType if `cls` is neither sealed nor a case class or object. + */ lazy val shapeWithClassParams: Type = if (cls.is(Case)) caseShape(cls) else if (cls.is(Sealed)) sealedShape else NoType - lazy val shape: Type = shapeWithClassParams match { - case delayed: LazyRef => HKTypeLambda.fromParams(cls.typeParams, delayed.ref) - case NoType => NoType - } - - lazy val lazyShape: Type = shapeWithClassParams match { - case delayed: LazyRef => HKTypeLambda.fromParams(cls.typeParams, delayed) - case NoType => NoType - } - + /** A completer for the synthesized `Shape` type. */ class ShapeCompleter extends TypeParamsCompleter { override def completerTypeParams(sym: Symbol)(implicit ctx: Context) = cls.typeParams @@ -92,7 +99,7 @@ trait Deriving { this: Typer => val tparams = cls.typeParams val abstractedShape = if (!shape0.exists) { - ctx.error(em"Cannot derive for $cls; it is neither sealed nor a case class or object", cls.pos) + ctx.error(em"Cannot derive for $cls; it is neither sealed nor a case class or object", templateStartPos) UnspecifiedErrorType } else if (tparams.isEmpty) @@ -114,30 +121,48 @@ trait Deriving { this: Typer => sym } - private def newMethod(name: TermName, info: Type, flags: FlagSet = EmptyFlags)(implicit ctx: Context) = - ctx.newSymbol(ctx.owner, name, flags | Method | Synthetic, info, coord = cls.pos.startPos) + /** Create a synthetic symbol owned by current owner */ + private def newSymbol(name: Name, info: Type, pos: Position, flags: FlagSet = EmptyFlags)(implicit ctx: Context) = + ctx.newSymbol(ctx.owner, name, flags | Synthetic, info, coord = pos) + + /** Create a synthetic method owned by current owner */ + private def newMethod(name: TermName, info: Type, + pos: Position = ctx.owner.pos, + flags: FlagSet = EmptyFlags)(implicit ctx: Context): TermSymbol = + newSymbol(name, info, pos, flags | Method).asTerm /** Enter type class instance with given name and info in current scope, provided - * an instance woth the same name does not exist already. - */ - private def addDerivedInstance(clsName: Name, info: Type, reportErrors: Boolean, implicitFlag: FlagSet = Implicit) = { + * an instance with the same name does not exist already. + * @param reportErrors Report an error if an instance with the same name exists already + */ + private def addDerivedInstance(clsName: Name, info: Type, pos: Position, reportErrors: Boolean) = { val instanceName = s"derived$$$clsName".toTermName if (ctx.denotNamed(instanceName).exists) { if (reportErrors) ctx.error(i"duplicate typeclass derivation for $clsName") } - else { - val implFlag = if (reportErrors) Implicit else EmptyFlags // for now - add(newMethod(instanceName, info, implFlag)) - } + else add(newMethod(instanceName, info, pos, Implicit)) } /* Check derived type tree `derived` for the following well-formedness conditions: * (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix) * (2) It must have exactly one type parameter * If it passes the checks, enter a typeclass instance for it in the current scope. + * Given + * + * class C[Ts] .... derives ... D ... + * + * where `T_1, ..., T_n` are the first-kinded type parameters in `Ts`, + * the typeclass instance has the form + * + * implicit def derived$D(implicit ev_1: D[T1], ..., ev_n: D[T_n]): D[C[Ts]] = D.derived + * + * See test run/typeclass-derivation2 for examples that spell out what would be generated. + * Note that the name of the derived method containd the name in the derives clause, not + * the underlying class name. This allows one to disambiguate derivations of type classes + * that have the same name but different prefixes through selective aliasing. */ private def processDerivedInstance(derived: untpd.Tree): Unit = { - val uncheckedType = typedAheadType(derived, AnyTypeConstructorProto).tpe.dealiasKeepAnnots + val uncheckedType = typedAheadType(derived, AnyTypeConstructorProto).tpe.dealias val derivedType = checkClassType(uncheckedType, derived.pos, traitReq = false, stablePrefixReq = true) val nparams = derivedType.classSymbol.typeParams.length if (nparams == 1) { @@ -147,9 +172,9 @@ trait Deriving { this: Typer => for (param <- firstKindedParams) yield derivedType.appliedTo(param.typeRef) val resultType = derivedType.appliedTo(cls.appliedRef) val instanceInfo = - if (evidenceParamInfos.isEmpty) ExprType(resultType) - else PolyType.fromParams(firstKindedParams, ImplicitMethodType(evidenceParamInfos, resultType)) - addDerivedInstance(derivedType.typeSymbol.name, instanceInfo, reportErrors = true) + if (cls.typeParams.isEmpty) ExprType(resultType) + else PolyType.fromParams(cls.typeParams, ImplicitMethodType(evidenceParamInfos, resultType)) + addDerivedInstance(derivedType.typeSymbol.name, instanceInfo, derived.pos, reportErrors = true) } else ctx.error( @@ -157,82 +182,229 @@ trait Deriving { this: Typer => derived.pos) } + /** Add value corresponding to `val reflectedClass = new ReflectedClass(...)` + * to `synthetics`, unless a definition of `reflectedClass` exists already. + */ + private def addReflectedClass(): Unit = + if (!ctx.denotNamed(nme.reflectedClass).exists) { + add(newSymbol(nme.reflectedClass, defn.ReflectedClassType, templateStartPos)) + } + + /** Add `type Shape = ... ` type to `synthetics`, unless a definition of type `Shape` exists already */ private def addShape(): Unit = if (!ctx.denotNamed(tpnme.Shape).exists) { - val shapeSym = add(ctx.newSymbol(ctx.owner, tpnme.Shape, EmptyFlags, new ShapeCompleter)) - val shapedCls = defn.ShapedClass + val shapeSym = add(newSymbol(tpnme.Shape, new ShapeCompleter, templateStartPos)) val lazyShapedInfo = new LazyType { def complete(denot: SymDenotation)(implicit ctx: Context) = { val tparams = cls.typeParams - val shapedType = shapedCls.typeRef.appliedTo( - cls.appliedRef, - shapeSym.typeRef.appliedTo(tparams.map(_.typeRef))) + val appliedShape = shapeSym.typeRef.appliedTo(tparams.map(_.typeRef)) + val shapedType = defn.ShapedType.appliedTo(cls.appliedRef, appliedShape) denot.info = PolyType.fromParams(tparams, shapedType).ensureMethodic } } - addDerivedInstance(shapedCls.name, lazyShapedInfo, reportErrors = false) + addDerivedInstance(defn.ShapedType.name, lazyShapedInfo, templateStartPos, reportErrors = false) } + /** Create symbols for derived instances and infrastructure, + * append them to `synthetics` buffer, + * and enter them into class scope. + */ def enterDerived(derived: List[untpd.Tree]) = { derived.foreach(processDerivedInstance(_)) addShape() + addReflectedClass() } - private def shapedRHS(shapedType: Type, pos: Position)(implicit ctx: Context) = { - import tpd._ - val AppliedType(_, clsArg :: shapeArg :: Nil) = shapedType - val shape = shapeArg.dealias - - val implClassSym = ctx.newNormalizedClassSymbol( - ctx.owner, tpnme.ANON_CLASS, EmptyFlags, shapedType :: Nil, coord = cls.pos.startPos) - val implClassCtx = ctx.withOwner(implClassSym) - val implClassConstr = newMethod(nme.CONSTRUCTOR, MethodType(Nil, implClassSym.typeRef))(implClassCtx).entered - - def implClassStats(implicit ctx: Context): List[Tree] = { - val reflectMeth = newMethod(nme.reflect, MethodType(clsArg :: Nil, defn.MirrorType)).entered - val reifyMeth = newMethod(nme.reify, MethodType(defn.MirrorType :: Nil, clsArg)).entered - val commonMeth = newMethod(nme.common, ExprType(defn.ReflectedClassType)).entered - List( - tpd.DefDef(reflectMeth, tpd.ref(defn.Predef_undefinedR)), // TODO: flesh out - tpd.DefDef(reifyMeth, tpd.ref(defn.Predef_undefinedR)), - tpd.DefDef(commonMeth, tpd.ref(defn.Predef_undefinedR))) + private def tupleElems(tp: Type): List[Type] = tp match { + case AppliedType(fn, hd :: tl :: Nil) if fn.classSymbol == defn.PairClass => + hd :: tupleElems(tl) + case _ => + Nil + } + + /** Extractor for the `cases` in a `Shaped.Cases(cases)` shape */ + private object ShapeCases { + def unapply(shape: Type): Option[List[Type]] = shape match { + case AppliedType(fn, cases :: Nil) if fn.classSymbol == defn.ShapeCasesClass => + Some(tupleElems(cases)) + case _ => + None } + } - val implClassDef = ClassDef(implClassSym, DefDef(implClassConstr), implClassStats(implClassCtx)) - Block(implClassDef :: Nil, New(implClassSym.typeRef, Nil)) + /** Extractor for the `pattern` and `elements` in a `Shaped.Case(pattern, elements)` shape */ + private object ShapeCase { + def unapply(shape: Type): Option[(Type, List[Type])] = shape match { + case AppliedType(fn, pat :: elems :: Nil) if fn.classSymbol == defn.ShapeCaseClass => + Some((pat, tupleElems(elems))) + case _ => + None + } } - private def typeclassInstance(sym: Symbol)(implicit ctx: Context) = - (tparamRefs: List[Type]) => (paramRefss: List[List[tpd.Tree]]) => { - val tparams = tparamRefs.map(_.typeSymbol.asType) - val params = if (paramRefss.isEmpty) Nil else paramRefss.head.map(_.symbol.asTerm) - tparams.foreach(ctx.enter) - params.foreach(ctx.enter) - def instantiated(info: Type): Type = info match { - case info: PolyType => instantiated(info.instantiate(tparamRefs)) - case info: MethodType => info.instantiate(params.map(_.termRef)) - case info => info - } - val resultType = instantiated(sym.info) - val typeCls = resultType.classSymbol - if (typeCls == defn.ShapedClass) - shapedRHS(resultType, sym.pos) - else { - val module = untpd.ref(typeCls.companionModule.termRef).withPos(sym.pos) - val rhs = untpd.Select(module, nme.derived) - typed(rhs, resultType) + /** A helper class to create definition trees for `synthetics` */ + class Finalizer { + import tpd._ + + /** The previously synthetsized `reflectedClass` symbol */ + private def reflectedClass = + synthetics.find(sym => !sym.is(Method) && sym.name == nme.reflectedClass).get.asTerm + + /** The string to pass to `ReflectedClass` for initializing case and element labels. + * See documentation of `ReflectedClass.label` for what needs to be passed. + */ + private def labelString(sh: Type): String = sh match { + case ShapeCases(cases) => + cases.map(labelString).mkString("\u0001") + case ShapeCase(pat: TermRef, _) => + pat.symbol.name.toString + case ShapeCase(pat, elems) => + val patCls = pat.widen.classSymbol + val patLabel = patCls.name.stripModuleClassSuffix.toString + val elemLabels = patCls.caseAccessors.map(_.name.toString) + (patLabel :: elemLabels).mkString("\u0000") + } + + /** The RHS of the `reflectedClass` value definition */ + private def reflectedClassRHS = + New(defn.ReflectedClassType, Literal(Constant(labelString(shapeWithClassParams))) :: Nil) + + /** The RHS of the `derived$Shaped` typeclass instance. + * Example: For the class definition + * + * enum Lst[+T] derives ... { case Cons(hd: T, tl: Lst[T]); case Nil } + * + * the following typeclass instance is generated: + * + * implicit def derived$Shape[T]: Shaped[Lst[T], Shape[T]] = new { + * def reflect(x$0: Lst[T]): Mirror = x$0 match { + * case x$0: Cons[T] => reflectedClass.mirror(0, x$0) + * case x$0: Nil.type => reflectedClass.mirror(1) + * } + * def reify(c: Mirror): Lst[T] = c.ordinal match { + * case 0 => Cons[T](c(0).asInstanceOf[T], c(1).asInstanceOf[Lst[T]]) + * case 1 => Nil + * } + * def common = reflectedClass + * } + */ + private def shapedRHS(shapedType: Type)(implicit ctx: Context) = { + val AppliedType(_, clsArg :: shapeArg :: Nil) = shapedType + val shape = shapeArg.dealias + + val implClassSym = ctx.newNormalizedClassSymbol( + ctx.owner, tpnme.ANON_CLASS, EmptyFlags, shapedType :: Nil, coord = templateStartPos) + val implClassCtx = ctx.withOwner(implClassSym) + val implClassConstr = + newMethod(nme.CONSTRUCTOR, MethodType(Nil, implClassSym.typeRef))(implClassCtx).entered + + def implClassStats(implicit ctx: Context): List[Tree] = { + + val reflectMethod: DefDef = { + val meth = newMethod(nme.reflect, MethodType(clsArg :: Nil, defn.MirrorType)).entered + def rhs(paramRef: Tree)(implicit ctx: Context): Tree = { + def reflectCase(scrut: Tree, idx: Int, elems: List[Type]): Tree = { + val ordinal = Literal(Constant(idx)) + val args = if (elems.isEmpty) List(ordinal) else List(ordinal, scrut) + val mirror = defn.ReflectedClassType + .member(nme.mirror) + .suchThat(sym => args.tpes.corresponds(sym.info.firstParamTypes)(_ <:< _)) + ref(reflectedClass).select(mirror.symbol).appliedToArgs(args) + } + shape match { + case ShapeCases(cases) => + val clauses = cases.zipWithIndex.map { + case (ShapeCase(pat, elems), idx) => + val patVar = newSymbol(nme.syntheticParamName(0), pat, meth.pos) + CaseDef( + Bind(patVar, Typed(untpd.Ident(nme.WILDCARD).withType(pat), TypeTree(pat))), + EmptyTree, + reflectCase(ref(patVar), idx, elems)) + } + Match(paramRef, clauses) + case ShapeCase(pat, elems) => + reflectCase(paramRef, 0, elems) + } + } + tpd.DefDef(meth, paramss => rhs(paramss.head.head)(ctx.fresh.setOwner(meth).setNewScope)) + } + + val reifyMethod: DefDef = { + val meth = newMethod(nme.reify, MethodType(defn.MirrorType :: Nil, clsArg)).entered + def rhs(paramRef: Tree)(implicit ctx: Context): Tree = { + def reifyCase(caseType: Type, elems: List[Type]): Tree = caseType match { + case caseType: TermRef => + ref(caseType) + case caseType => + val args = + for ((elemTp, idx) <- elems.zipWithIndex) + yield paramRef.select(nme.apply).appliedTo(Literal(Constant(idx))).asInstance(elemTp) + New(caseType, args) + } + shape match { + case ShapeCases(cases) => + val clauses = + for ((ShapeCase(pat, elems), idx) <- cases.zipWithIndex) + yield CaseDef(Literal(Constant(idx)), EmptyTree, reifyCase(pat, elems)) + Match(paramRef.select(nme.ordinal), clauses) + case ShapeCase(pat, elems) => + reifyCase(pat, elems) + } + } + + tpd.DefDef(meth, paramss => rhs(paramss.head.head)(ctx.withOwner(meth))) + } + + val commonMethod: DefDef = { + val meth = newMethod(nme.common, ExprType(defn.ReflectedClassType)).entered + tpd.DefDef(meth, ref(reflectedClass)) + } + + List(reflectMethod, reifyMethod, commonMethod) } + + val implClassDef = ClassDef(implClassSym, DefDef(implClassConstr), implClassStats(implClassCtx)) + Block(implClassDef :: Nil, New(implClassSym.typeRef, Nil)) } - def syntheticDef(sym: Symbol): tpd.Tree = - if (sym.isType) tpd.TypeDef(sym.asType) - else tpd.polyDefDef(sym.asTerm, typeclassInstance(sym)(ctx.fresh.setOwner(sym).setNewScope)) + /** The type class instance definition with symbol `sym` */ + private def typeclassInstance(sym: Symbol)(implicit ctx: Context) = + (tparamRefs: List[Type]) => (paramRefss: List[List[tpd.Tree]]) => { + val tparams = tparamRefs.map(_.typeSymbol.asType) + val params = if (paramRefss.isEmpty) Nil else paramRefss.head.map(_.symbol.asTerm) + tparams.foreach(ctx.enter) + params.foreach(ctx.enter) + def instantiated(info: Type): Type = info match { + case info: PolyType => instantiated(info.instantiate(tparamRefs)) + case info: MethodType => info.instantiate(params.map(_.termRef)) + case info => info + } + val resultType = instantiated(sym.info) + val typeCls = resultType.classSymbol + if (typeCls == defn.ShapedClass) + shapedRHS(resultType) + else { + val module = untpd.ref(typeCls.companionModule.termRef).withPos(sym.pos) + val rhs = untpd.Select(module, nme.derived) + typed(rhs, resultType) + } + } + + def syntheticDef(sym: Symbol): Tree = + if (sym.isType) + tpd.TypeDef(sym.asType) + else if (sym.is(Method)) + tpd.polyDefDef(sym.asTerm, typeclassInstance(sym)(ctx.fresh.setOwner(sym).setNewScope)) + else + tpd.ValDef(sym.asTerm, reflectedClassRHS) + + def syntheticDefs: List[Tree] = synthetics.map(syntheticDef).toList + } def finalize(stat: tpd.TypeDef): tpd.Tree = { val templ @ Template(_, _, _, _) = stat.rhs - val newDefs = synthetics.map(syntheticDef) tpd.cpy.TypeDef(stat)( - rhs = tpd.cpy.Template(templ)(body = templ.body ++ newDefs)) + rhs = tpd.cpy.Template(templ)(body = templ.body ++ new Finalizer().syntheticDefs)) } } } diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index e14ce1005a7f..9b723d8c6ee7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -993,7 +993,7 @@ class Namer { typer: Typer => val derivingClass = if (original.removeAttachment(DerivingCompanion).isDefined) cls.companionClass.asClass else cls - val deriver = new Deriver(derivingClass)(localCtx) + val deriver = new Deriver(derivingClass, impl.pos.startPos)(localCtx) deriver.enterDerived(impl.derived) original.putAttachment(Deriver, deriver) } diff --git a/library/src-scala3/scala/typelevel/Mirror.scala b/library/src-scala3/scala/typelevel/Mirror.scala index 44b29c48010b..7401d8f1f89d 100644 --- a/library/src-scala3/scala/typelevel/Mirror.scala +++ b/library/src-scala3/scala/typelevel/Mirror.scala @@ -11,8 +11,8 @@ class Mirror(val reflected: ReflectedClass, val ordinal: Int, val elems: Product def apply(n: Int): Any = elems.productElement(n) /** The name of the constructor of the case reflected by this mirror */ - def caseLabel: String = reflected.label(ordinal, 0) + def caseLabel: String = reflected.label(ordinal)(0) /** The label of the `n`'th element of the case reflected by this mirror */ - def elementLabel(n: Int) = reflected.label(ordinal, n + 1) + def elementLabel(n: Int) = reflected.label(ordinal)(n + 1) } diff --git a/library/src-scala3/scala/typelevel/ReflectedClass.scala b/library/src-scala3/scala/typelevel/ReflectedClass.scala index 0fc896ff678e..a76229cb423c 100644 --- a/library/src-scala3/scala/typelevel/ReflectedClass.scala +++ b/library/src-scala3/scala/typelevel/ReflectedClass.scala @@ -1,9 +1,17 @@ package scala.typelevel import annotation.tailrec +import collection.mutable.ArrayBuffer -/** @param caseLabels The case and element labels of the described ADT as encoded strings. -*/ -class ReflectedClass(caseLabels: Array[String]) { +/** @param labelsStr: A string encoding all case and element labels according to the + * following grammar: + * + * labelString ::= caseString { caseSeparator caseString } + * caseString ::= elemString { elemSeparator elemString } + * caseSeparator ::= '\001' + * elemSeparator ::= '\000' + * elemString: "any sequence of characters not containing '\000` or `\001`" + */ +class ReflectedClass(labelsStr: String) { import ReflectedClass._ /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ @@ -22,22 +30,28 @@ class ReflectedClass(caseLabels: Array[String]) { def mirror(ordinal: Int): Mirror = mirror(ordinal, EmptyProduct) - private[typelevel] def label(ordinal: Int, idx: Int): String = { - val labels = caseLabels(ordinal) - @tailrec def separatorPos(from: Int): Int = - if (from == labels.length || labels(from) == separator) from - else separatorPos(from + 1) - @tailrec def findLabel(count: Int, idx: Int): String = - if (idx == labels.length) "" - else if (count == 0) labels.substring(idx, separatorPos(idx)) - else findLabel(if (labels(idx) == separator) count - 1 else count, idx + 1) - findLabel(idx, 0) + val label: Array[Array[String]] = + initLabels(0, 0, new ArrayBuffer[String], new ArrayBuffer[Array[String]]) + + private def initLabels(start: Int, cur: Int, + elems: ArrayBuffer[String], + cases: ArrayBuffer[Array[String]]): Array[Array[String]] = { + def addElem = elems += labelsStr.substring(start, cur) + def addCase = cases += addElem.toArray + if (cur == labelsStr.length) + addCase.toArray + else if (labelsStr(cur) == caseSeparator) + initLabels(cur + 1, cur + 1, new ArrayBuffer, addCase) + else if (labelsStr(cur) == elemSeparator) + initLabels(cur + 1, cur + 1, addElem, cases) + else + initLabels(start, cur + 1, elems, cases) } } object ReflectedClass { - - private final val separator = '\000' + private final val elemSeparator = '\000' + private final val caseSeparator = '\001' /** Helper class to turn arrays into products */ private class ArrayProduct(val elems: Array[AnyRef]) extends Product { diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index 04e9e2004606..349d45623bb7 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -133,7 +133,7 @@ object Lst { val NilMirror = mirror(1) - implicit def lstShape[T]: Shaped[Lst[T], Shape[T]] = new { + implicit def derived$Shape[T]: Shaped[Lst[T], Shape[T]] = new { def reflect(xs: Lst[T]): Mirror = xs match { case xs: Cons[T] => mirror(0, xs) case Nil => NilMirror @@ -146,9 +146,9 @@ object Lst { } // three clauses that could be generated from a `derives` clause - implicit def LstEq[T: Eq]: Eq[Lst[T]] = Eq.derived - implicit def LstPickler[T: Pickler]: Pickler[Lst[T]] = Pickler.derived - implicit def LstShow[T: Show]: Show[Lst[T]] = Show.derived + implicit def derived$Eq[T: Eq]: Eq[Lst[T]] = Eq.derived + implicit def derived$Pickler[T: Pickler]: Pickler[Lst[T]] = Pickler.derived + implicit def derived$Show[T: Show]: Show[Lst[T]] = Show.derived } // A simple product type @@ -163,7 +163,7 @@ object Pair { val reflectedClass = new ReflectedClass("Pair\000x\000y") import reflectedClass.mirror - implicit def pairShape[T]: Shaped[Pair[T], Shape[T]] = new { + implicit def derived$Shape[T]: Shaped[Pair[T], Shape[T]] = new { def reflect(xy: Pair[T]) = mirror(0, xy) def reify(c: Mirror): Pair[T] = @@ -172,9 +172,9 @@ object Pair { } // two clauses that could be generated from a `derives` clause - implicit def PairEq[T: Eq]: Eq[Pair[T]] = Eq.derived - implicit def PairPickler[T: Pickler]: Pickler[Pair[T]] = Pickler.derived - implicit def PairShow[T: Show]: Show[Pair[T]] = Show.derived + implicit def derived$Eq[T: Eq]: Eq[Pair[T]] = Eq.derived + implicit def derived$Pickler[T: Pickler]: Pickler[Pair[T]] = Pickler.derived + implicit def derived$Show[T: Show]: Show[Pair[T]] = Show.derived } sealed trait Either[+L, +R] extends Product // derives Eq, Pickler, Show @@ -192,21 +192,21 @@ object Either { val reflectedClass = new ReflectedClass("Left\000x\001Right\000x") import reflectedClass.mirror - implicit def eitherShape[L, R]: Shaped[Either[L, R], Shape[L, R]] = new { + implicit def derived$Shape[L, R]: Shaped[Either[L, R], Shape[L, R]] = new { def reflect(e: Either[L, R]): Mirror = e match { case e: Left[L] => mirror(0, e) case e: Right[R] => mirror(1, e) } def reify(c: Mirror): Either[L, R] = c.ordinal match { - case 0 => Left[L](c(0).asInstanceOf) - case 1 => Right[R](c(0).asInstanceOf) + case 0 => Left(c(0).asInstanceOf) + case 1 => Right(c(0).asInstanceOf) } def common = reflectedClass } - implicit def EitherEq[L: Eq, R: Eq]: Eq[Either[L, R]] = Eq.derived - implicit def EitherPickler[L: Pickler, R: Pickler]: Pickler[Either[L, R]] = Pickler.derived - implicit def EitherShow[L: Show, R: Show]: Show[Either[L, R]] = Show.derived + implicit def derived$Eq[L: Eq, R: Eq]: Eq[Either[L, R]] = Eq.derived + implicit def derived$Pickler[L: Pickler, R: Pickler]: Pickler[Either[L, R]] = Pickler.derived + implicit def derived$Show[L: Show, R: Show]: Show[Either[L, R]] = Show.derived } // A typeclass diff --git a/tests/run/typeclass-derivation3.scala b/tests/run/typeclass-derivation3.scala index 84313706a244..cfd25ed3a0a2 100644 --- a/tests/run/typeclass-derivation3.scala +++ b/tests/run/typeclass-derivation3.scala @@ -9,95 +9,18 @@ object datatypes { case Nil } - object Lst { - // common compiler-generated infrastructure - import typelevel._ -/* - type Shape[T] = Shape.Cases[( - Shape.Case[Cons[T], (T, Lst[T])], - Shape.Case[Nil.type, Unit] - )] -*/ - val reflectedClass = new ReflectedClass(Array("Cons\000hd\000tl", "Nil")) - import reflectedClass.mirror - - val NilMirror = mirror(1) - - implicit def lstShape[T]: Shaped[Lst[T], Shape[T]] = new { - def reflect(xs: Lst[T]): Mirror = xs match { - case xs: Cons[T] => mirror(0, xs) - case Nil => NilMirror - } - def reify(c: Mirror): Lst[T] = c.ordinal match { - case 0 => Cons[T](c(0).asInstanceOf, c(1).asInstanceOf) - case 1 => Nil - } - def common = reflectedClass - } - - // three clauses that could be generated from a `derives` clause - //implicit def LstEq[T: Eq]: Eq[Lst[T]] = Eq.derived - //implicit def LstPickler[T: Pickler]: Pickler[Lst[T]] = Pickler.derived - //implicit def LstShow[T: Show]: Show[Lst[T]] = Show.derived - } + object Lst {} // A simple product type case class Pair[T](x: T, y: T) derives Eq, Pickler, Show + object Pair - object Pair { - // common compiler-generated infrastructure - import typelevel._ - - // type Shape[T] = Shape.Case[Pair[T], (T, T)] - - val reflectedClass = new ReflectedClass(Array("Pair\000x\000y")) - import reflectedClass.mirror - - implicit def pairShape[T]: Shaped[Pair[T], Shape[T]] = new { - def reflect(xy: Pair[T]) = - mirror(0, xy) - def reify(c: Mirror): Pair[T] = - Pair(c(0).asInstanceOf, c(1).asInstanceOf) - def common = reflectedClass - } - - // two clauses that could be generated from a `derives` clause - //implicit def PairEq[T: Eq]: Eq[Pair[T]] = Eq.derived - //implicit def PairPickler[T: Pickler]: Pickler[Pair[T]] = Pickler.derived - //implicit def PairShow[T: Show]: Show[Pair[T]] = Show.derived - } - + // another ADT sealed trait Either[+L, +R] extends Product derives Eq, Pickler, Show case class Left[L](x: L) extends Either[L, Nothing] case class Right[R](x: R) extends Either[Nothing, R] object Either { - import typelevel._ - - /* - type Shape[L, R] = Shape.Cases[( - Shape.Case[Left[L], L *: Unit], - Shape.Case[Right[R], R *: Unit] - )] -*/ - val reflectedClass = new ReflectedClass(Array("Left\000x", "Right\000x")) - import reflectedClass.mirror - - implicit def eitherShape[L, R]: Shaped[Either[L, R], Shape[L, R]] = new { - def reflect(e: Either[L, R]): Mirror = e match { - case e: Left[L] => mirror(0, e) - case e: Right[R] => mirror(1, e) - } - def reify(c: Mirror): Either[L, R] = c.ordinal match { - case 0 => Left[L](c(0).asInstanceOf) - case 1 => Right[R](c(0).asInstanceOf) - } - def common = reflectedClass - } - - //implicit def EitherEq[L: Eq, R: Eq]: Eq[Either[L, R]] = Eq.derived - //implicit def EitherPickler[L: Pickler, R: Pickler]: Pickler[Either[L, R]] = Pickler.derived - //implicit def EitherShow[L: Show, R: Show]: Show[Either[L, R]] = Show.derived } } From 9063951eeefb484fee827db2c2c9ffd209db3d61 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Dec 2018 17:08:53 +0100 Subject: [PATCH 17/56] Allow derivation for classes with no companions Two problems needed to be fixed - deciding that the derived type is the companion, not the synthetic companion - Avoiding forcing `Shape` types in checkRealizable since these can compute `children` too early. --- compiler/src/dotty/tools/dotc/core/CheckRealizable.scala | 9 +++++++-- compiler/src/dotty/tools/dotc/typer/Namer.scala | 3 ++- tests/run/typeclass-derivation3.scala | 7 +++---- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala b/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala index 6c6716bb1af3..fe4a431fa6c1 100644 --- a/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala +++ b/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala @@ -114,7 +114,11 @@ class CheckRealizable(implicit ctx: Context) { /** `Realizable` if `tp` has good bounds, a `HasProblem...` instance * pointing to a bad bounds member otherwise. "Has good bounds" means: * - * - all type members have good bounds (except for opaque helpers) + * - all non-synthetic type members have good bounds. + * Synthetic members are unchecked, for two reasons: + * - synthetic opaque aliases do have conflicting bounds, but this is OK + * - we should not force synthesized Shape types because that might + * query the `children` annotation too early. * - all refinements of the underlying type have good bounds (except for opaque companions) * - all base types are class types, and if their arguments are wildcards * they have good bounds. @@ -132,7 +136,8 @@ class CheckRealizable(implicit ctx: Context) { val memberProblems = for { mbr <- tp.nonClassTypeMembers - if !(mbr.info.loBound <:< mbr.info.hiBound) && !mbr.symbol.isOpaqueHelper + if !mbr.symbol.is(Synthetic) + if !(mbr.info.loBound <:< mbr.info.hiBound) } yield new HasProblemBounds(mbr.name, mbr.info) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 9b723d8c6ee7..16bc6609f91d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -991,7 +991,8 @@ class Namer { typer: Typer => if (impl.derived.nonEmpty) { val derivingClass = - if (original.removeAttachment(DerivingCompanion).isDefined) cls.companionClass.asClass + if (original.removeAttachment(DerivingCompanion).isDefined || + original.mods.is(Synthetic)) cls.companionClass.asClass else cls val deriver = new Deriver(derivingClass, impl.pos.startPos)(localCtx) deriver.enterDerived(impl.derived) diff --git a/tests/run/typeclass-derivation3.scala b/tests/run/typeclass-derivation3.scala index cfd25ed3a0a2..fe6804f1daab 100644 --- a/tests/run/typeclass-derivation3.scala +++ b/tests/run/typeclass-derivation3.scala @@ -3,25 +3,24 @@ import scala.annotation.tailrec object datatypes { import typeclasses._ + // An algebraic datatype enum Lst[+T] derives Eq, Pickler, Show { case Cons(hd: T, tl: Lst[T]) case Nil } - object Lst {} + // A simple product type case class Pair[T](x: T, y: T) derives Eq, Pickler, Show - object Pair // another ADT sealed trait Either[+L, +R] extends Product derives Eq, Pickler, Show case class Left[L](x: L) extends Either[L, Nothing] case class Right[R](x: R) extends Either[Nothing, R] - object Either { - } + //object Either {} } object typeclasses { From 0377cab538172d9c8b02e8faf928d2f3714a83d5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Dec 2018 19:04:02 +0100 Subject: [PATCH 18/56] Handle derived typeclasses with non-trivial prefixes --- .../src/dotty/tools/dotc/typer/Deriving.scala | 36 ++++++++++------ tests/neg/deriving-duplicate.scala | 21 ++++++++++ tests/run/deriving-interesting-prefixes.check | 4 ++ tests/run/deriving-interesting-prefixes.scala | 41 +++++++++++++++++++ 4 files changed, 89 insertions(+), 13 deletions(-) create mode 100644 tests/neg/deriving-duplicate.scala create mode 100644 tests/run/deriving-interesting-prefixes.check create mode 100644 tests/run/deriving-interesting-prefixes.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index fe4d4ce26328..bda00adc0bc4 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -16,12 +16,6 @@ import Inferencing._ import transform.TypeUtils._ import transform.SymUtils._ - -// TODOs: -// - handle case where there's no companion object -// - check that derived instances are stable -// - reference derived instances with correct prefix instead of just the symbol - /** A typer mixin that implements typeclass derivation functionality */ trait Deriving { this: Typer => @@ -131,6 +125,15 @@ trait Deriving { this: Typer => flags: FlagSet = EmptyFlags)(implicit ctx: Context): TermSymbol = newSymbol(name, info, pos, flags | Method).asTerm + /** A version of Type#underlyingClassRef that works also for higher-kinded types */ + private def underlyingClassRef(tp: Type): Type = tp match { + case tp: TypeRef if tp.symbol.isClass => tp + case tp: TypeRef if tp.symbol.isAbstractType => NoType + case tp: TermRef => NoType + case tp: TypeProxy => underlyingClassRef(tp.underlying) + case _ => NoType + } + /** Enter type class instance with given name and info in current scope, provided * an instance with the same name does not exist already. * @param reportErrors Report an error if an instance with the same name exists already @@ -138,7 +141,7 @@ trait Deriving { this: Typer => private def addDerivedInstance(clsName: Name, info: Type, pos: Position, reportErrors: Boolean) = { val instanceName = s"derived$$$clsName".toTermName if (ctx.denotNamed(instanceName).exists) { - if (reportErrors) ctx.error(i"duplicate typeclass derivation for $clsName") + if (reportErrors) ctx.error(i"duplicate typeclass derivation for $clsName", pos) } else add(newMethod(instanceName, info, pos, Implicit)) } @@ -162,8 +165,9 @@ trait Deriving { this: Typer => * that have the same name but different prefixes through selective aliasing. */ private def processDerivedInstance(derived: untpd.Tree): Unit = { - val uncheckedType = typedAheadType(derived, AnyTypeConstructorProto).tpe.dealias - val derivedType = checkClassType(uncheckedType, derived.pos, traitReq = false, stablePrefixReq = true) + val originalType = typedAheadType(derived, AnyTypeConstructorProto).tpe + val underlyingType = underlyingClassRef(originalType) + val derivedType = checkClassType(underlyingType, derived.pos, traitReq = false, stablePrefixReq = true) val nparams = derivedType.classSymbol.typeParams.length if (nparams == 1) { val typeClass = derivedType.classSymbol @@ -174,7 +178,7 @@ trait Deriving { this: Typer => val instanceInfo = if (cls.typeParams.isEmpty) ExprType(resultType) else PolyType.fromParams(cls.typeParams, ImplicitMethodType(evidenceParamInfos, resultType)) - addDerivedInstance(derivedType.typeSymbol.name, instanceInfo, derived.pos, reportErrors = true) + addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.pos, reportErrors = true) } else ctx.error( @@ -377,14 +381,20 @@ trait Deriving { this: Typer => def instantiated(info: Type): Type = info match { case info: PolyType => instantiated(info.instantiate(tparamRefs)) case info: MethodType => info.instantiate(params.map(_.termRef)) - case info => info + case info => info.widenExpr + } + def classAndCompanionRef(tp: Type): (ClassSymbol, TermRef) = tp match { + case tp @ TypeRef(prefix, _) if tp.symbol.isClass => + (tp.symbol.asClass, prefix.select(tp.symbol.companionModule).asInstanceOf[TermRef]) + case tp: TypeProxy => + classAndCompanionRef(tp.underlying) } val resultType = instantiated(sym.info) - val typeCls = resultType.classSymbol + val (typeCls, companionRef) = classAndCompanionRef(resultType) if (typeCls == defn.ShapedClass) shapedRHS(resultType) else { - val module = untpd.ref(typeCls.companionModule.termRef).withPos(sym.pos) + val module = untpd.ref(companionRef).withPos(sym.pos) val rhs = untpd.Select(module, nme.derived) typed(rhs, resultType) } diff --git a/tests/neg/deriving-duplicate.scala b/tests/neg/deriving-duplicate.scala new file mode 100644 index 000000000000..1574d3af6f4e --- /dev/null +++ b/tests/neg/deriving-duplicate.scala @@ -0,0 +1,21 @@ +object Test extends App { + + class A { + class TC1[T] { + } + object TC1 { + def derived[T]: TC1[T] = new TC1[T] + } + } + class A2 { + class TC1[T] { + } + object TC1 { + def derived[T]: TC1[T] = new TC1[T] + } + } + val a = new A + val a2 = new A2 + + case class D() derives a.TC1, a2.TC1 // error: duplicate typeclass derivation +} \ No newline at end of file diff --git a/tests/run/deriving-interesting-prefixes.check b/tests/run/deriving-interesting-prefixes.check new file mode 100644 index 000000000000..12cd65281462 --- /dev/null +++ b/tests/run/deriving-interesting-prefixes.check @@ -0,0 +1,4 @@ +a.TC1 +b.TC2 +a.TC1 +a2.TC1 diff --git a/tests/run/deriving-interesting-prefixes.scala b/tests/run/deriving-interesting-prefixes.scala new file mode 100644 index 000000000000..ce01f149eddc --- /dev/null +++ b/tests/run/deriving-interesting-prefixes.scala @@ -0,0 +1,41 @@ +object Test extends App { + + class A { + class TC1[T] { + def print() = println("a.TC1") + } + object TC1 { + def derived[T]: TC1[T] = new TC1[T] + } + } + class A2 { + class TC1[T] { + def print() = println("a2.TC1") + } + object TC1 { + def derived[T]: TC1[T] = new TC1[T] + } + } + class B { + class TC2[T] { + def print() = println("b.TC2") + } + object TC2 { + def derived[T]: TC2[T] = new TC2[T] + } + } + val a = new A + val a2 = new A2 + val b = new B + + case class C() derives a.TC1, b.TC2 + + implicitly[a.TC1[C]].print() + implicitly[b.TC2[C]].print() + + type TC1a[T] = a2.TC1[T] + case class D() derives a.TC1, TC1a + + implicitly[a.TC1[D]].print() + implicitly[a2.TC1[D]].print() +} \ No newline at end of file From 27f96dc808e0a48e97eec03be84bc737a7a95699 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 3 Dec 2018 19:52:19 +0100 Subject: [PATCH 19/56] Scaling test --- .../src/dotty/tools/dotc/config/Config.scala | 2 +- compiler/test/dotc/run-from-tasty.blacklist | 1 + .../test/dotc/run-test-pickling.blacklist | 1 + .../dotty/tools/dotc/CompilationTests.scala | 3 +- tests/pos-special/typeclass-scaling.scala | 394 ++++++++++++++++++ 5 files changed, 399 insertions(+), 2 deletions(-) create mode 100644 tests/pos-special/typeclass-scaling.scala diff --git a/compiler/src/dotty/tools/dotc/config/Config.scala b/compiler/src/dotty/tools/dotc/config/Config.scala index 4cb9369653e2..4366ac45170f 100644 --- a/compiler/src/dotty/tools/dotc/config/Config.scala +++ b/compiler/src/dotty/tools/dotc/config/Config.scala @@ -161,7 +161,7 @@ object Config { final val showCompletions = false /** If set, enables tracing */ - final val tracingEnabled = true + final val tracingEnabled = false /** Initial capacity of uniques HashMap. * Note: This MUST BE a power of two to work with util.HashSet diff --git a/compiler/test/dotc/run-from-tasty.blacklist b/compiler/test/dotc/run-from-tasty.blacklist index 70871f46819c..c0eb1cde5b56 100644 --- a/compiler/test/dotc/run-from-tasty.blacklist +++ b/compiler/test/dotc/run-from-tasty.blacklist @@ -9,4 +9,5 @@ implicitMatch.scala typeclass-derivation1.scala typeclass-derivation2.scala typeclass-derivation3.scala +deriving-interesting-prefixes.scala diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index 23b18bd7d78c..fcda63b4dd26 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -7,3 +7,4 @@ tuples1.scala tuples1a.scala typeclass-derivation2.scala typeclass-derivation3.scala +deriving-interesting-prefixes.scala diff --git a/compiler/test/dotty/tools/dotc/CompilationTests.scala b/compiler/test/dotty/tools/dotc/CompilationTests.scala index 982a036b9b39..59ad00ada53a 100644 --- a/compiler/test/dotty/tools/dotc/CompilationTests.scala +++ b/compiler/test/dotty/tools/dotc/CompilationTests.scala @@ -97,7 +97,8 @@ class CompilationTests extends ParallelTesting { // succeeds despite -Xfatal-warnings because of -nowarn "tests/neg-custom-args/fatal-warnings/xfatalWarnings.scala", defaultOptions.and("-nowarn", "-Xfatal-warnings") - ) + ) + + compileFile("tests/pos-special/typeclass-scaling.scala", defaultOptions.and("-Xmax-inlines", "40")) }.checkCompile() @Test def posTwice: Unit = { diff --git a/tests/pos-special/typeclass-scaling.scala b/tests/pos-special/typeclass-scaling.scala new file mode 100644 index 000000000000..51a01110d5aa --- /dev/null +++ b/tests/pos-special/typeclass-scaling.scala @@ -0,0 +1,394 @@ +import scala.collection.mutable +import scala.annotation.tailrec + +// The following command: +// +// sc typeclass-scaling.scala -Xmax-inlines 100 -Xprint:front -color:never -Yshow-no-inline -pagewidth 1000 >& x +// +// produces an output file with `wc` measuures (lines/words/chars): +// +// 41030 100905 3862435 +// +// The command +// +// time sc typeclass-scaling.scala -Xmax-inlines 100 +// +// gives (best of three): +// +// real 0m14.322s +// user 0m55.402s +// sys 0m1.110s +object datatypes { + import typeclasses._ + + enum E1[T] derives Eq, Pickler { + case C1(x1: T) + } + + enum E2[T] derives Eq, Pickler { + case C1(x1: T) + case C2(x1: T, x2: T) + } + + enum E3[T] derives Eq, Pickler { + case C1(x1: T) + case C2(x1: T, x2: T) + case C3(x1: T, x2: T, x3: T) + } + + enum E4[T] derives Eq, Pickler { + case C1(x1: T) + case C2(x1: T, x2: T) + case C3(x1: T, x2: T, x3: T) + case C4(x1: T, x2: T, x3: T, x4: T) + } + + enum E5[T] derives Eq, Pickler { + case C1(x1: T) + case C2(x1: T, x2: T) + case C3(x1: T, x2: T, x3: T) + case C4(x1: T, x2: T, x3: T, x4: T) + case C5(x1: T, x2: T, x3: T, x4: T, x5: T) + } + + enum E6[T] derives Eq, Pickler { + case C1(x1: T) + case C2(x1: T, x2: T) + case C3(x1: T, x2: T, x3: T) + case C4(x1: T, x2: T, x3: T, x4: T) + case C5(x1: T, x2: T, x3: T, x4: T, x5: T) + case C6(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T) + } + + enum E7[T] derives Eq, Pickler { + case C1(x1: T) + case C2(x1: T, x2: T) + case C3(x1: T, x2: T, x3: T) + case C4(x1: T, x2: T, x3: T, x4: T) + case C5(x1: T, x2: T, x3: T, x4: T, x5: T) + case C6(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T) + case C7(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T) + } + + enum E8[T] derives Eq, Pickler { + case C1(x1: T) + case C2(x1: T, x2: T) + case C3(x1: T, x2: T, x3: T) + case C4(x1: T, x2: T, x3: T, x4: T) + case C5(x1: T, x2: T, x3: T, x4: T, x5: T) + case C6(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T) + case C7(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T) + case C8(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T) + } + + enum E9[T] derives Eq, Pickler { + case C1(x1: T) + case C2(x1: T, x2: T) + case C3(x1: T, x2: T, x3: T) + case C4(x1: T, x2: T, x3: T, x4: T) + case C5(x1: T, x2: T, x3: T, x4: T, x5: T) + case C6(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T) + case C7(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T) + case C8(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T) + case C9(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T) + } + + enum E10[T] derives Eq, Pickler { + case C1(x1: T) + case C2(x1: T, x2: T) + case C3(x1: T, x2: T, x3: T) + case C4(x1: T, x2: T, x3: T, x4: T) + case C5(x1: T, x2: T, x3: T, x4: T, x5: T) + case C6(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T) + case C7(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T) + case C8(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T) + case C9(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T) + case C10(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T) + } + + enum E11[T] derives Eq, Pickler { + case C1(x1: T) + case C2(x1: T, x2: T) + case C3(x1: T, x2: T, x3: T) + case C4(x1: T, x2: T, x3: T, x4: T) + case C5(x1: T, x2: T, x3: T, x4: T, x5: T) + case C6(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T) + case C7(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T) + case C8(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T) + case C9(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T) + case C10(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T) + case C11(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T) + } + + enum E12[T] derives Eq, Pickler { + case C1(x1: T) + case C2(x1: T, x2: T) + case C3(x1: T, x2: T, x3: T) + case C4(x1: T, x2: T, x3: T, x4: T) + case C5(x1: T, x2: T, x3: T, x4: T, x5: T) + case C6(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T) + case C7(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T) + case C8(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T) + case C9(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T) + case C10(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T) + case C11(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T) + case C12(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T) + } + + enum E13[T] derives Eq, Pickler { + case C1(x1: T) + case C2(x1: T, x2: T) + case C3(x1: T, x2: T, x3: T) + case C4(x1: T, x2: T, x3: T, x4: T) + case C5(x1: T, x2: T, x3: T, x4: T, x5: T) + case C6(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T) + case C7(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T) + case C8(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T) + case C9(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T) + case C10(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T) + case C11(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T) + case C12(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T) + case C13(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T, x13: T) + } + + enum E14[T] derives Eq, Pickler { + case C1(x1: T) + case C2(x1: T, x2: T) + case C3(x1: T, x2: T, x3: T) + case C4(x1: T, x2: T, x3: T, x4: T) + case C5(x1: T, x2: T, x3: T, x4: T, x5: T) + case C6(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T) + case C7(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T) + case C8(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T) + case C9(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T) + case C10(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T) + case C11(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T) + case C12(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T) + case C13(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T, x13: T) + case C14(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T, x13: T, x14: T) + } + + enum E15[T] derives Eq, Pickler { + case C1(x1: T) + case C2(x1: T, x2: T) + case C3(x1: T, x2: T, x3: T) + case C4(x1: T, x2: T, x3: T, x4: T) + case C5(x1: T, x2: T, x3: T, x4: T, x5: T) + case C6(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T) + case C7(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T) + case C8(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T) + case C9(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T) + case C10(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T) + case C11(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T) + case C12(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T) + case C13(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T, x13: T) + case C14(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T, x13: T, x14: T) + case C15(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T, x13: T, x14: T, x15: T) + } + + enum E16[T] derives Eq, Pickler { + case C1(x1: T) + case C2(x1: T, x2: T) + case C3(x1: T, x2: T, x3: T) + case C4(x1: T, x2: T, x3: T, x4: T) + case C5(x1: T, x2: T, x3: T, x4: T, x5: T) + case C6(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T) + case C7(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T) + case C8(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T) + case C9(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T) + case C10(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T) + case C11(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T) + case C12(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T) + case C13(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T, x13: T) + case C14(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T, x13: T, x14: T) + case C15(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T, x13: T, x14: T, x15: T) + case C16(x1: T, x2: T, x3: T, x4: T, x5: T, x6: T, x7: T, x8: T, x9: T, x10: T, x11: T, x12: T, x13: T, x14: T, x15: T, x16: T) + } +} + +object typeclasses { + // A typeclass + trait Eq[T] { + def eql(x: T, y: T): Boolean + } + + object Eq { + import scala.typelevel.erasedValue + import typelevel._ + + inline def tryEql[T](x: T, y: T) = implicit match { + case eq: Eq[T] => eq.eql(x, y) + } + + inline def eqlElems[Elems <: Tuple](xs: Mirror, ys: Mirror, n: Int): Boolean = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + tryEql[elem](xs(n).asInstanceOf, ys(n).asInstanceOf) && + eqlElems[elems1](xs, ys, n + 1) + case _: Unit => + true + } + + inline def eqlCase[T, Elems <: Tuple](r: Reflected[T], x: T, y: T) = + eqlElems[Elems](r.reflect(x), r.reflect(y), 0) + + inline def eqlCases[T, Alts <: Tuple](r: Reflected[T], x: T, y: T): Boolean = + inline erasedValue[Alts] match { + case _: (Shape.Case[alt, elems] *: alts1) => + x match { + case x: `alt` => + y match { + case y: `alt` => eqlCase[T, elems](r, x, y) + case _ => false + } + case _ => eqlCases[T, alts1](r, x, y) + } + case _: Unit => + false + } + + inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Eq[T] = new { + def eql(x: T, y: T): Boolean = inline erasedValue[S] match { + case _: Shape.Cases[alts] => + eqlCases[T, alts](ev, x, y) + case _: Shape.Case[_, elems] => + eqlCase[T, elems](ev, x, y) + } + } + + implicit object IntEq extends Eq[Int] { + def eql(x: Int, y: Int) = x == y + } + } + + // Another typeclass + trait Pickler[T] { + def pickle(buf: mutable.ListBuffer[Int], x: T): Unit + def unpickle(buf: mutable.ListBuffer[Int]): T + } + + object Pickler { + import scala.typelevel.{erasedValue, constValue} + import typelevel._ + + def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) + + inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = implicit match { + case pkl: Pickler[T] => pkl.pickle(buf, x) + } + + inline def pickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Mirror, n: Int): Unit = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + tryPickle[elem](buf, elems(n).asInstanceOf[elem]) + pickleElems[elems1](buf, elems, n + 1) + case _: Unit => + } + + inline def pickleCase[T, Elems <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], x: T): Unit = + pickleElems[Elems](buf, r.reflect(x), 0) + + inline def pickleCases[T, Alts <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], x: T, n: Int): Unit = + inline erasedValue[Alts] match { + case _: (Shape.Case[alt, elems] *: alts1) => + x match { + case x: `alt` => + buf += n + pickleCase[T, elems](r, buf, x) + case _ => + pickleCases[T, alts1](r, buf, x, n + 1) + } + case _: Unit => + } + + inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = implicit match { + case pkl: Pickler[T] => pkl.unpickle(buf) + } + + inline def unpickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Array[AnyRef], n: Int): Unit = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + elems(n) = tryUnpickle[elem](buf).asInstanceOf[AnyRef] + unpickleElems[elems1](buf, elems, n + 1) + case _: Unit => + } + + inline def unpickleCase[T, Elems <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { + inline val size = constValue[Tuple.Size[Elems]] + inline if (size == 0) + r.reify(r.common.mirror(ordinal)) + else { + val elems = new Array[Object](size) + unpickleElems[Elems](buf, elems, 0) + r.reify(r.common.mirror(ordinal, elems)) + } + } + + inline def unpickleCases[T, Alts <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = + inline erasedValue[Alts] match { + case _: (Shape.Case[_, elems] *: alts1) => + if (n == ordinal) unpickleCase[T, elems](r, buf, ordinal) + else unpickleCases[T, alts1](r, buf, ordinal, n + 1) + case _ => + throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ordinal") + } + + inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Pickler[T] = new { + def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = inline erasedValue[S] match { + case _: Shape.Cases[alts] => + pickleCases[T, alts](ev, buf, x, 0) + case _: Shape.Case[_, elems] => + pickleCase[T, elems](ev, buf, x) + } + def unpickle(buf: mutable.ListBuffer[Int]): T = inline erasedValue[S] match { + case _: Shape.Cases[alts] => + unpickleCases[T, alts](ev, buf, nextInt(buf), 0) + case _: Shape.Case[_, elems] => + unpickleCase[T, elems](ev, buf, 0) + } + } + + implicit object IntPickler extends Pickler[Int] { + def pickle(buf: mutable.ListBuffer[Int], x: Int): Unit = buf += x + def unpickle(buf: mutable.ListBuffer[Int]): Int = nextInt(buf) + } + } +} +import datatypes._ +import typeclasses._ + +// Tests +object Test extends App { + implicitly[Eq[E1[Int]]] + implicitly[Eq[E2[Int]]] + implicitly[Eq[E3[Int]]] + implicitly[Eq[E4[Int]]] + implicitly[Eq[E5[Int]]] + implicitly[Eq[E6[Int]]] + implicitly[Eq[E7[Int]]] + implicitly[Eq[E8[Int]]] + implicitly[Eq[E9[Int]]] + implicitly[Eq[E10[Int]]] + implicitly[Eq[E11[Int]]] + implicitly[Eq[E12[Int]]] + implicitly[Eq[E13[Int]]] + implicitly[Eq[E14[Int]]] + implicitly[Eq[E15[Int]]] + implicitly[Eq[E16[Int]]] + implicitly[Pickler[E1[Int]]] + implicitly[Pickler[E2[Int]]] + implicitly[Pickler[E3[Int]]] + implicitly[Pickler[E4[Int]]] + implicitly[Pickler[E5[Int]]] + implicitly[Pickler[E6[Int]]] + implicitly[Pickler[E7[Int]]] + implicitly[Pickler[E8[Int]]] + implicitly[Pickler[E9[Int]]] + implicitly[Pickler[E10[Int]]] + implicitly[Pickler[E11[Int]]] + implicitly[Pickler[E12[Int]]] + implicitly[Pickler[E13[Int]]] + implicitly[Pickler[E14[Int]]] + implicitly[Pickler[E15[Int]]] + implicitly[Pickler[E16[Int]]] +} \ No newline at end of file From 17b0feea160c929bd0722822a432083fa0ecb846 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Dec 2018 13:55:36 +0100 Subject: [PATCH 20/56] Move typelevel classes to src-bootstrapped Also fixes review comments for these. --- .../scala/typelevel/Mirror.scala | 6 +++--- .../scala/typelevel/Reflected.scala | 0 .../scala/typelevel/ReflectedClass.scala | 0 .../scala/typelevel/Shape.scala | 2 +- .../scala/typelevel/Shaped.scala | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename library/{src-scala3 => src-bootstrapped}/scala/typelevel/Mirror.scala (72%) rename library/{src-scala3 => src-bootstrapped}/scala/typelevel/Reflected.scala (100%) rename library/{src-scala3 => src-bootstrapped}/scala/typelevel/ReflectedClass.scala (100%) rename library/{src-scala3 => src-bootstrapped}/scala/typelevel/Shape.scala (81%) rename library/{src-scala3 => src-bootstrapped}/scala/typelevel/Shaped.scala (100%) diff --git a/library/src-scala3/scala/typelevel/Mirror.scala b/library/src-bootstrapped/scala/typelevel/Mirror.scala similarity index 72% rename from library/src-scala3/scala/typelevel/Mirror.scala rename to library/src-bootstrapped/scala/typelevel/Mirror.scala index 7401d8f1f89d..d3565dbaf845 100644 --- a/library/src-scala3/scala/typelevel/Mirror.scala +++ b/library/src-bootstrapped/scala/typelevel/Mirror.scala @@ -1,9 +1,9 @@ package scala.typelevel /** A generic representation of a case in an ADT - * @param deriving The companion object of the ADT - * @param ordinal The ordinal value of the case in the list of the ADT's cases - * @param elems The elements of the case + * @param reflected The common class-speficic part of this mirror + * @param ordinal The ordinal value of the case in the list of the ADT's cases + * @param elems The elements of the case */ class Mirror(val reflected: ReflectedClass, val ordinal: Int, val elems: Product) { diff --git a/library/src-scala3/scala/typelevel/Reflected.scala b/library/src-bootstrapped/scala/typelevel/Reflected.scala similarity index 100% rename from library/src-scala3/scala/typelevel/Reflected.scala rename to library/src-bootstrapped/scala/typelevel/Reflected.scala diff --git a/library/src-scala3/scala/typelevel/ReflectedClass.scala b/library/src-bootstrapped/scala/typelevel/ReflectedClass.scala similarity index 100% rename from library/src-scala3/scala/typelevel/ReflectedClass.scala rename to library/src-bootstrapped/scala/typelevel/ReflectedClass.scala diff --git a/library/src-scala3/scala/typelevel/Shape.scala b/library/src-bootstrapped/scala/typelevel/Shape.scala similarity index 81% rename from library/src-scala3/scala/typelevel/Shape.scala rename to library/src-bootstrapped/scala/typelevel/Shape.scala index cd1f4596479a..7ba8cd5fcb38 100644 --- a/library/src-scala3/scala/typelevel/Shape.scala +++ b/library/src-bootstrapped/scala/typelevel/Shape.scala @@ -1,7 +1,7 @@ package scala.typelevel /** The shape of an ADT. - * This is eithe a product (`Case`) or a sum (`Cases`) of products. + * This is either a product (`Case`) or a sum (`Cases`) of products. */ sealed abstract class Shape diff --git a/library/src-scala3/scala/typelevel/Shaped.scala b/library/src-bootstrapped/scala/typelevel/Shaped.scala similarity index 100% rename from library/src-scala3/scala/typelevel/Shaped.scala rename to library/src-bootstrapped/scala/typelevel/Shaped.scala From 34f9c84b87df03d6a8c186ac574718899f7b9376 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 4 Dec 2018 16:43:43 +0100 Subject: [PATCH 21/56] Rename typelevel package to compiletime --- .../src/dotty/tools/dotc/core/Definitions.scala | 14 +++++++------- library/src-bootstrapped/scala/Tuple.scala | 2 +- .../scala/{typelevel => compiletime}/Mirror.scala | 2 +- .../{typelevel => compiletime}/Reflected.scala | 2 +- .../ReflectedClass.scala | 2 +- .../scala/{typelevel => compiletime}/Shape.scala | 6 +++--- .../scala/{typelevel => compiletime}/Shaped.scala | 2 +- .../scala/{typelevel => compiletime}/package.scala | 2 +- tests/neg/typeclass-derivation2.scala | 2 +- tests/pos-special/typeclass-scaling.scala | 8 ++++---- tests/pos/matchtype.scala | 2 +- tests/run/typeclass-derivation1.scala | 2 +- tests/run/typeclass-derivation2.scala | 6 +++--- tests/run/typeclass-derivation3.scala | 12 ++++++------ tests/run/typelevel-defaultValue.scala | 4 ++-- tests/run/typelevel-peano.scala | 2 +- 16 files changed, 35 insertions(+), 35 deletions(-) rename library/src-bootstrapped/scala/{typelevel => compiletime}/Mirror.scala (96%) rename library/src-bootstrapped/scala/{typelevel => compiletime}/Reflected.scala (93%) rename library/src-bootstrapped/scala/{typelevel => compiletime}/ReflectedClass.scala (99%) rename library/src-bootstrapped/scala/{typelevel => compiletime}/Shape.scala (73%) rename library/src-bootstrapped/scala/{typelevel => compiletime}/Shaped.scala (89%) rename library/src-bootstrapped/scala/{typelevel => compiletime}/package.scala (88%) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 3839a276599c..b818772f8fd9 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -214,7 +214,7 @@ class Definitions { lazy val Sys_errorR: TermRef = SysPackage.moduleClass.requiredMethodRef(nme.error) def Sys_error(implicit ctx: Context): Symbol = Sys_errorR.symbol - lazy val TypelevelPackageObjectRef: TermRef = ctx.requiredModuleRef("scala.typelevel.package") + lazy val TypelevelPackageObjectRef: TermRef = ctx.requiredModuleRef("scala.compiletime.package") lazy val TypelevelPackageObject: Symbol = TypelevelPackageObjectRef.symbol.moduleClass lazy val Typelevel_errorR: TermRef = TypelevelPackageObjectRef.symbol.requiredMethodRef(nme.error) def Typelevel_error(implicit ctx: Context): Symbol = Typelevel_errorR.symbol @@ -649,16 +649,16 @@ class Definitions { lazy val Product_productPrefixR: TermRef = ProductClass.requiredMethodRef(nme.productPrefix) def Product_productPrefix(implicit ctx: Context): Symbol = Product_productPrefixR.symbol - lazy val ShapedType: TypeRef = ctx.requiredClassRef("scala.typelevel.Shaped") + lazy val ShapedType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shaped") def ShapedClass(implicit ctx: Context): ClassSymbol = ShapedType.symbol.asClass - lazy val ShapeType: TypeRef = ctx.requiredClassRef("scala.typelevel.Shape") + lazy val ShapeType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape") def ShapeClass(implicit ctx: Context): ClassSymbol = ShapeType.symbol.asClass - lazy val ShapeCaseType: TypeRef = ctx.requiredClassRef("scala.typelevel.Shape.Case") + lazy val ShapeCaseType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape.Case") def ShapeCaseClass(implicit ctx: Context): ClassSymbol = ShapeCaseType.symbol.asClass - lazy val ShapeCasesType: TypeRef = ctx.requiredClassRef("scala.typelevel.Shape.Cases") + lazy val ShapeCasesType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape.Cases") def ShapeCasesClass(implicit ctx: Context): ClassSymbol = ShapeCasesType.symbol.asClass - lazy val MirrorType: TypeRef = ctx.requiredClassRef("scala.typelevel.Mirror") - lazy val ReflectedClassType: TypeRef = ctx.requiredClassRef("scala.typelevel.ReflectedClass") + lazy val MirrorType: TypeRef = ctx.requiredClassRef("scala.compiletime.Mirror") + lazy val ReflectedClassType: TypeRef = ctx.requiredClassRef("scala.compiletime.ReflectedClass") lazy val LanguageModuleRef: TermSymbol = ctx.requiredModule("scala.language") def LanguageModuleClass(implicit ctx: Context): ClassSymbol = LanguageModuleRef.moduleClass.asClass diff --git a/library/src-bootstrapped/scala/Tuple.scala b/library/src-bootstrapped/scala/Tuple.scala index ed5e3d43ff25..e98ad4df5444 100644 --- a/library/src-bootstrapped/scala/Tuple.scala +++ b/library/src-bootstrapped/scala/Tuple.scala @@ -1,6 +1,6 @@ package scala import annotation.showAsInfix -import typelevel._ +import compiletime._ import scala.StagedTuple sealed trait Tuple extends Any { diff --git a/library/src-bootstrapped/scala/typelevel/Mirror.scala b/library/src-bootstrapped/scala/compiletime/Mirror.scala similarity index 96% rename from library/src-bootstrapped/scala/typelevel/Mirror.scala rename to library/src-bootstrapped/scala/compiletime/Mirror.scala index d3565dbaf845..d62dd0deedfe 100644 --- a/library/src-bootstrapped/scala/typelevel/Mirror.scala +++ b/library/src-bootstrapped/scala/compiletime/Mirror.scala @@ -1,4 +1,4 @@ -package scala.typelevel +package scala.compiletime /** A generic representation of a case in an ADT * @param reflected The common class-speficic part of this mirror diff --git a/library/src-bootstrapped/scala/typelevel/Reflected.scala b/library/src-bootstrapped/scala/compiletime/Reflected.scala similarity index 93% rename from library/src-bootstrapped/scala/typelevel/Reflected.scala rename to library/src-bootstrapped/scala/compiletime/Reflected.scala index 8febfe69ad80..c938ab3287e3 100644 --- a/library/src-bootstrapped/scala/typelevel/Reflected.scala +++ b/library/src-bootstrapped/scala/compiletime/Reflected.scala @@ -1,4 +1,4 @@ -package scala.typelevel +package scala.compiletime /** A class for mapping between an ADT value and * the case mirror that represents the value. diff --git a/library/src-bootstrapped/scala/typelevel/ReflectedClass.scala b/library/src-bootstrapped/scala/compiletime/ReflectedClass.scala similarity index 99% rename from library/src-bootstrapped/scala/typelevel/ReflectedClass.scala rename to library/src-bootstrapped/scala/compiletime/ReflectedClass.scala index a76229cb423c..c72c8839e71a 100644 --- a/library/src-bootstrapped/scala/typelevel/ReflectedClass.scala +++ b/library/src-bootstrapped/scala/compiletime/ReflectedClass.scala @@ -1,4 +1,4 @@ -package scala.typelevel +package scala.compiletime import annotation.tailrec import collection.mutable.ArrayBuffer diff --git a/library/src-bootstrapped/scala/typelevel/Shape.scala b/library/src-bootstrapped/scala/compiletime/Shape.scala similarity index 73% rename from library/src-bootstrapped/scala/typelevel/Shape.scala rename to library/src-bootstrapped/scala/compiletime/Shape.scala index 7ba8cd5fcb38..583080ce8d7e 100644 --- a/library/src-bootstrapped/scala/typelevel/Shape.scala +++ b/library/src-bootstrapped/scala/compiletime/Shape.scala @@ -1,8 +1,8 @@ -package scala.typelevel +package scala.compiletime /** The shape of an ADT. - * This is either a product (`Case`) or a sum (`Cases`) of products. - */ + * This is either a product (`Case`) or a sum (`Cases`) of products. + */ sealed abstract class Shape object Shape { diff --git a/library/src-bootstrapped/scala/typelevel/Shaped.scala b/library/src-bootstrapped/scala/compiletime/Shaped.scala similarity index 89% rename from library/src-bootstrapped/scala/typelevel/Shaped.scala rename to library/src-bootstrapped/scala/compiletime/Shaped.scala index 1e0b6c1913ab..7a64dc64337f 100644 --- a/library/src-bootstrapped/scala/typelevel/Shaped.scala +++ b/library/src-bootstrapped/scala/compiletime/Shaped.scala @@ -1,4 +1,4 @@ -package scala.typelevel +package scala.compiletime /** Every generic derivation starts with a typeclass instance of this type. * It informs that type `T` has shape `S` and also implements runtime reflection on `T`. diff --git a/library/src-bootstrapped/scala/typelevel/package.scala b/library/src-bootstrapped/scala/compiletime/package.scala similarity index 88% rename from library/src-bootstrapped/scala/typelevel/package.scala rename to library/src-bootstrapped/scala/compiletime/package.scala index c48cc40ca0fe..4aa2921066d6 100644 --- a/library/src-bootstrapped/scala/typelevel/package.scala +++ b/library/src-bootstrapped/scala/compiletime/package.scala @@ -1,6 +1,6 @@ package scala -package object typelevel { +package object compiletime { erased def erasedValue[T]: T = ??? diff --git a/tests/neg/typeclass-derivation2.scala b/tests/neg/typeclass-derivation2.scala index dc4bef9493ef..727e2a6b30e4 100644 --- a/tests/neg/typeclass-derivation2.scala +++ b/tests/neg/typeclass-derivation2.scala @@ -203,7 +203,7 @@ trait Show[T] { def show(x: T): String } object Show { - import scala.typelevel.erasedValue + import scala.compiletime.erasedValue import TypeLevel._ inline def tryShow[T](x: T): String = implicit match { diff --git a/tests/pos-special/typeclass-scaling.scala b/tests/pos-special/typeclass-scaling.scala index 51a01110d5aa..c46991073788 100644 --- a/tests/pos-special/typeclass-scaling.scala +++ b/tests/pos-special/typeclass-scaling.scala @@ -213,8 +213,8 @@ object typeclasses { } object Eq { - import scala.typelevel.erasedValue - import typelevel._ + import scala.compiletime.erasedValue + import compiletime._ inline def tryEql[T](x: T, y: T) = implicit match { case eq: Eq[T] => eq.eql(x, y) @@ -268,8 +268,8 @@ object typeclasses { } object Pickler { - import scala.typelevel.{erasedValue, constValue} - import typelevel._ + import scala.compiletime.{erasedValue, constValue} + import compiletime._ def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) diff --git a/tests/pos/matchtype.scala b/tests/pos/matchtype.scala index 58f452c2ad11..858b2cb267d4 100644 --- a/tests/pos/matchtype.scala +++ b/tests/pos/matchtype.scala @@ -1,4 +1,4 @@ -import typelevel._ +import compiletime._ object Test { type T[X] = X match { case String => Int diff --git a/tests/run/typeclass-derivation1.scala b/tests/run/typeclass-derivation1.scala index 47e457e8ed10..06c6a4fad698 100644 --- a/tests/run/typeclass-derivation1.scala +++ b/tests/run/typeclass-derivation1.scala @@ -1,5 +1,5 @@ object Deriving { - import scala.typelevel._ + import scala.compiletime._ sealed trait Shape diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index 349d45623bb7..47038ee3b498 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -215,7 +215,7 @@ trait Eq[T] { } object Eq { - import scala.typelevel.erasedValue + import scala.compiletime.erasedValue import TypeLevel._ inline def tryEql[T](x: T, y: T) = implicit match { @@ -270,7 +270,7 @@ trait Pickler[T] { } object Pickler { - import scala.typelevel.{erasedValue, constValue} + import scala.compiletime.{erasedValue, constValue} import TypeLevel._ def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) @@ -361,7 +361,7 @@ trait Show[T] { def show(x: T): String } object Show { - import scala.typelevel.erasedValue + import scala.compiletime.erasedValue import TypeLevel._ inline def tryShow[T](x: T): String = implicit match { diff --git a/tests/run/typeclass-derivation3.scala b/tests/run/typeclass-derivation3.scala index fe6804f1daab..1663791dd9c7 100644 --- a/tests/run/typeclass-derivation3.scala +++ b/tests/run/typeclass-derivation3.scala @@ -30,8 +30,8 @@ object typeclasses { } object Eq { - import scala.typelevel.erasedValue - import typelevel._ + import scala.compiletime.erasedValue + import compiletime._ inline def tryEql[T](x: T, y: T) = implicit match { case eq: Eq[T] => eq.eql(x, y) @@ -85,8 +85,8 @@ object typeclasses { } object Pickler { - import scala.typelevel.{erasedValue, constValue} - import typelevel._ + import scala.compiletime.{erasedValue, constValue} + import compiletime._ def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) @@ -176,8 +176,8 @@ object typeclasses { def show(x: T): String } object Show { - import scala.typelevel.erasedValue - import typelevel._ + import scala.compiletime.erasedValue + import compiletime._ inline def tryShow[T](x: T): String = implicit match { case s: Show[T] => s.show(x) diff --git a/tests/run/typelevel-defaultValue.scala b/tests/run/typelevel-defaultValue.scala index 314803beaef1..7c3b0af46d25 100644 --- a/tests/run/typelevel-defaultValue.scala +++ b/tests/run/typelevel-defaultValue.scala @@ -1,11 +1,11 @@ -object typelevel { +object compiletime { erased def erasedValue[T]: T = ??? } object Test extends App { - inline def defaultValue[T] <: Option[Any] = inline typelevel.erasedValue[T] match { + inline def defaultValue[T] <: Option[Any] = inline compiletime.erasedValue[T] match { case _: Byte => Some(0: Byte) case c: Char => Some(0: Char) case d @ (_: Short) => Some(0: Short) diff --git a/tests/run/typelevel-peano.scala b/tests/run/typelevel-peano.scala index c180a6d69f31..802f614fcfb7 100644 --- a/tests/run/typelevel-peano.scala +++ b/tests/run/typelevel-peano.scala @@ -1,5 +1,5 @@ -import typelevel._ +import compiletime._ object Test extends App { From e36c5e253a686606c853de3fc8d1f000b7be2859 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Dec 2018 15:27:23 +0100 Subject: [PATCH 22/56] Packaging reorgs - Finish typelevel -> compiletime by renaming internal symbols from TypeLevelX to CompiletimeX - Move some files that are in fact used at runtime from scala.compiletime to scala.reflect - Implement a new more efficient derivation scheme that is based on ordinals instead of repeated type tests. --- .../dotty/tools/dotc/core/Definitions.scala | 24 +- .../dotty/tools/dotc/core/TypeComparer.scala | 4 +- .../src/dotty/tools/dotc/core/Types.scala | 2 +- .../src/dotty/tools/dotc/typer/Deriving.scala | 4 +- .../src/dotty/tools/dotc/typer/Inliner.scala | 8 +- compiler/test/dotc/run-from-tasty.blacklist | 1 + .../scala/compiletime/Shaped.scala | 2 +- .../scala/reflect}/Mirror.scala | 2 +- .../scala/reflect}/Reflected.scala | 2 +- .../scala/reflect}/ReflectedClass.scala | 28 +- tests/pos-special/typeclass-scaling.scala | 2 + tests/run/typeclass-derivation2.scala | 43 +- tests/run/typeclass-derivation2a.check | 10 + tests/run/typeclass-derivation2a.scala | 476 ++++++++++++++++++ tests/run/typeclass-derivation3.scala | 95 ++-- 15 files changed, 594 insertions(+), 109 deletions(-) rename library/{src-bootstrapped/scala/compiletime => src/scala/reflect}/Mirror.scala (96%) rename library/{src-bootstrapped/scala/compiletime => src/scala/reflect}/Reflected.scala (93%) rename library/{src-bootstrapped/scala/compiletime => src/scala/reflect}/ReflectedClass.scala (79%) create mode 100644 tests/run/typeclass-derivation2a.check create mode 100644 tests/run/typeclass-derivation2a.scala diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index b818772f8fd9..02418a33cf6a 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -214,14 +214,14 @@ class Definitions { lazy val Sys_errorR: TermRef = SysPackage.moduleClass.requiredMethodRef(nme.error) def Sys_error(implicit ctx: Context): Symbol = Sys_errorR.symbol - lazy val TypelevelPackageObjectRef: TermRef = ctx.requiredModuleRef("scala.compiletime.package") - lazy val TypelevelPackageObject: Symbol = TypelevelPackageObjectRef.symbol.moduleClass - lazy val Typelevel_errorR: TermRef = TypelevelPackageObjectRef.symbol.requiredMethodRef(nme.error) - def Typelevel_error(implicit ctx: Context): Symbol = Typelevel_errorR.symbol - lazy val Typelevel_constValueR: TermRef = TypelevelPackageObjectRef.symbol.requiredMethodRef("constValue") - def Typelevel_constValue(implicit ctx: Context): Symbol = Typelevel_constValueR.symbol - lazy val Typelevel_constValueOptR: TermRef = TypelevelPackageObjectRef.symbol.requiredMethodRef("constValueOpt") - def Typelevel_constValueOpt(implicit ctx: Context): Symbol = Typelevel_constValueOptR.symbol + lazy val CompiletimePackageObjectRef: TermRef = ctx.requiredModuleRef("scala.compiletime.package") + lazy val CompiletimePackageObject: Symbol = CompiletimePackageObjectRef.symbol.moduleClass + lazy val Compiletime_errorR: TermRef = CompiletimePackageObjectRef.symbol.requiredMethodRef(nme.error) + def Compiletime_error(implicit ctx: Context): Symbol = Compiletime_errorR.symbol + lazy val Compiletime_constValueR: TermRef = CompiletimePackageObjectRef.symbol.requiredMethodRef("constValue") + def Compiletime_constValue(implicit ctx: Context): Symbol = Compiletime_constValueR.symbol + lazy val Compiletime_constValueOptR: TermRef = CompiletimePackageObjectRef.symbol.requiredMethodRef("constValueOpt") + def Compiletime_constValueOpt(implicit ctx: Context): Symbol = Compiletime_constValueOptR.symbol /** The `scalaShadowing` package is used to safely modify classes and * objects in scala so that they can be used from dotty. They will @@ -657,8 +657,8 @@ class Definitions { def ShapeCaseClass(implicit ctx: Context): ClassSymbol = ShapeCaseType.symbol.asClass lazy val ShapeCasesType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape.Cases") def ShapeCasesClass(implicit ctx: Context): ClassSymbol = ShapeCasesType.symbol.asClass - lazy val MirrorType: TypeRef = ctx.requiredClassRef("scala.compiletime.Mirror") - lazy val ReflectedClassType: TypeRef = ctx.requiredClassRef("scala.compiletime.ReflectedClass") + lazy val MirrorType: TypeRef = ctx.requiredClassRef("scala.reflect.Mirror") + lazy val ReflectedClassType: TypeRef = ctx.requiredClassRef("scala.reflect.ReflectedClass") lazy val LanguageModuleRef: TermSymbol = ctx.requiredModule("scala.language") def LanguageModuleClass(implicit ctx: Context): ClassSymbol = LanguageModuleRef.moduleClass.asClass @@ -917,8 +917,8 @@ class Definitions { } } - final def isTypelevel_S(sym: Symbol)(implicit ctx: Context): Boolean = - sym.name == tpnme.S && sym.owner == TypelevelPackageObject + final def isCompiletime_S(sym: Symbol)(implicit ctx: Context): Boolean = + sym.name == tpnme.S && sym.owner == CompiletimePackageObject // ----- Symbol sets --------------------------------------------------- diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index f50d1a9a9896..240d2988e473 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -873,7 +873,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] { compareLower(bounds(param2), tyconIsTypeRef = false) case tycon2: TypeRef => isMatchingApply(tp1) || - defn.isTypelevel_S(tycon2.symbol) && compareS(tp2, tp1, fromBelow = true) || { + defn.isCompiletime_S(tycon2.symbol) && compareS(tp2, tp1, fromBelow = true) || { tycon2.info match { case info2: TypeBounds => compareLower(info2, tyconIsTypeRef = true) @@ -912,7 +912,7 @@ class TypeComparer(initctx: Context) extends ConstraintHandling[AbsentContext] { case tycon1: TypeRef => val sym = tycon1.symbol !sym.isClass && ( - defn.isTypelevel_S(sym) && compareS(tp1, tp2, fromBelow = false) || + defn.isCompiletime_S(sym) && compareS(tp1, tp2, fromBelow = false) || recur(tp1.superType, tp2)) case tycon1: TypeProxy => recur(tp1.superType, tp2) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index c52b244d1671..3edb9db445a9 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -3407,7 +3407,7 @@ object Types { case _ => NoType } - if (defn.isTypelevel_S(tycon.symbol) && args.length == 1) { + if (defn.isCompiletime_S(tycon.symbol) && args.length == 1) { trace(i"normalize S $this", typr, show = true) { args.head.normalized match { case ConstantType(Constant(n: Int)) => ConstantType(Constant(n + 1)) diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index bda00adc0bc4..b59a6191b1d0 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -271,7 +271,9 @@ trait Deriving { this: Typer => /** The RHS of the `reflectedClass` value definition */ private def reflectedClassRHS = - New(defn.ReflectedClassType, Literal(Constant(labelString(shapeWithClassParams))) :: Nil) + New(defn.ReflectedClassType, + List(Literal(Constant(cls.typeRef)), + Literal(Constant(labelString(shapeWithClassParams))))) /** The RHS of the `derived$Shaped` typeclass instance. * Example: For the class definition diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 658ca818d807..34504a43cf47 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -362,12 +362,12 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { def inlined(pt: Type): Tree = { if (callTypeArgs.length == 1) - if (inlinedMethod == defn.Typelevel_constValue) { + if (inlinedMethod == defn.Compiletime_constValue) { val constVal = tryConstValue if (!constVal.isEmpty) return constVal ctx.error(i"not a constant type: ${callTypeArgs.head}; cannot take constValue", call.sourcePos) } - else if (inlinedMethod == defn.Typelevel_constValueOpt) { + else if (inlinedMethod == defn.Compiletime_constValueOpt) { val constVal = tryConstValue return ( if (constVal.isEmpty) ref(defn.NoneModuleRef) @@ -479,7 +479,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { // Drop unused bindings val (finalBindings, finalExpansion) = dropUnusedDefs(bindingsBuf.toList, expansion1) - if (inlinedMethod == defn.Typelevel_error) issueError() + if (inlinedMethod == defn.Compiletime_error) issueError() // Take care that only argument bindings go into `bindings`, since positions are // different for bindings from arguments and bindings from body. @@ -991,7 +991,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { | patterns : ${tree.cases.map(patStr).mkString("\n ")}""" else em"""cannot reduce inline match with - | scrutinee: $sel : ${selType} + | scrutinee: $sel : ${selType} : ${selType.underlyingIfProxy} | patterns : ${tree.cases.map(patStr).mkString("\n ")}""" errorTree(tree, msg) } diff --git a/compiler/test/dotc/run-from-tasty.blacklist b/compiler/test/dotc/run-from-tasty.blacklist index c0eb1cde5b56..3c0fa861ad21 100644 --- a/compiler/test/dotc/run-from-tasty.blacklist +++ b/compiler/test/dotc/run-from-tasty.blacklist @@ -8,6 +8,7 @@ puzzle.scala implicitMatch.scala typeclass-derivation1.scala typeclass-derivation2.scala +typeclass-derivation2a.scala typeclass-derivation3.scala deriving-interesting-prefixes.scala diff --git a/library/src-bootstrapped/scala/compiletime/Shaped.scala b/library/src-bootstrapped/scala/compiletime/Shaped.scala index 7a64dc64337f..f2c7df2fb07a 100644 --- a/library/src-bootstrapped/scala/compiletime/Shaped.scala +++ b/library/src-bootstrapped/scala/compiletime/Shaped.scala @@ -3,4 +3,4 @@ package scala.compiletime /** Every generic derivation starts with a typeclass instance of this type. * It informs that type `T` has shape `S` and also implements runtime reflection on `T`. */ -abstract class Shaped[T, S <: Shape] extends Reflected[T] +abstract class Shaped[T, S <: Shape] extends scala.reflect.Reflected[T] diff --git a/library/src-bootstrapped/scala/compiletime/Mirror.scala b/library/src/scala/reflect/Mirror.scala similarity index 96% rename from library/src-bootstrapped/scala/compiletime/Mirror.scala rename to library/src/scala/reflect/Mirror.scala index d62dd0deedfe..92aa3175045c 100644 --- a/library/src-bootstrapped/scala/compiletime/Mirror.scala +++ b/library/src/scala/reflect/Mirror.scala @@ -1,4 +1,4 @@ -package scala.compiletime +package scala.reflect /** A generic representation of a case in an ADT * @param reflected The common class-speficic part of this mirror diff --git a/library/src-bootstrapped/scala/compiletime/Reflected.scala b/library/src/scala/reflect/Reflected.scala similarity index 93% rename from library/src-bootstrapped/scala/compiletime/Reflected.scala rename to library/src/scala/reflect/Reflected.scala index c938ab3287e3..fbeeb554fd64 100644 --- a/library/src-bootstrapped/scala/compiletime/Reflected.scala +++ b/library/src/scala/reflect/Reflected.scala @@ -1,4 +1,4 @@ -package scala.compiletime +package scala.reflect /** A class for mapping between an ADT value and * the case mirror that represents the value. diff --git a/library/src-bootstrapped/scala/compiletime/ReflectedClass.scala b/library/src/scala/reflect/ReflectedClass.scala similarity index 79% rename from library/src-bootstrapped/scala/compiletime/ReflectedClass.scala rename to library/src/scala/reflect/ReflectedClass.scala index c72c8839e71a..ebb35a12edf8 100644 --- a/library/src-bootstrapped/scala/compiletime/ReflectedClass.scala +++ b/library/src/scala/reflect/ReflectedClass.scala @@ -1,23 +1,28 @@ -package scala.compiletime +package scala.reflect import annotation.tailrec import collection.mutable.ArrayBuffer -/** @param labelsStr: A string encoding all case and element labels according to the +/** The part of `Reflected` instances that is common for all instances of a class. + * @param labelsStr: A string encoding all case and element labels according to the * following grammar: * * labelString ::= caseString { caseSeparator caseString } * caseString ::= elemString { elemSeparator elemString } - * caseSeparator ::= '\001' - * elemSeparator ::= '\000' - * elemString: "any sequence of characters not containing '\000` or `\001`" + * caseSeparator ::= '\u0001' + * elemSeparator ::= '\u0000' + * elemString: "any sequence of characters not containing '\u0000` or `\u0001`" */ -class ReflectedClass(labelsStr: String) { +class ReflectedClass(val runtimeClass: Class[_], labelsStr: String) { import ReflectedClass._ /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ def mirror(ordinal: Int, product: Product): Mirror = new Mirror(this, ordinal, product) + /** A mirror of a case with no elements */ + def mirror(ordinal: Int): Mirror = + mirror(ordinal, EmptyProduct) + /** A mirror with elements given as an array */ def mirror(ordinal: Int, elems: Array[AnyRef]): Mirror = mirror(ordinal, new ArrayProduct(elems)) @@ -26,10 +31,9 @@ class ReflectedClass(labelsStr: String) { def mirror(ordinal: Int, numElems: Int): Mirror = mirror(ordinal, new Array[AnyRef](numElems)) - /** A mirror of a case with no elements */ - def mirror(ordinal: Int): Mirror = - mirror(ordinal, EmptyProduct) - + /** Case and element labels as a two-dimensional array. + * Each row of the array contains a case label, followed by the labels of the elements of that case. + */ val label: Array[Array[String]] = initLabels(0, 0, new ArrayBuffer[String], new ArrayBuffer[Array[String]]) @@ -50,8 +54,8 @@ class ReflectedClass(labelsStr: String) { } object ReflectedClass { - private final val elemSeparator = '\000' - private final val caseSeparator = '\001' + private final val elemSeparator = '\u0000' + private final val caseSeparator = '\u0001' /** Helper class to turn arrays into products */ private class ArrayProduct(val elems: Array[AnyRef]) extends Product { diff --git a/tests/pos-special/typeclass-scaling.scala b/tests/pos-special/typeclass-scaling.scala index c46991073788..453c518e7415 100644 --- a/tests/pos-special/typeclass-scaling.scala +++ b/tests/pos-special/typeclass-scaling.scala @@ -215,6 +215,7 @@ object typeclasses { object Eq { import scala.compiletime.erasedValue import compiletime._ + import reflect._ inline def tryEql[T](x: T, y: T) = implicit match { case eq: Eq[T] => eq.eql(x, y) @@ -270,6 +271,7 @@ object typeclasses { object Pickler { import scala.compiletime.{erasedValue, constValue} import compiletime._ + import reflect._ def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index 47038ee3b498..43d31db292b5 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -133,10 +133,10 @@ object Lst { val NilMirror = mirror(1) - implicit def derived$Shape[T]: Shaped[Lst[T], Shape[T]] = new { + implicit def derived$Shaped[T]: Shaped[Lst[T], Shape[T]] = new { def reflect(xs: Lst[T]): Mirror = xs match { case xs: Cons[T] => mirror(0, xs) - case Nil => NilMirror + case Nil => mirror(1) } def reify(c: Mirror): Lst[T] = c.ordinal match { case 0 => Cons[T](c(0).asInstanceOf, c(1).asInstanceOf) @@ -171,7 +171,7 @@ object Pair { def common = reflectedClass } - // two clauses that could be generated from a `derives` clause + // clauses that could be generated from a `derives` clause implicit def derived$Eq[T: Eq]: Eq[Pair[T]] = Eq.derived implicit def derived$Pickler[T: Pickler]: Pickler[Pair[T]] = Pickler.derived implicit def derived$Show[T: Show]: Show[Pair[T]] = Show.derived @@ -222,11 +222,11 @@ object Eq { case eq: Eq[T] => eq.eql(x, y) } - inline def eqlElems[Elems <: Tuple](xs: Mirror, ys: Mirror, n: Int): Boolean = + inline def eqlElems[Elems <: Tuple](xm: Mirror, ym: Mirror, n: Int): Boolean = inline erasedValue[Elems] match { case _: (elem *: elems1) => - tryEql[elem](xs(n).asInstanceOf, ys(n).asInstanceOf) && - eqlElems[elems1](xs, ys, n + 1) + tryEql[elem](xm(n).asInstanceOf, ym(n).asInstanceOf) && + eqlElems[elems1](xm, ym, n + 1) case _: Unit => true } @@ -234,28 +234,27 @@ object Eq { inline def eqlCase[T, Elems <: Tuple](r: Reflected[T], x: T, y: T) = eqlElems[Elems](r.reflect(x), r.reflect(y), 0) - inline def eqlCases[T, Alts <: Tuple](r: Reflected[T], x: T, y: T): Boolean = + inline def eqlCases[T, Alts <: Tuple](xm: Mirror, ym: Mirror, ordinal: Int, n: Int): Boolean = inline erasedValue[Alts] match { case _: (Shape.Case[alt, elems] *: alts1) => - x match { - case x: `alt` => - y match { - case y: `alt` => eqlCase[T, elems](r, x, y) - case _ => false - } - case _ => eqlCases[T, alts1](r, x, y) - } - case _: Unit => - false - } + if (n == ordinal) eqlElems[elems](xm, ym, 0) + else eqlCases[T, alts1](xm, ym, ordinal, n + 1) + case _: Unit => + false + } - inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Eq[T] = new { - def eql(x: T, y: T): Boolean = inline erasedValue[S] match { + inline def eqlMain[T, S <: Shape](xm: Mirror, ym: Mirror): Boolean = + inline erasedValue[S] match { case _: Shape.Cases[alts] => - eqlCases[T, alts](ev, x, y) + val ord = xm.ordinal + ord == ym.ordinal && + eqlCases[T, alts](xm, ym, ord, 0) case _: Shape.Case[_, elems] => - eqlCase[T, elems](ev, x, y) + eqlElems[elems](xm, ym, 0) } + + inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Eq[T] = new { + def eql(x: T, y: T): Boolean = eqlMain[T, S](ev.reflect(x), ev.reflect(y)) } implicit object IntEq extends Eq[Int] { diff --git a/tests/run/typeclass-derivation2a.check b/tests/run/typeclass-derivation2a.check new file mode 100644 index 000000000000..fd3681599ffc --- /dev/null +++ b/tests/run/typeclass-derivation2a.check @@ -0,0 +1,10 @@ +ListBuffer(0, 11, 0, 22, 0, 33, 1) +Cons(11,Cons(22,Cons(33,Nil))) +ListBuffer(0, 0, 11, 0, 22, 0, 33, 1, 0, 0, 11, 0, 22, 1, 1) +Cons(Cons(11,Cons(22,Cons(33,Nil))),Cons(Cons(11,Cons(22,Nil)),Nil)) +ListBuffer(1, 2) +Pair(1,2) +Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil()))) +Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil()))), tl = Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Nil())), tl = Nil())) +Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil())) +Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil())) diff --git a/tests/run/typeclass-derivation2a.scala b/tests/run/typeclass-derivation2a.scala new file mode 100644 index 000000000000..1d58cdba6e20 --- /dev/null +++ b/tests/run/typeclass-derivation2a.scala @@ -0,0 +1,476 @@ +import scala.collection.mutable +import scala.annotation.tailrec + +object TypeLevel { + /** @param caseLabels The case and element labels of the described ADT as encoded strings. + */ + class ReflectedClass(labelsStr: String) { + import ReflectedClass._ + + /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ + def mirror(ordinal: Int, product: Product): Mirror = + new Mirror(this, ordinal, product) + + /** A mirror with elements given as an array */ + def mirror(ordinal: Int, elems: Array[AnyRef]): Mirror = + mirror(ordinal, new ArrayProduct(elems)) + + /** A mirror with an initial empty array of `numElems` elements, to be filled in. */ + def mirror(ordinal: Int, numElems: Int): Mirror = + mirror(ordinal, new Array[AnyRef](numElems)) + + /** A mirror of a case with no elements */ + def mirror(ordinal: Int): Mirror = + mirror(ordinal, EmptyProduct) + + val label: Array[Array[String]] = + initLabels(0, 0, new mutable.ArrayBuffer[String], new mutable.ArrayBuffer[Array[String]]) + + private final val elemSeparator = '\000' + private final val caseSeparator = '\001' + + private def initLabels(start: Int, cur: Int, + elems: mutable.ArrayBuffer[String], + cases: mutable.ArrayBuffer[Array[String]]): Array[Array[String]] = { + def addElem = elems += labelsStr.substring(start, cur) + def addCase = cases += addElem.toArray + if (cur == labelsStr.length) + addCase.toArray + else if (labelsStr(cur) == caseSeparator) + initLabels(cur + 1, cur + 1, new mutable.ArrayBuffer, addCase) + else if (labelsStr(cur) == elemSeparator) + initLabels(cur + 1, cur + 1, addElem, cases) + else + initLabels(start, cur + 1, elems, cases) + } + } + + object ReflectedClass { + /** Helper class to turn arrays into products */ + private class ArrayProduct(val elems: Array[AnyRef]) extends Product { + def canEqual(that: Any): Boolean = true + def productElement(n: Int) = elems(n) + def productArity = elems.length + override def productIterator: Iterator[Any] = elems.iterator + def update(n: Int, x: Any) = elems(n) = x.asInstanceOf[AnyRef] + } + + /** Helper object */ + private object EmptyProduct extends Product { + def canEqual(that: Any): Boolean = true + def productElement(n: Int) = throw new IndexOutOfBoundsException + def productArity = 0 + } + } + + /** A generic representation of a case in an ADT + * @param deriving The companion object of the ADT + * @param ordinal The ordinal value of the case in the list of the ADT's cases + * @param elems The elements of the case + */ + class Mirror(val reflected: ReflectedClass, val ordinal: Int, val elems: Product) { + + /** The `n`'th element of this generic case */ + def apply(n: Int): Any = elems.productElement(n) + + /** The name of the constructor of the case reflected by this mirror */ + def caseLabel: String = reflected.label(ordinal)(0) + + /** The label of the `n`'th element of the case reflected by this mirror */ + def elementLabel(n: Int) = reflected.label(ordinal)(n + 1) + } + + /** A class for mapping between an ADT value and + * the case mirror that represents the value. + */ + abstract class Reflected[T] { + + /** The case mirror corresponding to ADT instance `x` */ + def reflect(x: T): Mirror + + /** The ADT instance corresponding to given `mirror` */ + def reify(mirror: Mirror): T + + /** The companion object of the ADT */ + def common: ReflectedClass + } + + /** The shape of an ADT. + * This is eithe a product (`Case`) or a sum (`Cases`) of products. + */ + enum Shape { + + /** A sum with alternative types `Alts` */ + case Cases[Alts <: Tuple] + + /** A product type `T` with element types `Elems` */ + case Case[T, Elems <: Tuple] + } + + /** Every generic derivation starts with a typeclass instance of this type. + * It informs that type `T` has shape `S` and also implements runtime reflection on `T`. + */ + abstract class Shaped[T, S <: Shape] extends Reflected[T] +} + +// An algebraic datatype +enum Lst[+T] { // derives Eq, Pickler, Show + case Cons(hd: T, tl: Lst[T]) + case Nil +} + +object Lst { + // common compiler-generated infrastructure + import TypeLevel._ + + type Shape[T] = Shape.Cases[( + Shape.Case[Cons[T], (T, Lst[T])], + Shape.Case[Nil.type, Unit] + )] + + val reflectedClass = new ReflectedClass("Cons\000hd\000tl\001Nil") + import reflectedClass.mirror + + val NilMirror = mirror(1) + + implicit def derived$Shaped[T]: Shaped[Lst[T], Shape[T]] = new { + def reflect(xs: Lst[T]): Mirror = xs match { + case xs: Cons[T] => mirror(0, xs) + case Nil => mirror(1) + } + def reify(c: Mirror): Lst[T] = c.ordinal match { + case 0 => Cons[T](c(0).asInstanceOf, c(1).asInstanceOf) + case 1 => Nil + } + def common = reflectedClass + } + + // three clauses that could be generated from a `derives` clause + implicit def derived$Eq[T: Eq]: Eq[Lst[T]] = Eq.derived + implicit def derived$Pickler[T: Pickler]: Pickler[Lst[T]] = Pickler.derived + implicit def derived$Show[T: Show]: Show[Lst[T]] = Show.derived +} + +// A simple product type +case class Pair[T](x: T, y: T) // derives Eq, Pickler, Show + +object Pair { + // common compiler-generated infrastructure + import TypeLevel._ + + type Shape[T] = Shape.Case[Pair[T], (T, T)] + + val reflectedClass = new ReflectedClass("Pair\000x\000y") + import reflectedClass.mirror + + implicit def derived$Shape[T]: Shaped[Pair[T], Shape[T]] = new { + def reflect(xy: Pair[T]) = + mirror(0, xy) + def reify(c: Mirror): Pair[T] = + Pair(c(0).asInstanceOf, c(1).asInstanceOf) + def common = reflectedClass + } + + // clauses that could be generated from a `derives` clause + implicit def derived$Eq[T: Eq]: Eq[Pair[T]] = Eq.derived + implicit def derived$Pickler[T: Pickler]: Pickler[Pair[T]] = Pickler.derived + implicit def derived$Show[T: Show]: Show[Pair[T]] = Show.derived +} + +sealed trait Either[+L, +R] extends Product // derives Eq, Pickler, Show +case class Left[L](x: L) extends Either[L, Nothing] +case class Right[R](x: R) extends Either[Nothing, R] + +object Either { + import TypeLevel._ + + type Shape[L, R] = Shape.Cases[( + Shape.Case[Left[L], L *: Unit], + Shape.Case[Right[R], R *: Unit] + )] + + val reflectedClass = new ReflectedClass("Left\000x\001Right\000x") + import reflectedClass.mirror + + implicit def derived$Shape[L, R]: Shaped[Either[L, R], Shape[L, R]] = new { + def reflect(e: Either[L, R]): Mirror = e match { + case e: Left[L] => mirror(0, e) + case e: Right[R] => mirror(1, e) + } + def reify(c: Mirror): Either[L, R] = c.ordinal match { + case 0 => Left(c(0).asInstanceOf) + case 1 => Right(c(0).asInstanceOf) + } + def common = reflectedClass + } + + implicit def derived$Eq[L: Eq, R: Eq]: Eq[Either[L, R]] = Eq.derived + implicit def derived$Pickler[L: Pickler, R: Pickler]: Pickler[Either[L, R]] = Pickler.derived + implicit def derived$Show[L: Show, R: Show]: Show[Either[L, R]] = Show.derived +} + +// A typeclass +trait Eq[T] { + def eql(x: T, y: T): Boolean +} + +object Eq { + import scala.compiletime.erasedValue + import TypeLevel._ + + inline def tryEql[T](x: T, y: T) = implicit match { + case eq: Eq[T] => eq.eql(x, y) + } + + inline def eqlElems[Elems <: Tuple](xm: Mirror, ym: Mirror, n: Int): Boolean = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + tryEql[elem](xm(n).asInstanceOf, ym(n).asInstanceOf) && + eqlElems[elems1](xm, ym, n + 1) + case _: Unit => + true + } + + inline def eqlCases[Alts <: Tuple](xm: Mirror, ym: Mirror, n: Int): Boolean = + inline erasedValue[Alts] match { + case _: (Shape.Case[alt, elems] *: alts1) => + if (xm.ordinal == n) eqlElems[elems](xm, ym, 0) + else eqlCases[alts1](xm, ym, n + 1) + case _: Unit => + false + } + + inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Eq[T] = new { + def eql(x: T, y: T): Boolean = { + val xm = ev.reflect(x) + val ym = ev.reflect(y) + inline erasedValue[S] match { + case _: Shape.Cases[alts] => + xm.ordinal == ym.ordinal && + eqlCases[alts](xm, ym, 0) + case _: Shape.Case[_, elems] => + eqlElems[elems](xm, ym, 0) + } + } + } + + implicit object IntEq extends Eq[Int] { + def eql(x: Int, y: Int) = x == y + } +} + +// Another typeclass +trait Pickler[T] { + def pickle(buf: mutable.ListBuffer[Int], x: T): Unit + def unpickle(buf: mutable.ListBuffer[Int]): T +} + +object Pickler { + import scala.compiletime.{erasedValue, constValue} + import TypeLevel._ + + def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) + + inline def tryPickle[T](buf: mutable.ListBuffer[Int], x: T): Unit = implicit match { + case pkl: Pickler[T] => pkl.pickle(buf, x) + } + + inline def pickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Mirror, n: Int): Unit = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + tryPickle[elem](buf, elems(n).asInstanceOf[elem]) + pickleElems[elems1](buf, elems, n + 1) + case _: Unit => + } + + inline def pickleCases[Alts <: Tuple](buf: mutable.ListBuffer[Int], xm: Mirror, n: Int): Unit = + inline erasedValue[Alts] match { + case _: (Shape.Case[alt, elems] *: alts1) => + if (xm.ordinal == n) pickleElems[elems](buf, xm, 0) + else pickleCases[alts1](buf, xm, n + 1) + case _: Unit => + } + + inline def tryUnpickle[T](buf: mutable.ListBuffer[Int]): T = implicit match { + case pkl: Pickler[T] => pkl.unpickle(buf) + } + + inline def unpickleElems[Elems <: Tuple](buf: mutable.ListBuffer[Int], elems: Array[AnyRef], n: Int): Unit = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + elems(n) = tryUnpickle[elem](buf).asInstanceOf[AnyRef] + unpickleElems[elems1](buf, elems, n + 1) + case _: Unit => + } + + inline def unpickleCase[T, Elems <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { + inline val size = constValue[Tuple.Size[Elems]] + inline if (size == 0) + r.reify(r.common.mirror(ordinal)) + else { + val elems = new Array[Object](size) + unpickleElems[Elems](buf, elems, 0) + r.reify(r.common.mirror(ordinal, elems)) + } + } + + inline def unpickleCases[T, Alts <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = + inline erasedValue[Alts] match { + case _: (Shape.Case[_, elems] *: alts1) => + if (n == ordinal) unpickleCase[T, elems](r, buf, ordinal) + else unpickleCases[T, alts1](r, buf, ordinal, n + 1) + case _ => + throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ordinal") + } + + inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Pickler[T] = new { + def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = { + val xm = ev.reflect(x) + inline erasedValue[S] match { + case _: Shape.Cases[alts] => + buf += xm.ordinal + pickleCases[alts](buf, xm, 0) + case _: Shape.Case[_, elems] => + pickleElems[elems](buf, xm, 0) + } + } + def unpickle(buf: mutable.ListBuffer[Int]): T = inline erasedValue[S] match { + case _: Shape.Cases[alts] => + unpickleCases[T, alts](ev, buf, nextInt(buf), 0) + case _: Shape.Case[_, elems] => + unpickleCase[T, elems](ev, buf, 0) + } + } + + implicit object IntPickler extends Pickler[Int] { + def pickle(buf: mutable.ListBuffer[Int], x: Int): Unit = buf += x + def unpickle(buf: mutable.ListBuffer[Int]): Int = nextInt(buf) + } +} + +// A third typeclass, making use of labels +trait Show[T] { + def show(x: T): String +} +object Show { + import scala.compiletime.erasedValue + import TypeLevel._ + + inline def tryShow[T](x: T): String = implicit match { + case s: Show[T] => s.show(x) + } + + inline def showElems[Elems <: Tuple](elems: Mirror, n: Int): List[String] = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + val formal = elems.elementLabel(n) + val actual = tryShow[elem](elems(n).asInstanceOf) + s"$formal = $actual" :: showElems[elems1](elems, n + 1) + case _: Unit => + Nil + } + + inline def showCases[Alts <: Tuple](xm: Mirror, n: Int): String = + inline erasedValue[Alts] match { + case _: (Shape.Case[alt, elems] *: alts1) => + if (xm.ordinal == n) showElems[elems](xm, 0).mkString(", ") + else showCases[alts1](xm, n + 1) + case _: Unit => + throw new MatchError(xm) + } + + inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Show[T] = new { + def show(x: T): String = { + val xm = ev.reflect(x) + val args = inline erasedValue[S] match { + case _: Shape.Cases[alts] => + showCases[alts](xm, 0) + case _: Shape.Case[_, elems] => + showElems[elems](xm, 0).mkString(", ") + } + s"${xm.caseLabel}($args)" + } + } + + implicit object IntShow extends Show[Int] { + def show(x: Int): String = x.toString + } +} + +// Tests +object Test extends App { + import TypeLevel._ + val eq = implicitly[Eq[Lst[Int]]] + val xs = Lst.Cons(11, Lst.Cons(22, Lst.Cons(33, Lst.Nil))) + val ys = Lst.Cons(11, Lst.Cons(22, Lst.Nil)) + assert(eq.eql(xs, xs)) + assert(!eq.eql(xs, ys)) + assert(!eq.eql(ys, xs)) + assert(eq.eql(ys, ys)) + + val eq2 = implicitly[Eq[Lst[Lst[Int]]]] + val xss = Lst.Cons(xs, Lst.Cons(ys, Lst.Nil)) + val yss = Lst.Cons(xs, Lst.Nil) + assert(eq2.eql(xss, xss)) + assert(!eq2.eql(xss, yss)) + assert(!eq2.eql(yss, xss)) + assert(eq2.eql(yss, yss)) + + val buf = new mutable.ListBuffer[Int] + val pkl = implicitly[Pickler[Lst[Int]]] + pkl.pickle(buf, xs) + println(buf) + val xs1 = pkl.unpickle(buf) + println(xs1) + assert(xs1 == xs) + assert(eq.eql(xs1, xs)) + + val pkl2 = implicitly[Pickler[Lst[Lst[Int]]]] + pkl2.pickle(buf, xss) + println(buf) + val xss1 = pkl2.unpickle(buf) + println(xss1) + assert(xss == xss1) + assert(eq2.eql(xss, xss1)) + + val p1 = Pair(1, 2) + val p2 = Pair(1, 2) + val p3 = Pair(2, 1) + val eqp = implicitly[Eq[Pair[Int]]] + assert(eqp.eql(p1, p2)) + assert(!eqp.eql(p2, p3)) + + val pklp = implicitly[Pickler[Pair[Int]]] + pklp.pickle(buf, p1) + println(buf) + val p1a = pklp.unpickle(buf) + println(p1a) + assert(p1 == p1a) + assert(eqp.eql(p1, p1a)) + + def showPrintln[T: Show](x: T): Unit = + println(implicitly[Show[T]].show(x)) + showPrintln(xs) + showPrintln(xss) + + val zs = Lst.Cons(Left(1), Lst.Cons(Right(Pair(2, 3)), Lst.Nil)) + showPrintln(zs) + + def pickle[T: Pickler](buf: mutable.ListBuffer[Int], x: T): Unit = + implicitly[Pickler[T]].pickle(buf, x) + + def unpickle[T: Pickler](buf: mutable.ListBuffer[Int]): T = + implicitly[Pickler[T]].unpickle(buf) + + def copy[T: Pickler](x: T): T = { + val buf = new mutable.ListBuffer[Int] + pickle(buf, x) + unpickle[T](buf) + } + + def eql[T: Eq](x: T, y: T) = implicitly[Eq[T]].eql(x, y) + + val zs1 = copy(zs) + showPrintln(zs1) + assert(eql(zs, zs1)) +} \ No newline at end of file diff --git a/tests/run/typeclass-derivation3.scala b/tests/run/typeclass-derivation3.scala index 1663791dd9c7..d852df9a71ca 100644 --- a/tests/run/typeclass-derivation3.scala +++ b/tests/run/typeclass-derivation3.scala @@ -32,6 +32,7 @@ object typeclasses { object Eq { import scala.compiletime.erasedValue import compiletime._ + import reflect.Mirror inline def tryEql[T](x: T, y: T) = implicit match { case eq: Eq[T] => eq.eql(x, y) @@ -46,30 +47,26 @@ object typeclasses { true } - inline def eqlCase[T, Elems <: Tuple](r: Reflected[T], x: T, y: T) = - eqlElems[Elems](r.reflect(x), r.reflect(y), 0) - - inline def eqlCases[T, Alts <: Tuple](r: Reflected[T], x: T, y: T): Boolean = + inline def eqlCases[Alts <: Tuple](xm: Mirror, ym: Mirror, n: Int): Boolean = inline erasedValue[Alts] match { case _: (Shape.Case[alt, elems] *: alts1) => - x match { - case x: `alt` => - y match { - case y: `alt` => eqlCase[T, elems](r, x, y) - case _ => false - } - case _ => eqlCases[T, alts1](r, x, y) - } + if (xm.ordinal == n) eqlElems[elems](xm, ym, 0) + else eqlCases[alts1](xm, ym, n + 1) case _: Unit => - false - } + false + } inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Eq[T] = new { - def eql(x: T, y: T): Boolean = inline erasedValue[S] match { - case _: Shape.Cases[alts] => - eqlCases[T, alts](ev, x, y) - case _: Shape.Case[_, elems] => - eqlCase[T, elems](ev, x, y) + def eql(x: T, y: T): Boolean = { + val xm = ev.reflect(x) + val ym = ev.reflect(y) + inline erasedValue[S] match { + case _: Shape.Cases[alts] => + xm.ordinal == ym.ordinal && + eqlCases[alts](xm, ym, 0) + case _: Shape.Case[_, elems] => + eqlElems[elems](xm, ym, 0) + } } } @@ -87,6 +84,7 @@ object typeclasses { object Pickler { import scala.compiletime.{erasedValue, constValue} import compiletime._ + import reflect.{Mirror, Reflected} def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) @@ -102,19 +100,11 @@ object typeclasses { case _: Unit => } - inline def pickleCase[T, Elems <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], x: T): Unit = - pickleElems[Elems](buf, r.reflect(x), 0) - - inline def pickleCases[T, Alts <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], x: T, n: Int): Unit = + inline def pickleCases[Alts <: Tuple](buf: mutable.ListBuffer[Int], xm: Mirror, n: Int): Unit = inline erasedValue[Alts] match { case _: (Shape.Case[alt, elems] *: alts1) => - x match { - case x: `alt` => - buf += n - pickleCase[T, elems](r, buf, x) - case _ => - pickleCases[T, alts1](r, buf, x, n + 1) - } + if (xm.ordinal == n) pickleElems[elems](buf, xm, 0) + else pickleCases[alts1](buf, xm, n + 1) case _: Unit => } @@ -151,11 +141,15 @@ object typeclasses { } inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Pickler[T] = new { - def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = inline erasedValue[S] match { - case _: Shape.Cases[alts] => - pickleCases[T, alts](ev, buf, x, 0) - case _: Shape.Case[_, elems] => - pickleCase[T, elems](ev, buf, x) + def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = { + val xm = ev.reflect(x) + inline erasedValue[S] match { + case _: Shape.Cases[alts] => + buf += xm.ordinal + pickleCases[alts](buf, xm, 0) + case _: Shape.Case[_, elems] => + pickleElems[elems](buf, xm, 0) + } } def unpickle(buf: mutable.ListBuffer[Int]): T = inline erasedValue[S] match { case _: Shape.Cases[alts] => @@ -178,6 +172,7 @@ object typeclasses { object Show { import scala.compiletime.erasedValue import compiletime._ + import reflect.Mirror inline def tryShow[T](x: T): String = implicit match { case s: Show[T] => s.show(x) @@ -193,29 +188,25 @@ object typeclasses { Nil } - inline def showCase[T, Elems <: Tuple](r: Reflected[T], x: T): String = { - val mirror = r.reflect(x) - val args = showElems[Elems](mirror, 0).mkString(", ") - s"${mirror.caseLabel}($args)" - } - - inline def showCases[T, Alts <: Tuple](r: Reflected[T], x: T): String = + inline def showCases[Alts <: Tuple](xm: Mirror, n: Int): String = inline erasedValue[Alts] match { case _: (Shape.Case[alt, elems] *: alts1) => - x match { - case x: `alt` => showCase[T, elems](r, x) - case _ => showCases[T, alts1](r, x) - } + if (xm.ordinal == n) showElems[elems](xm, 0).mkString(", ") + else showCases[alts1](xm, n + 1) case _: Unit => - throw new MatchError(x) + throw new MatchError(xm) } inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Show[T] = new { - def show(x: T): String = inline erasedValue[S] match { - case _: Shape.Cases[alts] => - showCases[T, alts](ev, x) - case _: Shape.Case[_, elems] => - showCase[T, elems](ev, x) + def show(x: T): String = { + val xm = ev.reflect(x) + val args = inline erasedValue[S] match { + case _: Shape.Cases[alts] => + showCases[alts](xm, 0) + case _: Shape.Case[_, elems] => + showElems[elems](xm, 0).mkString(", ") + } + s"${xm.caseLabel}($args)" } } From 0db9e9d2c7099e9fdfa1b5aeeb56e0451e1abb59 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Dec 2018 15:29:22 +0100 Subject: [PATCH 23/56] Add docs --- docs/docs/reference/derivation.md | 372 ++++++++++++++++++++++++++++++ docs/sidebar.yml | 2 + 2 files changed, 374 insertions(+) create mode 100644 docs/docs/reference/derivation.md diff --git a/docs/docs/reference/derivation.md b/docs/docs/reference/derivation.md new file mode 100644 index 000000000000..fa86a979de5a --- /dev/null +++ b/docs/docs/reference/derivation.md @@ -0,0 +1,372 @@ +--- +layout: doc-page +title: Typeclass Derivation +--- + +Implicit instances for some typeclass traits can be derived automatically. Example: +```scala +enum Tree[T] derives Eq, Ordering, Pickling { + case Branch(left: Tree[T], right: Tree[T]) + case Leaf(elem: T) +} +``` +The derives clause automatically generates typeclass instances for +`Eq`, `Ordering`, and `Pickling` in the companion object `Tree`: +```scala +impl [T: Eq] of Eq[Tree[T]] = Eq.derived +impl [T: Ordering] of Ordering[Tree[T]] = Ordering.derived +impl [T: Pickling] of Pickling[Tree[T]] = Pickling.derived +``` + +**Note**: This page uses the new syntax proposed for implicits that is explored in #5448. This is not yet an endorsement of that syntax, but rather a way to experiment with it. + +### Deriving Types + +Besides for `enums`, typeclasses can also be derived for other sets of classes and objects that form an algebraic data type. These are: + + - individual case classes or case objects + - sealed classes or traits that have only case classes and case objects as children. + + Examples: + + ```scala +case class Labelled[T](x: T, label: String) derives Eq, Show + +sealed trait Option[T] derives Eq +case class Some[T] extends Option[T] +case object None extends Option[Nothing] +``` + +The generated typeclass instances are placed in the companion objects `Labelled` and `Option`, respectively. + +### Derivable Traits + +A trait can appear in a `derives` clause as long as + + - it has a single type parameter, + - its companion object defines a method named `derived`. + +These two conditions ensure that the synthesized derived instances for the trait are well-formed. The type and implementation of a `derived` method are arbitrary, but typically it has a definition like this: +``` + def derived[T] with (ev: Shaped[T, S]) = ... +``` +That is, the `derived` method takes an implicit parameter of type `Shaped` that determines the _shape_ `S` of the deriving type `T` and it computes the typeclass implementation according to that shape. Implicit `Shaped` instances are generated automatically for all types that have a `derives` clause. + +This is all a user of typeclass derivation has to know. The rest of this page contains information needed to be able to write a typeclass that can be used in a `derives` clause. In particular, it details the means provided for the implementation of data generic `derived` methods. + + +### The Shape Type + +For every class with a `derives` clause, the compiler generates a type named `Shape` in the companion object of that class. For instance, here is the generated `Shape` type for the `Tree` enum: +```scala +type Shape[T] = Cases[ + Case[Branch[T], (Tree[T], Tree[T])], + Case[Leaf[T], T *: Unit] +] +``` +Informally, this states that + +> The shape of a `Tree[T]` is one of two cases: Either a `Branch[T]` with two + elements of type `Tree[T]`, or a `Leaf[T]` with a single element of type `T`. + +The type constructors `Cases` and `Case` come from the companion object of a class +`scala.compiletime.Shape`, which is defined in the standard library as follows: +```scala +sealed abstract class Shape + +object Shape { + + /** A sum with alternative types `Alts` */ + case class Cases[Alts <: Tuple] extends Shape + + /** A product type `T` with element types `Elems` */ + case class Case[T, Elems <: Tuple] extends Shape +} +``` + +Here is the `Shape` type for `Labelled`: +```scala +type Shape[T] = Case[Labelled[T], (T, String)] +``` +And here is the one for `Option`: +```scala +type Shape[T] = Cases[ + Case[Some[T], T * Unit], + Case [None.type, Unit] +] +``` +Note that an empty element tuple is represented as type `Unit`. A single-element tuple +is represented as `T *: Unit` since there is no direct syntax for such tuples: `(T)` is just `T` in parentheses, not a tuple. + +The `Shape` type generation is suppressed if the companion object already contains a type member named `Shape`. + +### The Shaped TypeClass + +For every class `C[T_1,...,T_n]` with a `derives` clause, the compiler also generates a type class instance like this: +```scala +impl [T_1, ..., T_n] of Shaped[C[T_1,...,T_n], Shape[T_1,...,T_n]] ... +``` +This instance is generated together with the `Shape` type in the companion object of the class. +For instance, the definition +```scala +enum Result[+T, +E] derives Logging { + case class Ok[T](result: T) + case class Err[E](err: E) +} +``` +would produce the following members: +```scala +object Result { + import scala.compiletime.Shape._ + + type Shape[T, E] = Cases[( + Case[Ok[T], T *: Unit], + Case[Err[E], E *: Unit] + )] + + impl [T, E] of Shaped[Result[T, E], Shape[T, E]] = ... +} +``` + +The `Shaped` class is defined in package `scala.reflect`. + +```scala +abstract class Shaped[T, S <: Shape] extends Reflected[T] +``` +It is a subclass of class `scala.reflect.Reflected`, which defines two methods that map between a type `T` and a generic representation of `T`, which we call a `Mirror`: +```scala +abstract class Reflected[T] { + + /** The mirror corresponding to ADT instance `x` */ + def reflect(x: T): Mirror + + /** The ADT instance corresponding to given `mirror` */ + def reify(mirror: Mirror): T + + /** The companion object of the ADT */ + def common: ReflectedClass +} +``` + +The `reflect` method maps an instance value of the ADT `T` to its mirror whereas the `reify` method goes the other way. There's also a `common` method that returns a value of type `ReflectedClass` which contains information that is the same +for all instances of a class (right now, this consists of essentially just the names of the cases and their parameters). + +### Mirrors + +A mirror is a generic representation of an instance value of an ADT. `Mirror` objects have three components: + + - `reflected: ReflectedClass`: The representation of the ADT class + - `ordinal: Int`: The ordinal number of the case among all cases of the ADT, starting from 0 + - `elems: Product`: The elements of the instance, represented as a `Product`. + + The `Mirror` class is defined in package `scala.reflect` as follows: + +```scala +class Mirror(val reflected: ReflectedClass, val ordinal: Int, val elems: Product) { + + /** The `n`'th element of this generic case */ + def apply(n: Int): Any = elems.productElement(n) + + /** The name of the constructor of the case reflected by this mirror */ + def caseLabel: String = reflected.label(ordinal)(0) + + /** The label of the `n`'th element of the case reflected by this mirror */ + def elementLabel(n: Int) = reflected.label(ordinal)(n + 1) +} +``` + +### ReflectedClass + +Here's the API of `scala.reflect.ReflectedClass`: + +```scala +class ReflectedClass(val runtimeClass: Class[_], labelsStr: String) { + + /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ + def mirror(ordinal: Int, product: Product): Mirror = + new Mirror(this, ordinal, product) + + /** A mirror with elements given as an array */ + def mirror(ordinal: Int, elems: Array[AnyRef]): Mirror = + mirror(ordinal, new ArrayProduct(elems)) + + /** A mirror with an initial empty array of `numElems` elements, to be filled in. */ + def mirror(ordinal: Int, numElems: Int): Mirror = + mirror(ordinal, new Array[AnyRef](numElems)) + + /** A mirror of a case with no elements */ + def mirror(ordinal: Int): Mirror = + mirror(ordinal, EmptyProduct) + + + /** Case and element labels as a two-dimensional array. + * Each row of the array contains a case label, followed by the labels of the elements of that case. + */ + val label: Array[Array[String]] = ... +``` + +The class provides four overloaded methods to create mirrors. The first of these is invoked by the `reify` method that maps an ADT instance to its mirror. It simply passes the +instance itself (which is a `Product`) to the second parameter of the mirror. That operation does not involve any copying and is thus quite efficient. The second and third versions of `mirror` are typically invoked by typeclass methods that create instances from mirrors. An example would be an `unpickle` method that first creates an array of elements, then creates +a mirror over that array, and finally uses the `reify` method in `Reflected` to create the ADT instance. The fourth version of `mirror` is used to create mirrors of instances that do not have any elements. + +### How to Write Generic Typeclasses + +Based on the machinery developed so far it becomes possible to define type classes generically. This means that the `derived` method will compute a type class instance for any ADT that has a `Shaped` instance, recursively. +The implementation of these methods typically uses three new typelevel constructs in Dotty: inline methods, inline matches and implicit matches. As an example, here is one possible implementation of a generic `Eq` type class, with explanations. Let's assume `Eq` is defined by the following trait: +```scala +trait Eq[T] { + def eql(x: T, y: T): Boolean +} +``` +We need to implement a method `Eq.derived` that produces an instance of `Eq[T]` provided +there exists evidence of type `Shaped[T, S]` for some shape `S`. Here's a possible solution: +```scala + inline def derived[T, S <: Shape] with (ev: Shaped[T, S]): Eq[T] = new Eq[T] { + def eql(x: T, y: T): Boolean = { + val mx = ev.reflect(x) // (1) + val my = ev.reflect(y) // (2) + inline erasedValue[S] match { + case _: Cases[alts] => + mx.ordinal == my.ordinal && // (3) + eqlCases[alts](mx, my, 0) // [4] + case _: Case[_, elems] => + eqlElems[elems](mx, my, 0) // [5] + } + } + } +``` +The implementation of the inline method `derived` creates an instance of `Eq[T]` and implements its `eql` method. The right hand side of `eql` mixes compile-time and runtime elements. In the code above, runtime elements are marked with a number in parentheses, i.e +`(1)`, `(2)`, `(3)`. Compile-time calls that expand to runtime code are marked with a number in brackets, i.e. `[4]`, `[5]`. The implementation of `eql` consists of the following steps. + + 1. Map the compared values `x` and `y` to their mirrors using the `reflect` method of the implicitly passed `Shaped` evidence `(1)`, `(2)`. + 2. Match at compile-time against the type `S`. Dotty does not have a construct for matching types directly, buy we can emulate it using an `inline` match over an `erasedValue`. Depending on the actual type `S`, the match will reduce at compile time to one of its two alternatives. + 3. If `S` is of the form `Cases[alts]` for some tuple `alts` of alternative types, the equality test consists of comparing the ordinal values of the two mirrors `(3)` and, if they are equal, comparing the elements of the case indicated by that ordinal value. That second step is performed by code that results from the compile-time expansion of the `eqlCases` call `[4]`. + 4. If `S` is of the form `Case[elems]` for some tuple `elems` for element types, the elements of the case are compared by code that results from the compile-time expansion of the `eqlElems` call `[5]`. + +Here is a possible implementation of `eqlCases`: +```scala + inline def eqlCases[Alts <: Tuple](mx: Mirror, my: Mirror, n: Int): Boolean = + inline erasedValue[Alts] match { + case _: (Shape.Case[_, elems] *: alts1) => + if (mx.ordinal == n) // (6) + eqlElems[elems](mx, my, 0) // [7] + else + eqlCases[alts1](mx, my, n + 1) // [8] + case _: Unit => + throw new MatchError(mx.ordinal) // (9) + } +``` +The inline method `eqlCases` takes as type arguments the alternatives of the ADT that remain to be tested. It takes as value arguments mirrors of the two instances `x` and `y` to be compared and an integer `n` that indicates the ordinal number of the case that is tested next. Its produces an expression that compares these two values. + +If the list of alternatives `Alts` consists of a case of type `Case[_, elems]`, possibly followed by further cases in `alts1`, we generate the following code: + + 1. Compare the `ordinal` value of `mx` (a runtime value) with the case number `n` (a compile-time value translated to a constant in the generated code) in an if-then-else `(6)`. + 2. In the then-branch of the conditional we have that the `ordinal` value of both mirrors + matches the number of the case with elements `elems`. Proceed by comparing the elements + of the case in code expanded from the `eqlElems` call `[7]`. + 3. In the else-branch of the conditional we have that the present case does not match + the ordinal value of both mirrors. Proceed by trying the remaining cases in `alts1` using + code expanded from the `eqlCases` call `[8]`. + + If the list of alternatives `Alts` is the empty tuple, there are no further cases to check. + This place in the code should not be reachable at runtime. Therefore an appropriate + implementation is by throwing a `MatchError` or some other runtime exception `(9)`. + +The `eqlElems` method compares the elements of two mirrors that are known to have the same +ordinal number, which means they represent the same case of the ADT. Here is a possible +implementation: +```scala + inline def eqlElems[Elems <: Tuple](xs: Mirror, ys: Mirror, n: Int): Boolean = + inline erasedValue[Elems] match { + case _: (elem *: elems1) => + tryEql[elem]( // [12] + xs(n).asInstanceOf[elem], // (10) + ys(n).asInstanceOf[elem]) && // (11) + eqlElems[elems1](xs, ys, n + 1) // [13] + case _: Unit => + true // (14) + } +``` +`eqlElems` takes as arguments the two mirrors of the elements to compare and a compile-time index `n`, indicating the index of the next element to test. It is defined in terms of an another compile-time match, this time over the tuple type `Elems` of all element types that remain to be tested. If that type is +non-empty, say of form `elem *: elems1`, the following code is produced: + + 1. Access the `n`'th elements of both mirrors and cast them to the current element type `elem` + `(10)`, `(11)`. Note that because of the way runtime reflection mirrors compile-time `Shape` types, the casts are guaranteed to succeed. + 2. Compare the element values using code expanded by the `tryEql` call `[12]`. + 3. "And" the result with code that compares the remaining elements using a recursive call + to `eqlElems` `[13]`. + + If type `Elems` is empty, there are no more elements to be compared, so the comparison's result is `true`. `(14)` + + Since `eqlElems` is an inline method, its recursive calls are unrolled. The end result is a conjunction `test_1 && ... && test_n && true` of test expressions produced by the `tryEql` calls. + +The last, and in a sense most interesting part of the derivation is the comparison of a pair of element values in `tryEql`. Here is the definition of this method: +```scala + inline def tryEql[T](x: T, y: T) = implicit match { + case ev: Eq[T] => + ev.eql(x, y) // (15) + case _ => + error("No `Eq` instance was found for $T") + } +``` +`tryEql` is an inline method that takes an element type `T` and two element values of that type as arguments. It is defined using an `inline match` that tries to find an implicit instance of `Eq[T]`. If an instance `ev` is found, it proceeds by comparing the arguments using `ev.eql`. On the other hand, if no instance is found +this signals a compilation error: the user tried a generic derivation of `Eq` for a class with an element type that does not support an `Eq` instance itself. The error is signalled by +calling the `error` method defined in `scala.compiletime`. + +**Note:** At the moment our error diagnostics for meta programming does not support yet interpolated string arguments for the `scala.compiletime.error` method that is called in the second case above. As an alternative, one can simply leave off the second case, then a missing typeclass would result in a "failure to reduce match" error. + +**Example:** Here is a slightly polished and compacted version of the code that's generated by inline expansion for the derived `Eq` instance of class `Tree`. + +```scala +impl Eq_Tree_impl[T] with (elemEq: Eq[T]) of Eq[Tree[T]] { + def eql(x: Tree[T], y: Tree[T]): Boolean = { + val ev = implOf[Shaped[Tree[T], Tree.Shape[T]]] + val mx = ev.reflect(x) + val my = ev.reflect(y) + mx.ordinal == my.ordinal && { + if (mx.ordinal == 0) { + derived$Eq.eql(mx(0).asInstanceOf[Tree[T]], my(0).asInstanceOf[Tree[T]]) && + derived$Eq.eql(mx(1).asInstanceOf[Tree[T]], my(1).asInstanceOf[Tree[T]]) + } + else if (mx.ordinal == 1) { + elemEq.eql(mx(0).asInstanceOf[T], my(0).asInstanceOf[T]) + } + else throw new MatchError(mx.ordinal) + } + } +} +``` + +One important difference between this approach and Scala-2 typeclass derivation frameworks such as Shapeless or Magnolia is that no automatic attempt is made to generate typeclass instances of elements recursively using the generic derivation framework. There must be an implicit instance of `Eq[T]` (which can of course be produced in turn using `Eq.derived`), or the compilation will fail. The advantage of this more restrictive approach to typeclass derivation is that it avoids uncontrolled transitive typeclass derivation by design. This keeps code sizes smaller, compile times lower, and is generally more predictable. + +### Derived Instances Elsewhere + +Sometimes one would like to derive a typeclass instance for an ADT after the ADT is defined, without being able to change the code of the ADT itself. +To do this, simply define an instance with the `derived` method of the typeclass as right hand side. E.g, to implement `Ordering` for `Option`, define: +```scala +impl [T: Ordering] of Ordering[Option[T]] = Ordering.derived +``` +Usually, the `Ordering.derived` clause has an implicit parameter of type `Shaped[Option[T], Option.Shape[T]]`. Since the `Option` trait has a `derives` clause, the necessary implicit instance is already present in the companion object of `Option`. If the ADT in question does not have a `derives` clause, an implicit `Shaped` instance would still be synthesized by the compiler at the point where `derived` is called. This is similar to the situation with type tags or class tags: If no implicit instance is found, the compiler will synthesize one. + +### Syntax + +``` +Template ::= InheritClauses [TemplateBody] +EnumDef ::= id ClassConstr InheritClauses EnumBody +InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] +ConstrApps ::= ConstrApp {‘with’ ConstrApp} + | ConstrApp {‘,’ ConstrApp} +``` + +### Discussion + +The typeclass derivation framework is quite small and low-level. There are essentially two pieces of infrastructure that are generated by the compiler + + - The `Shape` type representing the shape of an ADT + - A way to map between ADT instances and generic mirrors + +Generic mirrors make use of the already existing `Product` infrastructure for case classes, which means they are efficient and their generation requires not much code. + +Generic mirrors can be so simple because, just like `Product`s, they are weakly typed. On the other hand, this means that code for generic typeclasses has to ensure that type exploration and value selection proceed in lockstep and it has to assert this conformance in some places using casts. If generic typeclasses are correctly written these casts will never fail. + +It could make sense to explore a higher-level framework that encapsulates all casts in the framework. This could give more guidance to the typeclass implementer. It also seems quite possible to put such a framework on top of the lower-level mechanisms presented here. \ No newline at end of file diff --git a/docs/sidebar.yml b/docs/sidebar.yml index afb476df13d7..92e368b80b0c 100644 --- a/docs/sidebar.yml +++ b/docs/sidebar.yml @@ -43,6 +43,8 @@ sidebar: url: docs/reference/enums/desugarEnums.html - title: Other New Features subsection: + - title: Typeclass Derivation + url: docs/reference/derivation.html - title: Multiversal Equality url: docs/reference/other-new-features/multiversal-equality.html - title: Trait Parameters From 944eca0f3ff0f267f7497ecb03a302198cc72b61 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Dec 2018 18:13:31 +0100 Subject: [PATCH 24/56] blacklist new test for pickling --- compiler/test/dotc/run-test-pickling.blacklist | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index fcda63b4dd26..699b810f50ac 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -6,5 +6,6 @@ t7374 tuples1.scala tuples1a.scala typeclass-derivation2.scala +typeclass-derivation2a.scala typeclass-derivation3.scala deriving-interesting-prefixes.scala From 98fcbc17db3a2b3498967f3ea7a56b81bb64aebb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Dec 2018 18:46:56 +0100 Subject: [PATCH 25/56] Shaped -> Generic Go to a system where Generic contains the Shape as a member instead of having it as a parameter. This makes Generic searchable with an implicit without having to know its shape. --- tests/run/typeclass-derivation2a.scala | 87 +++++++++++++------------- 1 file changed, 42 insertions(+), 45 deletions(-) diff --git a/tests/run/typeclass-derivation2a.scala b/tests/run/typeclass-derivation2a.scala index 1d58cdba6e20..21fe7826da21 100644 --- a/tests/run/typeclass-derivation2a.scala +++ b/tests/run/typeclass-derivation2a.scala @@ -80,21 +80,6 @@ object TypeLevel { def elementLabel(n: Int) = reflected.label(ordinal)(n + 1) } - /** A class for mapping between an ADT value and - * the case mirror that represents the value. - */ - abstract class Reflected[T] { - - /** The case mirror corresponding to ADT instance `x` */ - def reflect(x: T): Mirror - - /** The ADT instance corresponding to given `mirror` */ - def reify(mirror: Mirror): T - - /** The companion object of the ADT */ - def common: ReflectedClass - } - /** The shape of an ADT. * This is eithe a product (`Case`) or a sum (`Cases`) of products. */ @@ -110,7 +95,20 @@ object TypeLevel { /** Every generic derivation starts with a typeclass instance of this type. * It informs that type `T` has shape `S` and also implements runtime reflection on `T`. */ - abstract class Shaped[T, S <: Shape] extends Reflected[T] + abstract class Generic[T] { + + /** The shape of the `T` */ + type Shape <: TypeLevel.Shape + + /** The case mirror corresponding to ADT instance `x` */ + def reflect(x: T): Mirror + + /** The ADT instance corresponding to given `mirror` */ + def reify(mirror: Mirror): T + + /** The companion object of the ADT */ + def common: ReflectedClass + } } // An algebraic datatype @@ -123,17 +121,14 @@ object Lst { // common compiler-generated infrastructure import TypeLevel._ - type Shape[T] = Shape.Cases[( - Shape.Case[Cons[T], (T, Lst[T])], - Shape.Case[Nil.type, Unit] - )] - val reflectedClass = new ReflectedClass("Cons\000hd\000tl\001Nil") import reflectedClass.mirror - val NilMirror = mirror(1) - - implicit def derived$Shaped[T]: Shaped[Lst[T], Shape[T]] = new { + class GenericLst[T] extends Generic[Lst[T]] { + type Shape = Shape.Cases[( + Shape.Case[Cons[T], (T, Lst[T])], + Shape.Case[Nil.type, Unit] + )] def reflect(xs: Lst[T]): Mirror = xs match { case xs: Cons[T] => mirror(0, xs) case Nil => mirror(1) @@ -144,6 +139,7 @@ object Lst { } def common = reflectedClass } + implicit def GenericLst[T]: GenericLst[T] = new GenericLst[T] // three clauses that could be generated from a `derives` clause implicit def derived$Eq[T: Eq]: Eq[Lst[T]] = Eq.derived @@ -158,18 +154,19 @@ object Pair { // common compiler-generated infrastructure import TypeLevel._ - type Shape[T] = Shape.Case[Pair[T], (T, T)] - val reflectedClass = new ReflectedClass("Pair\000x\000y") import reflectedClass.mirror - implicit def derived$Shape[T]: Shaped[Pair[T], Shape[T]] = new { + class GenericPair[T] extends Generic[Pair[T]] { + type Shape = Shape.Case[Pair[T], (T, T)] + def reflect(xy: Pair[T]) = mirror(0, xy) def reify(c: Mirror): Pair[T] = Pair(c(0).asInstanceOf, c(1).asInstanceOf) def common = reflectedClass } + implicit def GenericPair[T]: GenericPair[T] = new GenericPair[T] // clauses that could be generated from a `derives` clause implicit def derived$Eq[T: Eq]: Eq[Pair[T]] = Eq.derived @@ -184,15 +181,14 @@ case class Right[R](x: R) extends Either[Nothing, R] object Either { import TypeLevel._ - type Shape[L, R] = Shape.Cases[( - Shape.Case[Left[L], L *: Unit], - Shape.Case[Right[R], R *: Unit] - )] - val reflectedClass = new ReflectedClass("Left\000x\001Right\000x") import reflectedClass.mirror - implicit def derived$Shape[L, R]: Shaped[Either[L, R], Shape[L, R]] = new { + class GenericEither[L, R] extends Generic[Either[L, R]] { + type Shape = Shape.Cases[( + Shape.Case[Left[L], L *: Unit], + Shape.Case[Right[R], R *: Unit] + )] def reflect(e: Either[L, R]): Mirror = e match { case e: Left[L] => mirror(0, e) case e: Right[R] => mirror(1, e) @@ -203,6 +199,7 @@ object Either { } def common = reflectedClass } + implicit def GenericEither[L, R]: GenericEither[L, R] = new GenericEither[L, R] implicit def derived$Eq[L: Eq, R: Eq]: Eq[Either[L, R]] = Eq.derived implicit def derived$Pickler[L: Pickler, R: Pickler]: Pickler[Either[L, R]] = Pickler.derived @@ -240,11 +237,11 @@ object Eq { false } - inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Eq[T] = new { + inline def derived[T, S <: Shape](implicit ev: Generic[T]): Eq[T] = new { def eql(x: T, y: T): Boolean = { val xm = ev.reflect(x) val ym = ev.reflect(y) - inline erasedValue[S] match { + inline erasedValue[ev.Shape] match { case _: Shape.Cases[alts] => xm.ordinal == ym.ordinal && eqlCases[alts](xm, ym, 0) @@ -303,18 +300,18 @@ object Pickler { case _: Unit => } - inline def unpickleCase[T, Elems <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { + inline def unpickleCase[T, Elems <: Tuple](gen: Generic[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { inline val size = constValue[Tuple.Size[Elems]] inline if (size == 0) - r.reify(r.common.mirror(ordinal)) + gen.reify(r.common.mirror(ordinal)) else { val elems = new Array[Object](size) unpickleElems[Elems](buf, elems, 0) - r.reify(r.common.mirror(ordinal, elems)) + gen.reify(r.common.mirror(ordinal, elems)) } } - inline def unpickleCases[T, Alts <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = + inline def unpickleCases[T, Alts <: Tuple](r: Generic[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = inline erasedValue[Alts] match { case _: (Shape.Case[_, elems] *: alts1) => if (n == ordinal) unpickleCase[T, elems](r, buf, ordinal) @@ -323,10 +320,10 @@ object Pickler { throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ordinal") } - inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Pickler[T] = new { + inline def derived[T, S <: Shape](implicit ev: Generic[T]): Pickler[T] = new { def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = { val xm = ev.reflect(x) - inline erasedValue[S] match { + inline erasedValue[ev.Shape] match { case _: Shape.Cases[alts] => buf += xm.ordinal pickleCases[alts](buf, xm, 0) @@ -334,7 +331,7 @@ object Pickler { pickleElems[elems](buf, xm, 0) } } - def unpickle(buf: mutable.ListBuffer[Int]): T = inline erasedValue[S] match { + def unpickle(buf: mutable.ListBuffer[Int]): T = inline erasedValue[ev.Shape] match { case _: Shape.Cases[alts] => unpickleCases[T, alts](ev, buf, nextInt(buf), 0) case _: Shape.Case[_, elems] => @@ -379,10 +376,10 @@ object Show { throw new MatchError(xm) } - inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Show[T] = new { + inline def derived[T, S <: Shape](implicit ev: Generic[T]): Show[T] = new { def show(x: T): String = { val xm = ev.reflect(x) - val args = inline erasedValue[S] match { + val args = inline erasedValue[ev.Shape] match { case _: Shape.Cases[alts] => showCases[alts](xm, 0) case _: Shape.Case[_, elems] => @@ -473,4 +470,4 @@ object Test extends App { val zs1 = copy(zs) showPrintln(zs1) assert(eql(zs, zs1)) -} \ No newline at end of file +} From 0e8e7ce9832cadfced3b51f461b937aab3e40449 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Dec 2018 12:09:33 +0100 Subject: [PATCH 26/56] Change Shaped -> Generic Several changes - Change scheme from two-parameter Shaped to one-parameter Generic - Move derivation classes in scala.reflect into src-bootstrapped. Necessary since `Generic` now refers to compiletime.Shape and compiletime is a package that exists only in src-bootstrapped --- .../dotty/tools/dotc/core/Definitions.scala | 18 +-- .../src/dotty/tools/dotc/core/StdNames.scala | 2 +- .../src/dotty/tools/dotc/typer/Deriving.scala | 152 ++++++++---------- docs/docs/reference/derivation.md | 127 ++++++++------- .../scala/compiletime/Shaped.scala | 6 - .../scala/reflect/Generic.scala} | 6 +- .../scala/reflect/GenericClass.scala} | 23 +-- .../scala/reflect/Mirror.scala | 6 +- tests/neg/typeclass-derivation2.scala | 28 ++-- tests/pos-special/typeclass-scaling.scala | 105 ++++++------ tests/run/typeclass-derivation2.scala | 2 + tests/run/typeclass-derivation2a.scala | 115 ++++++------- tests/run/typeclass-derivation3.scala | 43 ++--- 13 files changed, 316 insertions(+), 317 deletions(-) delete mode 100644 library/src-bootstrapped/scala/compiletime/Shaped.scala rename library/{src/scala/reflect/Reflected.scala => src-bootstrapped/scala/reflect/Generic.scala} (78%) rename library/{src/scala/reflect/ReflectedClass.scala => src-bootstrapped/scala/reflect/GenericClass.scala} (75%) rename library/{src => src-bootstrapped}/scala/reflect/Mirror.scala (72%) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index 02418a33cf6a..d20f3151a9aa 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -649,16 +649,16 @@ class Definitions { lazy val Product_productPrefixR: TermRef = ProductClass.requiredMethodRef(nme.productPrefix) def Product_productPrefix(implicit ctx: Context): Symbol = Product_productPrefixR.symbol - lazy val ShapedType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shaped") - def ShapedClass(implicit ctx: Context): ClassSymbol = ShapedType.symbol.asClass - lazy val ShapeType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape") - def ShapeClass(implicit ctx: Context): ClassSymbol = ShapeType.symbol.asClass - lazy val ShapeCaseType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape.Case") - def ShapeCaseClass(implicit ctx: Context): ClassSymbol = ShapeCaseType.symbol.asClass - lazy val ShapeCasesType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape.Cases") + lazy val GenericType: TypeRef = ctx.requiredClassRef("scala.reflect.Generic") + def GenericClass(implicit ctx: Context): ClassSymbol = GenericType.symbol.asClass + lazy val ShapeType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape") + def ShapeClass(implicit ctx: Context): ClassSymbol = ShapeType.symbol.asClass + lazy val ShapeCaseType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape.Case") + def ShapeCaseClass(implicit ctx: Context): ClassSymbol = ShapeCaseType.symbol.asClass + lazy val ShapeCasesType: TypeRef = ctx.requiredClassRef("scala.compiletime.Shape.Cases") def ShapeCasesClass(implicit ctx: Context): ClassSymbol = ShapeCasesType.symbol.asClass - lazy val MirrorType: TypeRef = ctx.requiredClassRef("scala.reflect.Mirror") - lazy val ReflectedClassType: TypeRef = ctx.requiredClassRef("scala.reflect.ReflectedClass") + lazy val MirrorType: TypeRef = ctx.requiredClassRef("scala.reflect.Mirror") + lazy val GenericClassType: TypeRef = ctx.requiredClassRef("scala.reflect.GenericClass") lazy val LanguageModuleRef: TermSymbol = ctx.requiredModule("scala.language") def LanguageModuleClass(implicit ctx: Context): ClassSymbol = LanguageModuleRef.moduleClass.asClass diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index a2888a43ddf5..b0f3db0429aa 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -428,6 +428,7 @@ object StdNames { val flatMap: N = "flatMap" val foreach: N = "foreach" val genericArrayOps: N = "genericArrayOps" + val genericClass: N = "genericClass" val get: N = "get" val getClass_ : N = "getClass" val getOrElse: N = "getOrElse" @@ -495,7 +496,6 @@ object StdNames { val raw_ : N = "raw" val readResolve: N = "readResolve" val reflect: N = "reflect" - val reflectedClass: N = "reflectedClass" val reflectiveSelectable: N = "reflectiveSelectable" val reify : N = "reify" val rootMirror : N = "rootMirror" diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index b59a6191b1d0..c64a41746d19 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -83,32 +83,6 @@ trait Deriving { this: Typer => else if (cls.is(Sealed)) sealedShape else NoType - /** A completer for the synthesized `Shape` type. */ - class ShapeCompleter extends TypeParamsCompleter { - - override def completerTypeParams(sym: Symbol)(implicit ctx: Context) = cls.typeParams - - def completeInCreationContext(denot: SymDenotation) = { - val shape0 = shapeWithClassParams - val tparams = cls.typeParams - val abstractedShape = - if (!shape0.exists) { - ctx.error(em"Cannot derive for $cls; it is neither sealed nor a case class or object", templateStartPos) - UnspecifiedErrorType - } - else if (tparams.isEmpty) - shape0 - else - HKTypeLambda(tparams.map(_.name.withVariance(0)))( - tl => tparams.map(tparam => tl.integrate(tparams, tparam.info).bounds), - tl => tl.integrate(tparams, shape0)) - denot.info = TypeAlias(abstractedShape) - } - - def complete(denot: SymDenotation)(implicit ctx: Context) = - completeInCreationContext(denot) - } - private def add(sym: Symbol): sym.type = { ctx.enter(sym) synthetics += sym @@ -116,7 +90,9 @@ trait Deriving { this: Typer => } /** Create a synthetic symbol owned by current owner */ - private def newSymbol(name: Name, info: Type, pos: Position, flags: FlagSet = EmptyFlags)(implicit ctx: Context) = + private def newSymbol(name: Name, info: Type, + pos: Position = ctx.owner.pos, + flags: FlagSet = EmptyFlags)(implicit ctx: Context) = ctx.newSymbol(ctx.owner, name, flags | Synthetic, info, coord = pos) /** Create a synthetic method owned by current owner */ @@ -186,28 +162,27 @@ trait Deriving { this: Typer => derived.pos) } - /** Add value corresponding to `val reflectedClass = new ReflectedClass(...)` - * to `synthetics`, unless a definition of `reflectedClass` exists already. + /** Add value corresponding to `val genericClass = new GenericClass(...)` + * to `synthetics`, unless a definition of `genericClass` exists already. */ - private def addReflectedClass(): Unit = - if (!ctx.denotNamed(nme.reflectedClass).exists) { - add(newSymbol(nme.reflectedClass, defn.ReflectedClassType, templateStartPos)) + private def addGenericClass(): Unit = + if (!ctx.denotNamed(nme.genericClass).exists) { + add(newSymbol(nme.genericClass, defn.GenericClassType, templateStartPos)) } - /** Add `type Shape = ... ` type to `synthetics`, unless a definition of type `Shape` exists already */ - private def addShape(): Unit = - if (!ctx.denotNamed(tpnme.Shape).exists) { - val shapeSym = add(newSymbol(tpnme.Shape, new ShapeCompleter, templateStartPos)) - val lazyShapedInfo = new LazyType { - def complete(denot: SymDenotation)(implicit ctx: Context) = { - val tparams = cls.typeParams - val appliedShape = shapeSym.typeRef.appliedTo(tparams.map(_.typeRef)) - val shapedType = defn.ShapedType.appliedTo(cls.appliedRef, appliedShape) - denot.info = PolyType.fromParams(tparams, shapedType).ensureMethodic - } + private def addGeneric(): Unit = { + val genericCompleter = new LazyType { + def complete(denot: SymDenotation)(implicit ctx: Context) = { + val resultType = + RefinedType( + defn.GenericType.appliedTo(cls.appliedRef), + tpnme.Shape, + TypeAlias(shapeWithClassParams)) + denot.info = PolyType.fromParams(cls.typeParams, resultType).ensureMethodic } - addDerivedInstance(defn.ShapedType.name, lazyShapedInfo, templateStartPos, reportErrors = false) } + addDerivedInstance(defn.GenericType.name, genericCompleter, templateStartPos, reportErrors = false) + } /** Create symbols for derived instances and infrastructure, * append them to `synthetics` buffer, @@ -215,8 +190,8 @@ trait Deriving { this: Typer => */ def enterDerived(derived: List[untpd.Tree]) = { derived.foreach(processDerivedInstance(_)) - addShape() - addReflectedClass() + addGeneric() + addGenericClass() } private def tupleElems(tp: Type): List[Type] = tp match { @@ -250,12 +225,12 @@ trait Deriving { this: Typer => class Finalizer { import tpd._ - /** The previously synthetsized `reflectedClass` symbol */ - private def reflectedClass = - synthetics.find(sym => !sym.is(Method) && sym.name == nme.reflectedClass).get.asTerm + /** The previously synthetsized `genericClass` symbol */ + private def genericClass = + synthetics.find(sym => !sym.is(Method) && sym.name == nme.genericClass).get.asTerm - /** The string to pass to `ReflectedClass` for initializing case and element labels. - * See documentation of `ReflectedClass.label` for what needs to be passed. + /** The string to pass to `GenericClass` for initializing case and element labels. + * See documentation of `GenericClass.label` for what needs to be passed. */ private def labelString(sh: Type): String = sh match { case ShapeCases(cases) => @@ -269,53 +244,62 @@ trait Deriving { this: Typer => (patLabel :: elemLabels).mkString("\u0000") } - /** The RHS of the `reflectedClass` value definition */ - private def reflectedClassRHS = - New(defn.ReflectedClassType, + /** The RHS of the `genericClass` value definition */ + private def genericClassRHS = + New(defn.GenericClassType, List(Literal(Constant(cls.typeRef)), Literal(Constant(labelString(shapeWithClassParams))))) - /** The RHS of the `derived$Shaped` typeclass instance. + /** The RHS of the `derived$Generic` typeclass instance. * Example: For the class definition * * enum Lst[+T] derives ... { case Cons(hd: T, tl: Lst[T]); case Nil } * - * the following typeclass instance is generated: + * the following typeclass instance is generated, where + * = Cases[(Case[Cons[T], (T, Lst[T])], Case[Nil.type, Unit])]: * - * implicit def derived$Shape[T]: Shaped[Lst[T], Shape[T]] = new { - * def reflect(x$0: Lst[T]): Mirror = x$0 match { - * case x$0: Cons[T] => reflectedClass.mirror(0, x$0) - * case x$0: Nil.type => reflectedClass.mirror(1) - * } - * def reify(c: Mirror): Lst[T] = c.ordinal match { - * case 0 => Cons[T](c(0).asInstanceOf[T], c(1).asInstanceOf[Lst[T]]) - * case 1 => Nil + * implicit def derived$Generic[T]: Generic[Lst[T]] { type Shape = } = + * new Generic[Lst[T]] { + * type Shape = + * def reflect(x$0: Lst[T]): Mirror = x$0 match { + * case x$0: Cons[T] => genericClass.mirror(0, x$0) + * case x$0: Nil.type => genericClass.mirror(1) + * } + * def reify(c: Mirror): Lst[T] = c.ordinal match { + * case 0 => Cons[T](c(0).asInstanceOf[T], c(1).asInstanceOf[Lst[T]]) + * case 1 => Nil + * } + * def common = genericClass * } - * def common = reflectedClass - * } */ - private def shapedRHS(shapedType: Type)(implicit ctx: Context) = { - val AppliedType(_, clsArg :: shapeArg :: Nil) = shapedType + private def genericRHS(genericType: Type)(implicit ctx: Context) = { + val RefinedType( + genericInstance @ AppliedType(_, clsArg :: Nil), + tpnme.Shape, + TypeAlias(shapeArg)) = genericType val shape = shapeArg.dealias val implClassSym = ctx.newNormalizedClassSymbol( - ctx.owner, tpnme.ANON_CLASS, EmptyFlags, shapedType :: Nil, coord = templateStartPos) + ctx.owner, tpnme.ANON_CLASS, EmptyFlags, genericInstance :: Nil, coord = templateStartPos) val implClassCtx = ctx.withOwner(implClassSym) val implClassConstr = newMethod(nme.CONSTRUCTOR, MethodType(Nil, implClassSym.typeRef))(implClassCtx).entered def implClassStats(implicit ctx: Context): List[Tree] = { - + val shapeType: TypeDef = { + val shapeAlias = newSymbol(tpnme.Shape, TypeAlias(shape)).entered.asType + TypeDef(shapeAlias) + } val reflectMethod: DefDef = { val meth = newMethod(nme.reflect, MethodType(clsArg :: Nil, defn.MirrorType)).entered def rhs(paramRef: Tree)(implicit ctx: Context): Tree = { def reflectCase(scrut: Tree, idx: Int, elems: List[Type]): Tree = { val ordinal = Literal(Constant(idx)) val args = if (elems.isEmpty) List(ordinal) else List(ordinal, scrut) - val mirror = defn.ReflectedClassType + val mirror = defn.GenericClassType .member(nme.mirror) .suchThat(sym => args.tpes.corresponds(sym.info.firstParamTypes)(_ <:< _)) - ref(reflectedClass).select(mirror.symbol).appliedToArgs(args) + ref(genericClass).select(mirror.symbol).appliedToArgs(args) } shape match { case ShapeCases(cases) => @@ -362,11 +346,11 @@ trait Deriving { this: Typer => } val commonMethod: DefDef = { - val meth = newMethod(nme.common, ExprType(defn.ReflectedClassType)).entered - tpd.DefDef(meth, ref(reflectedClass)) + val meth = newMethod(nme.common, ExprType(defn.GenericClassType)).entered + tpd.DefDef(meth, ref(genericClass)) } - List(reflectMethod, reifyMethod, commonMethod) + List(shapeType, reflectMethod, reifyMethod, commonMethod) } val implClassDef = ClassDef(implClassSym, DefDef(implClassConstr), implClassStats(implClassCtx)) @@ -393,8 +377,8 @@ trait Deriving { this: Typer => } val resultType = instantiated(sym.info) val (typeCls, companionRef) = classAndCompanionRef(resultType) - if (typeCls == defn.ShapedClass) - shapedRHS(resultType) + if (typeCls == defn.GenericClass) + genericRHS(resultType) else { val module = untpd.ref(companionRef).withPos(sym.pos) val rhs = untpd.Select(module, nme.derived) @@ -408,15 +392,17 @@ trait Deriving { this: Typer => else if (sym.is(Method)) tpd.polyDefDef(sym.asTerm, typeclassInstance(sym)(ctx.fresh.setOwner(sym).setNewScope)) else - tpd.ValDef(sym.asTerm, reflectedClassRHS) + tpd.ValDef(sym.asTerm, genericClassRHS) def syntheticDefs: List[Tree] = synthetics.map(syntheticDef).toList } - def finalize(stat: tpd.TypeDef): tpd.Tree = { - val templ @ Template(_, _, _, _) = stat.rhs - tpd.cpy.TypeDef(stat)( - rhs = tpd.cpy.Template(templ)(body = templ.body ++ new Finalizer().syntheticDefs)) - } + def finalize(stat: tpd.TypeDef): tpd.Tree = + if (ctx.reporter.hasErrors) stat + else { + val templ @ Template(_, _, _, _) = stat.rhs + tpd.cpy.TypeDef(stat)( + rhs = tpd.cpy.Template(templ)(body = templ.body ++ new Finalizer().syntheticDefs)) + } } } diff --git a/docs/docs/reference/derivation.md b/docs/docs/reference/derivation.md index fa86a979de5a..d5ec958a9b22 100644 --- a/docs/docs/reference/derivation.md +++ b/docs/docs/reference/derivation.md @@ -48,18 +48,18 @@ A trait can appear in a `derives` clause as long as These two conditions ensure that the synthesized derived instances for the trait are well-formed. The type and implementation of a `derived` method are arbitrary, but typically it has a definition like this: ``` - def derived[T] with (ev: Shaped[T, S]) = ... + def derived[T] with (ev: Generic[T]) = ... ``` -That is, the `derived` method takes an implicit parameter of type `Shaped` that determines the _shape_ `S` of the deriving type `T` and it computes the typeclass implementation according to that shape. Implicit `Shaped` instances are generated automatically for all types that have a `derives` clause. +That is, the `derived` method takes an implicit parameter of type `Generic` that determines the _shape_ of the deriving type `T` and it computes the typeclass implementation according to that shape. Implicit `Generic` instances are generated automatically for all types that have a `derives` clause. This is all a user of typeclass derivation has to know. The rest of this page contains information needed to be able to write a typeclass that can be used in a `derives` clause. In particular, it details the means provided for the implementation of data generic `derived` methods. ### The Shape Type -For every class with a `derives` clause, the compiler generates a type named `Shape` in the companion object of that class. For instance, here is the generated `Shape` type for the `Tree` enum: +For every class with a `derives` clause, the compiler computes the shape of that class as a type. For instance, here is the shape type for the `Tree[T]` enum: ```scala -type Shape[T] = Cases[ +Cases[ Case[Branch[T], (Tree[T], Tree[T])], Case[Leaf[T], T *: Unit] ] @@ -84,29 +84,27 @@ object Shape { } ``` -Here is the `Shape` type for `Labelled`: +Here is the shape type for `Labelled[T]`: ```scala -type Shape[T] = Case[Labelled[T], (T, String)] +Case[Labelled[T], (T, String)] ``` -And here is the one for `Option`: +And here is the one for `Option[T]`: ```scala -type Shape[T] = Cases[ +Cases[ Case[Some[T], T * Unit], - Case [None.type, Unit] + Case[None.type, Unit] ] ``` Note that an empty element tuple is represented as type `Unit`. A single-element tuple is represented as `T *: Unit` since there is no direct syntax for such tuples: `(T)` is just `T` in parentheses, not a tuple. -The `Shape` type generation is suppressed if the companion object already contains a type member named `Shape`. +### The Generic TypeClass -### The Shaped TypeClass - -For every class `C[T_1,...,T_n]` with a `derives` clause, the compiler also generates a type class instance like this: +For every class `C[T_1,...,T_n]` with a `derives` clause, the compiler generates in the companion object of `C` a type class instance like this: ```scala -impl [T_1, ..., T_n] of Shaped[C[T_1,...,T_n], Shape[T_1,...,T_n]] ... +impl [T_1, ..., T_n] of Generic[C[T_1,...,T_n]] { type Shape = ... } = ... ``` -This instance is generated together with the `Shape` type in the companion object of the class. +where the right hand side of `Shape` is the shape type of `C[T_1,...,T_n]`. For instance, the definition ```scala enum Result[+T, +E] derives Logging { @@ -114,28 +112,25 @@ enum Result[+T, +E] derives Logging { case class Err[E](err: E) } ``` -would produce the following members: +would produce: ```scala object Result { import scala.compiletime.Shape._ - type Shape[T, E] = Cases[( - Case[Ok[T], T *: Unit], - Case[Err[E], E *: Unit] - )] - - impl [T, E] of Shaped[Result[T, E], Shape[T, E]] = ... + impl [T, E] of Generic[Result[T, E]] { + type Shape = Cases[( + Case[Ok[T], T *: Unit], + Case[Err[E], E *: Unit] + )] + ... + } } ``` - -The `Shaped` class is defined in package `scala.reflect`. +The `Generic` class is defined in package `scala.reflect`. ```scala -abstract class Shaped[T, S <: Shape] extends Reflected[T] -``` -It is a subclass of class `scala.reflect.Reflected`, which defines two methods that map between a type `T` and a generic representation of `T`, which we call a `Mirror`: -```scala -abstract class Reflected[T] { +abstract class Generic[T] { + type Shape <: scala.compiletime.Shape /** The mirror corresponding to ADT instance `x` */ def reflect(x: T): Mirror @@ -144,43 +139,47 @@ abstract class Reflected[T] { def reify(mirror: Mirror): T /** The companion object of the ADT */ - def common: ReflectedClass + def common: GenericClass } ``` - -The `reflect` method maps an instance value of the ADT `T` to its mirror whereas the `reify` method goes the other way. There's also a `common` method that returns a value of type `ReflectedClass` which contains information that is the same -for all instances of a class (right now, this consists of essentially just the names of the cases and their parameters). +It defines the `Shape` type for the ADT `T`, as well as two methods that map between a +type `T` and a generic representation of `T`, which we call a `Mirror`: +The `reflect` method maps an instance value of the ADT `T` to its mirror whereas +the `reify` method goes the other way. There's also a `common` method that returns +a value of type `GenericClass` which contains information that is the same for all +instances of a class (right now, this consists of the runtime `Class` value and +the names of the cases and their parameters). ### Mirrors A mirror is a generic representation of an instance value of an ADT. `Mirror` objects have three components: - - `reflected: ReflectedClass`: The representation of the ADT class + - `adtClass: GenericClass`: The representation of the ADT class - `ordinal: Int`: The ordinal number of the case among all cases of the ADT, starting from 0 - `elems: Product`: The elements of the instance, represented as a `Product`. The `Mirror` class is defined in package `scala.reflect` as follows: ```scala -class Mirror(val reflected: ReflectedClass, val ordinal: Int, val elems: Product) { +class Mirror(val adtClass: GenericClass, val ordinal: Int, val elems: Product) { /** The `n`'th element of this generic case */ def apply(n: Int): Any = elems.productElement(n) /** The name of the constructor of the case reflected by this mirror */ - def caseLabel: String = reflected.label(ordinal)(0) + def caseLabel: String = adtClass.label(ordinal)(0) /** The label of the `n`'th element of the case reflected by this mirror */ - def elementLabel(n: Int) = reflected.label(ordinal)(n + 1) + def elementLabel(n: Int) = adtClass.label(ordinal)(n + 1) } ``` -### ReflectedClass +### GenericClass -Here's the API of `scala.reflect.ReflectedClass`: +Here's the API of `scala.reflect.GenericClass`: ```scala -class ReflectedClass(val runtimeClass: Class[_], labelsStr: String) { +class GenericClass(val runtimeClass: Class[_], labelsStr: String) { /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ def mirror(ordinal: Int, product: Product): Mirror = @@ -198,7 +197,6 @@ class ReflectedClass(val runtimeClass: Class[_], labelsStr: String) { def mirror(ordinal: Int): Mirror = mirror(ordinal, EmptyProduct) - /** Case and element labels as a two-dimensional array. * Each row of the array contains a case label, followed by the labels of the elements of that case. */ @@ -211,7 +209,7 @@ a mirror over that array, and finally uses the `reify` method in `Reflected` to ### How to Write Generic Typeclasses -Based on the machinery developed so far it becomes possible to define type classes generically. This means that the `derived` method will compute a type class instance for any ADT that has a `Shaped` instance, recursively. +Based on the machinery developed so far it becomes possible to define type classes generically. This means that the `derived` method will compute a type class instance for any ADT that has a `Generic` instance, recursively. The implementation of these methods typically uses three new typelevel constructs in Dotty: inline methods, inline matches and implicit matches. As an example, here is one possible implementation of a generic `Eq` type class, with explanations. Let's assume `Eq` is defined by the following trait: ```scala trait Eq[T] { @@ -219,13 +217,13 @@ trait Eq[T] { } ``` We need to implement a method `Eq.derived` that produces an instance of `Eq[T]` provided -there exists evidence of type `Shaped[T, S]` for some shape `S`. Here's a possible solution: +there exists evidence of type `Generic[T]`. Here's a possible solution: ```scala - inline def derived[T, S <: Shape] with (ev: Shaped[T, S]): Eq[T] = new Eq[T] { + inline def derived[T, S <: Shape] with (ev: Generic[T]): Eq[T] = new Eq[T] { def eql(x: T, y: T): Boolean = { val mx = ev.reflect(x) // (1) val my = ev.reflect(y) // (2) - inline erasedValue[S] match { + inline erasedValue[ev.Shape] match { case _: Cases[alts] => mx.ordinal == my.ordinal && // (3) eqlCases[alts](mx, my, 0) // [4] @@ -238,10 +236,10 @@ there exists evidence of type `Shaped[T, S]` for some shape `S`. Here's a possib The implementation of the inline method `derived` creates an instance of `Eq[T]` and implements its `eql` method. The right hand side of `eql` mixes compile-time and runtime elements. In the code above, runtime elements are marked with a number in parentheses, i.e `(1)`, `(2)`, `(3)`. Compile-time calls that expand to runtime code are marked with a number in brackets, i.e. `[4]`, `[5]`. The implementation of `eql` consists of the following steps. - 1. Map the compared values `x` and `y` to their mirrors using the `reflect` method of the implicitly passed `Shaped` evidence `(1)`, `(2)`. - 2. Match at compile-time against the type `S`. Dotty does not have a construct for matching types directly, buy we can emulate it using an `inline` match over an `erasedValue`. Depending on the actual type `S`, the match will reduce at compile time to one of its two alternatives. - 3. If `S` is of the form `Cases[alts]` for some tuple `alts` of alternative types, the equality test consists of comparing the ordinal values of the two mirrors `(3)` and, if they are equal, comparing the elements of the case indicated by that ordinal value. That second step is performed by code that results from the compile-time expansion of the `eqlCases` call `[4]`. - 4. If `S` is of the form `Case[elems]` for some tuple `elems` for element types, the elements of the case are compared by code that results from the compile-time expansion of the `eqlElems` call `[5]`. + 1. Map the compared values `x` and `y` to their mirrors using the `reflect` method of the implicitly passed `Generic` evidence `(1)`, `(2)`. + 2. Match at compile-time against the shape of the ADT given in `ev.Shape`. Dotty does not have a construct for matching types directly, buy we can emulate it using an `inline` match over an `erasedValue`. Depending on the actual type `ev.Shape`, the match will reduce at compile time to one of its two alternatives. + 3. If `ev.Shape` is of the form `Cases[alts]` for some tuple `alts` of alternative types, the equality test consists of comparing the ordinal values of the two mirrors `(3)` and, if they are equal, comparing the elements of the case indicated by that ordinal value. That second step is performed by code that results from the compile-time expansion of the `eqlCases` call `[4]`. + 4. If `ev.Shape` is of the form `Case[elems]` for some tuple `elems` for element types, the elements of the case are compared by code that results from the compile-time expansion of the `eqlElems` call `[5]`. Here is a possible implementation of `eqlCases`: ```scala @@ -320,7 +318,7 @@ calling the `error` method defined in `scala.compiletime`. ```scala impl Eq_Tree_impl[T] with (elemEq: Eq[T]) of Eq[Tree[T]] { def eql(x: Tree[T], y: Tree[T]): Boolean = { - val ev = implOf[Shaped[Tree[T], Tree.Shape[T]]] + val ev = implOf[Generic[Tree[T]]] val mx = ev.reflect(x) val my = ev.reflect(y) mx.ordinal == my.ordinal && { @@ -346,7 +344,13 @@ To do this, simply define an instance with the `derived` method of the typeclass ```scala impl [T: Ordering] of Ordering[Option[T]] = Ordering.derived ``` -Usually, the `Ordering.derived` clause has an implicit parameter of type `Shaped[Option[T], Option.Shape[T]]`. Since the `Option` trait has a `derives` clause, the necessary implicit instance is already present in the companion object of `Option`. If the ADT in question does not have a `derives` clause, an implicit `Shaped` instance would still be synthesized by the compiler at the point where `derived` is called. This is similar to the situation with type tags or class tags: If no implicit instance is found, the compiler will synthesize one. +Usually, the `Ordering.derived` clause has an implicit parameter of type +`Generic[Option[T]]`. Since the `Option` trait has a `derives` clause, +the necessary implicit instance is already present in the companion object of `Option`. +If the ADT in question does not have a `derives` clause, an implicit `Generic` instance +would still be synthesized by the compiler at the point where `derived` is called. +This is similar to the situation with type tags or class tags: If no implicit instance +is found, the compiler will synthesize one. ### Syntax @@ -360,13 +364,22 @@ ConstrApps ::= ConstrApp {‘with’ ConstrApp} ### Discussion -The typeclass derivation framework is quite small and low-level. There are essentially two pieces of infrastructure that are generated by the compiler +The typeclass derivation framework is quite small and low-level. There are essentially +two pieces of infrastructure in the compiler-generated `Generic` instances: - - The `Shape` type representing the shape of an ADT - - A way to map between ADT instances and generic mirrors + - a type representing the shape of an ADT, + - a way to map between ADT instances and generic mirrors. -Generic mirrors make use of the already existing `Product` infrastructure for case classes, which means they are efficient and their generation requires not much code. +Generic mirrors make use of the already existing `Product` infrastructure for case +classes, which means they are efficient and their generation requires not much code. -Generic mirrors can be so simple because, just like `Product`s, they are weakly typed. On the other hand, this means that code for generic typeclasses has to ensure that type exploration and value selection proceed in lockstep and it has to assert this conformance in some places using casts. If generic typeclasses are correctly written these casts will never fail. +Generic mirrors can be so simple because, just like `Product`s, they are weakly +typed. On the other hand, this means that code for generic typeclasses has to +ensure that type exploration and value selection proceed in lockstep and it +has to assert this conformance in some places using casts. If generic typeclasses +are correctly written these casts will never fail. -It could make sense to explore a higher-level framework that encapsulates all casts in the framework. This could give more guidance to the typeclass implementer. It also seems quite possible to put such a framework on top of the lower-level mechanisms presented here. \ No newline at end of file +It could make sense to explore a higher-level framework that encapsulates all casts +in the framework. This could give more guidance to the typeclass implementer. +It also seems quite possible to put such a framework on top of the lower-level +mechanisms presented here. \ No newline at end of file diff --git a/library/src-bootstrapped/scala/compiletime/Shaped.scala b/library/src-bootstrapped/scala/compiletime/Shaped.scala deleted file mode 100644 index f2c7df2fb07a..000000000000 --- a/library/src-bootstrapped/scala/compiletime/Shaped.scala +++ /dev/null @@ -1,6 +0,0 @@ -package scala.compiletime - -/** Every generic derivation starts with a typeclass instance of this type. - * It informs that type `T` has shape `S` and also implements runtime reflection on `T`. - */ -abstract class Shaped[T, S <: Shape] extends scala.reflect.Reflected[T] diff --git a/library/src/scala/reflect/Reflected.scala b/library/src-bootstrapped/scala/reflect/Generic.scala similarity index 78% rename from library/src/scala/reflect/Reflected.scala rename to library/src-bootstrapped/scala/reflect/Generic.scala index fbeeb554fd64..24be8a686322 100644 --- a/library/src/scala/reflect/Reflected.scala +++ b/library/src-bootstrapped/scala/reflect/Generic.scala @@ -3,7 +3,9 @@ package scala.reflect /** A class for mapping between an ADT value and * the case mirror that represents the value. */ -abstract class Reflected[T] { +abstract class Generic[T] { + + type Shape <: scala.compiletime.Shape /** The case mirror corresponding to ADT instance `x` */ def reflect(x: T): Mirror @@ -12,5 +14,5 @@ abstract class Reflected[T] { def reify(mirror: Mirror): T /** The companion object of the ADT */ - def common: ReflectedClass + def common: GenericClass } diff --git a/library/src/scala/reflect/ReflectedClass.scala b/library/src-bootstrapped/scala/reflect/GenericClass.scala similarity index 75% rename from library/src/scala/reflect/ReflectedClass.scala rename to library/src-bootstrapped/scala/reflect/GenericClass.scala index ebb35a12edf8..2738f3c5c9c9 100644 --- a/library/src/scala/reflect/ReflectedClass.scala +++ b/library/src-bootstrapped/scala/reflect/GenericClass.scala @@ -2,18 +2,19 @@ package scala.reflect import annotation.tailrec import collection.mutable.ArrayBuffer -/** The part of `Reflected` instances that is common for all instances of a class. - * @param labelsStr: A string encoding all case and element labels according to the - * following grammar: +/** The part of `Generic` instances that is common for all instances of a class. + * @param runtimeClass The runtime class instance + * @param labelsStr A string encoding all case and element labels according to the + * following grammar: * - * labelString ::= caseString { caseSeparator caseString } - * caseString ::= elemString { elemSeparator elemString } - * caseSeparator ::= '\u0001' - * elemSeparator ::= '\u0000' - * elemString: "any sequence of characters not containing '\u0000` or `\u0001`" + * labelString ::= caseString { caseSeparator caseString } + * caseString ::= elemString { elemSeparator elemString } + * caseSeparator ::= '\u0001' + * elemSeparator ::= '\u0000' + * elemString: "any sequence of characters not containing '\u0000` or `\u0001`" */ -class ReflectedClass(val runtimeClass: Class[_], labelsStr: String) { - import ReflectedClass._ +class GenericClass(val runtimeClass: Class[_], labelsStr: String) { + import GenericClass._ /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ def mirror(ordinal: Int, product: Product): Mirror = @@ -53,7 +54,7 @@ class ReflectedClass(val runtimeClass: Class[_], labelsStr: String) { } } -object ReflectedClass { +object GenericClass { private final val elemSeparator = '\u0000' private final val caseSeparator = '\u0001' diff --git a/library/src/scala/reflect/Mirror.scala b/library/src-bootstrapped/scala/reflect/Mirror.scala similarity index 72% rename from library/src/scala/reflect/Mirror.scala rename to library/src-bootstrapped/scala/reflect/Mirror.scala index 92aa3175045c..37afde30b118 100644 --- a/library/src/scala/reflect/Mirror.scala +++ b/library/src-bootstrapped/scala/reflect/Mirror.scala @@ -5,14 +5,14 @@ package scala.reflect * @param ordinal The ordinal value of the case in the list of the ADT's cases * @param elems The elements of the case */ -class Mirror(val reflected: ReflectedClass, val ordinal: Int, val elems: Product) { +class Mirror(val adtClass: GenericClass, val ordinal: Int, val elems: Product) { /** The `n`'th element of this generic case */ def apply(n: Int): Any = elems.productElement(n) /** The name of the constructor of the case reflected by this mirror */ - def caseLabel: String = reflected.label(ordinal)(0) + def caseLabel: String = adtClass.label(ordinal)(0) /** The label of the `n`'th element of the case reflected by this mirror */ - def elementLabel(n: Int) = reflected.label(ordinal)(n + 1) + def elementLabel(n: Int) = adtClass.label(ordinal)(n + 1) } diff --git a/tests/neg/typeclass-derivation2.scala b/tests/neg/typeclass-derivation2.scala index 727e2a6b30e4..3cdb001a56b6 100644 --- a/tests/neg/typeclass-derivation2.scala +++ b/tests/neg/typeclass-derivation2.scala @@ -4,8 +4,8 @@ import scala.annotation.tailrec object TypeLevel { /** @param caseLabels The case and element labels of the described ADT as encoded strings. */ - class ReflectedClass(labelsStr: String) { - import ReflectedClass._ + class GenericClass(labelsStr: String) { + import GenericClass._ /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ def mirror(ordinal: Int, product: Product): Mirror = @@ -45,7 +45,7 @@ object TypeLevel { } } - object ReflectedClass { + object GenericClass { /** Helper class to turn arrays into products */ private class ArrayProduct(val elems: Array[AnyRef]) extends Product { def canEqual(that: Any): Boolean = true @@ -68,7 +68,7 @@ object TypeLevel { * @param ordinal The ordinal value of the case in the list of the ADT's cases * @param elems The elements of the case */ - class Mirror(val reflected: ReflectedClass, val ordinal: Int, val elems: Product) { + class Mirror(val reflected: GenericClass, val ordinal: Int, val elems: Product) { /** The `n`'th element of this generic case */ def apply(n: Int): Any = elems.productElement(n) @@ -92,7 +92,7 @@ object TypeLevel { def reify(mirror: Mirror): T /** The companion object of the ADT */ - def common: ReflectedClass + def common: GenericClass } /** The shape of an ADT. @@ -128,8 +128,8 @@ object Lst { Shape.Case[Nil.type, Unit] )] - val reflectedClass = new ReflectedClass("Cons\000hd\000tl\001Nil") - import reflectedClass.mirror + val genericClass = new GenericClass("Cons\000hd\000tl\001Nil") + import genericClass.mirror val NilMirror = mirror(1) @@ -142,7 +142,7 @@ object Lst { case 0 => Cons[T](c(0).asInstanceOf, c(1).asInstanceOf) case 1 => Nil } - def common = reflectedClass + def common = genericClass } // three clauses that could be generated from a `derives` clause @@ -158,15 +158,15 @@ object Pair { type Shape[T] = Shape.Case[Pair[T], (T, T)] - val reflectedClass = new ReflectedClass("Pair\000x\000y") - import reflectedClass.mirror + val genericClass = new GenericClass("Pair\000x\000y") + import genericClass.mirror implicit def pairShape[T]: Shaped[Pair[T], Shape[T]] = new { def reflect(xy: Pair[T]) = mirror(0, xy) def reify(c: Mirror): Pair[T] = Pair(c(0).asInstanceOf, c(1).asInstanceOf) - def common = reflectedClass + def common = genericClass } } @@ -182,8 +182,8 @@ object Either { Shape.Case[Right[R], R *: Unit] )] - val reflectedClass = new ReflectedClass("Left\000x\001Right\000x") - import reflectedClass.mirror + val genericClass = new GenericClass("Left\000x\001Right\000x") + import genericClass.mirror implicit def eitherShape[L, R]: Shaped[Either[L, R], Shape[L, R]] = new { def reflect(e: Either[L, R]): Mirror = e match { @@ -194,7 +194,7 @@ object Either { case 0 => Left[L](c(0).asInstanceOf) case 1 => Right[R](c(0).asInstanceOf) } - def common = reflectedClass + def common = genericClass } implicit def EitherShow[L: Show, R: Show]: Show[Either[L, R]] = Show.derived } diff --git a/tests/pos-special/typeclass-scaling.scala b/tests/pos-special/typeclass-scaling.scala index 453c518e7415..695eb631d350 100644 --- a/tests/pos-special/typeclass-scaling.scala +++ b/tests/pos-special/typeclass-scaling.scala @@ -5,9 +5,9 @@ import scala.annotation.tailrec // // sc typeclass-scaling.scala -Xmax-inlines 100 -Xprint:front -color:never -Yshow-no-inline -pagewidth 1000 >& x // -// produces an output file with `wc` measuures (lines/words/chars): +// produces an output file with `wc` measures (lines/words/chars): // -// 41030 100905 3862435 +// 39161 97323 3124249 // // The command // @@ -15,9 +15,9 @@ import scala.annotation.tailrec // // gives (best of three): // -// real 0m14.322s -// user 0m55.402s -// sys 0m1.110s +// real 0m13.095s +// user 0m50.491s +// sys 0m0.970s object datatypes { import typeclasses._ @@ -215,7 +215,7 @@ object typeclasses { object Eq { import scala.compiletime.erasedValue import compiletime._ - import reflect._ + import reflect.{Mirror, Generic} inline def tryEql[T](x: T, y: T) = implicit match { case eq: Eq[T] => eq.eql(x, y) @@ -230,30 +230,26 @@ object typeclasses { true } - inline def eqlCase[T, Elems <: Tuple](r: Reflected[T], x: T, y: T) = - eqlElems[Elems](r.reflect(x), r.reflect(y), 0) - - inline def eqlCases[T, Alts <: Tuple](r: Reflected[T], x: T, y: T): Boolean = + inline def eqlCases[Alts <: Tuple](xm: Mirror, ym: Mirror, n: Int): Boolean = inline erasedValue[Alts] match { case _: (Shape.Case[alt, elems] *: alts1) => - x match { - case x: `alt` => - y match { - case y: `alt` => eqlCase[T, elems](r, x, y) - case _ => false - } - case _ => eqlCases[T, alts1](r, x, y) - } + if (xm.ordinal == n) eqlElems[elems](xm, ym, 0) + else eqlCases[alts1](xm, ym, n + 1) case _: Unit => - false - } + false + } - inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Eq[T] = new { - def eql(x: T, y: T): Boolean = inline erasedValue[S] match { - case _: Shape.Cases[alts] => - eqlCases[T, alts](ev, x, y) - case _: Shape.Case[_, elems] => - eqlCase[T, elems](ev, x, y) + inline def derived[T](implicit ev: Generic[T]): Eq[T] = new { + def eql(x: T, y: T): Boolean = { + val xm = ev.reflect(x) + val ym = ev.reflect(y) + inline erasedValue[ev.Shape] match { + case _: Shape.Cases[alts] => + xm.ordinal == ym.ordinal && + eqlCases[alts](xm, ym, 0) + case _: Shape.Case[_, elems] => + eqlElems[elems](xm, ym, 0) + } } } @@ -271,7 +267,7 @@ object typeclasses { object Pickler { import scala.compiletime.{erasedValue, constValue} import compiletime._ - import reflect._ + import reflect.{Mirror, Generic} def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) @@ -287,19 +283,11 @@ object typeclasses { case _: Unit => } - inline def pickleCase[T, Elems <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], x: T): Unit = - pickleElems[Elems](buf, r.reflect(x), 0) - - inline def pickleCases[T, Alts <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], x: T, n: Int): Unit = + inline def pickleCases[Alts <: Tuple](buf: mutable.ListBuffer[Int], xm: Mirror, n: Int): Unit = inline erasedValue[Alts] match { case _: (Shape.Case[alt, elems] *: alts1) => - x match { - case x: `alt` => - buf += n - pickleCase[T, elems](r, buf, x) - case _ => - pickleCases[T, alts1](r, buf, x, n + 1) - } + if (xm.ordinal == n) pickleElems[elems](buf, xm, 0) + else pickleCases[alts1](buf, xm, n + 1) case _: Unit => } @@ -315,39 +303,44 @@ object typeclasses { case _: Unit => } - inline def unpickleCase[T, Elems <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { + inline def unpickleCase[T, Elems <: Tuple](gen: Generic[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { inline val size = constValue[Tuple.Size[Elems]] inline if (size == 0) - r.reify(r.common.mirror(ordinal)) + gen.reify(gen.common.mirror(ordinal)) else { val elems = new Array[Object](size) unpickleElems[Elems](buf, elems, 0) - r.reify(r.common.mirror(ordinal, elems)) + gen.reify(gen.common.mirror(ordinal, elems)) } } - inline def unpickleCases[T, Alts <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = + inline def unpickleCases[T, Alts <: Tuple](gen: Generic[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = inline erasedValue[Alts] match { case _: (Shape.Case[_, elems] *: alts1) => - if (n == ordinal) unpickleCase[T, elems](r, buf, ordinal) - else unpickleCases[T, alts1](r, buf, ordinal, n + 1) + if (n == ordinal) unpickleCase[T, elems](gen, buf, ordinal) + else unpickleCases[T, alts1](gen, buf, ordinal, n + 1) case _ => throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ordinal") } - inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Pickler[T] = new { - def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = inline erasedValue[S] match { - case _: Shape.Cases[alts] => - pickleCases[T, alts](ev, buf, x, 0) - case _: Shape.Case[_, elems] => - pickleCase[T, elems](ev, buf, x) - } - def unpickle(buf: mutable.ListBuffer[Int]): T = inline erasedValue[S] match { - case _: Shape.Cases[alts] => - unpickleCases[T, alts](ev, buf, nextInt(buf), 0) - case _: Shape.Case[_, elems] => - unpickleCase[T, elems](ev, buf, 0) + inline def derived[T](implicit ev: Generic[T]): Pickler[T] = new { + def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = { + val xm = ev.reflect(x) + inline erasedValue[ev.Shape] match { + case _: Shape.Cases[alts] => + buf += xm.ordinal + pickleCases[alts](buf, xm, 0) + case _: Shape.Case[_, elems] => + pickleElems[elems](buf, xm, 0) + } } + def unpickle(buf: mutable.ListBuffer[Int]): T = + inline erasedValue[ev.Shape] match { + case _: Shape.Cases[alts] => + unpickleCases[T, alts](ev, buf, nextInt(buf), 0) + case _: Shape.Case[_, elems] => + unpickleCase[T, elems](ev, buf, 0) + } } implicit object IntPickler extends Pickler[Int] { diff --git a/tests/run/typeclass-derivation2.scala b/tests/run/typeclass-derivation2.scala index 43d31db292b5..464d5e5af976 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -1,6 +1,8 @@ import scala.collection.mutable import scala.annotation.tailrec +// A typeclass derivation encoding using Shape/Shaped scheme, now superseded by +// typeclass-derivation2a object TypeLevel { /** @param caseLabels The case and element labels of the described ADT as encoded strings. */ diff --git a/tests/run/typeclass-derivation2a.scala b/tests/run/typeclass-derivation2a.scala index 21fe7826da21..615b064fa84e 100644 --- a/tests/run/typeclass-derivation2a.scala +++ b/tests/run/typeclass-derivation2a.scala @@ -1,11 +1,13 @@ import scala.collection.mutable import scala.annotation.tailrec +// Simulation of typeclass derivation encoding that's currently implemented. +// The real typeclass derivation is tested in typeclass-derivation3.scala. object TypeLevel { /** @param caseLabels The case and element labels of the described ADT as encoded strings. */ - class ReflectedClass(labelsStr: String) { - import ReflectedClass._ + class GenericClass(labelsStr: String) { + import GenericClass._ /** A mirror of case with ordinal number `ordinal` and elements as given by `Product` */ def mirror(ordinal: Int, product: Product): Mirror = @@ -45,7 +47,7 @@ object TypeLevel { } } - object ReflectedClass { + object GenericClass { /** Helper class to turn arrays into products */ private class ArrayProduct(val elems: Array[AnyRef]) extends Product { def canEqual(that: Any): Boolean = true @@ -68,7 +70,7 @@ object TypeLevel { * @param ordinal The ordinal value of the case in the list of the ADT's cases * @param elems The elements of the case */ - class Mirror(val reflected: ReflectedClass, val ordinal: Int, val elems: Product) { + class Mirror(val reflected: GenericClass, val ordinal: Int, val elems: Product) { /** The `n`'th element of this generic case */ def apply(n: Int): Any = elems.productElement(n) @@ -107,7 +109,7 @@ object TypeLevel { def reify(mirror: Mirror): T /** The companion object of the ADT */ - def common: ReflectedClass + def common: GenericClass } } @@ -121,25 +123,27 @@ object Lst { // common compiler-generated infrastructure import TypeLevel._ - val reflectedClass = new ReflectedClass("Cons\000hd\000tl\001Nil") - import reflectedClass.mirror - - class GenericLst[T] extends Generic[Lst[T]] { - type Shape = Shape.Cases[( - Shape.Case[Cons[T], (T, Lst[T])], - Shape.Case[Nil.type, Unit] - )] - def reflect(xs: Lst[T]): Mirror = xs match { - case xs: Cons[T] => mirror(0, xs) - case Nil => mirror(1) - } - def reify(c: Mirror): Lst[T] = c.ordinal match { - case 0 => Cons[T](c(0).asInstanceOf, c(1).asInstanceOf) - case 1 => Nil + val genericClass = new GenericClass("Cons\000hd\000tl\001Nil") + import genericClass.mirror + + private type ShapeOf[T] = Shape.Cases[( + Shape.Case[Cons[T], (T, Lst[T])], + Shape.Case[Nil.type, Unit] + )] + + implicit def GenericLst[T]: Generic[Lst[T]] { type Shape = ShapeOf[T] } = + new Generic[Lst[T]] { + type Shape = ShapeOf[T] + def reflect(xs: Lst[T]): Mirror = xs match { + case xs: Cons[T] => mirror(0, xs) + case Nil => mirror(1) + } + def reify(c: Mirror): Lst[T] = c.ordinal match { + case 0 => Cons[T](c(0).asInstanceOf, c(1).asInstanceOf) + case 1 => Nil + } + def common = genericClass } - def common = reflectedClass - } - implicit def GenericLst[T]: GenericLst[T] = new GenericLst[T] // three clauses that could be generated from a `derives` clause implicit def derived$Eq[T: Eq]: Eq[Lst[T]] = Eq.derived @@ -154,19 +158,20 @@ object Pair { // common compiler-generated infrastructure import TypeLevel._ - val reflectedClass = new ReflectedClass("Pair\000x\000y") - import reflectedClass.mirror + val genericClass = new GenericClass("Pair\000x\000y") + import genericClass.mirror - class GenericPair[T] extends Generic[Pair[T]] { - type Shape = Shape.Case[Pair[T], (T, T)] + private type ShapeOf[T] = Shape.Case[Pair[T], (T, T)] - def reflect(xy: Pair[T]) = - mirror(0, xy) - def reify(c: Mirror): Pair[T] = - Pair(c(0).asInstanceOf, c(1).asInstanceOf) - def common = reflectedClass - } - implicit def GenericPair[T]: GenericPair[T] = new GenericPair[T] + implicit def GenericPair[T]: Generic[Pair[T]] { type Shape = ShapeOf[T] } = + new Generic[Pair[T]] { + type Shape = ShapeOf[T] + def reflect(xy: Pair[T]) = + mirror(0, xy) + def reify(c: Mirror): Pair[T] = + Pair(c(0).asInstanceOf, c(1).asInstanceOf) + def common = genericClass + } // clauses that could be generated from a `derives` clause implicit def derived$Eq[T: Eq]: Eq[Pair[T]] = Eq.derived @@ -181,25 +186,27 @@ case class Right[R](x: R) extends Either[Nothing, R] object Either { import TypeLevel._ - val reflectedClass = new ReflectedClass("Left\000x\001Right\000x") - import reflectedClass.mirror - - class GenericEither[L, R] extends Generic[Either[L, R]] { - type Shape = Shape.Cases[( - Shape.Case[Left[L], L *: Unit], - Shape.Case[Right[R], R *: Unit] - )] - def reflect(e: Either[L, R]): Mirror = e match { - case e: Left[L] => mirror(0, e) - case e: Right[R] => mirror(1, e) - } - def reify(c: Mirror): Either[L, R] = c.ordinal match { - case 0 => Left(c(0).asInstanceOf) - case 1 => Right(c(0).asInstanceOf) + val genericClass = new GenericClass("Left\000x\001Right\000x") + import genericClass.mirror + + private type ShapeOf[L, R] = Shape.Cases[( + Shape.Case[Left[L], L *: Unit], + Shape.Case[Right[R], R *: Unit] + )] + + implicit def GenericEither[L, R]: Generic[Either[L, R]] { type Shape = ShapeOf[L, R] } = + new Generic[Either[L, R]] { + type Shape = ShapeOf[L, R] + def reflect(e: Either[L, R]): Mirror = e match { + case e: Left[L] => mirror(0, e) + case e: Right[R] => mirror(1, e) + } + def reify(c: Mirror): Either[L, R] = c.ordinal match { + case 0 => Left(c(0).asInstanceOf) + case 1 => Right(c(0).asInstanceOf) + } + def common = genericClass } - def common = reflectedClass - } - implicit def GenericEither[L, R]: GenericEither[L, R] = new GenericEither[L, R] implicit def derived$Eq[L: Eq, R: Eq]: Eq[Either[L, R]] = Eq.derived implicit def derived$Pickler[L: Pickler, R: Pickler]: Pickler[Either[L, R]] = Pickler.derived @@ -303,11 +310,11 @@ object Pickler { inline def unpickleCase[T, Elems <: Tuple](gen: Generic[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { inline val size = constValue[Tuple.Size[Elems]] inline if (size == 0) - gen.reify(r.common.mirror(ordinal)) + gen.reify(gen.common.mirror(ordinal)) else { val elems = new Array[Object](size) unpickleElems[Elems](buf, elems, 0) - gen.reify(r.common.mirror(ordinal, elems)) + gen.reify(gen.common.mirror(ordinal, elems)) } } diff --git a/tests/run/typeclass-derivation3.scala b/tests/run/typeclass-derivation3.scala index d852df9a71ca..ab9e6b886923 100644 --- a/tests/run/typeclass-derivation3.scala +++ b/tests/run/typeclass-derivation3.scala @@ -32,7 +32,7 @@ object typeclasses { object Eq { import scala.compiletime.erasedValue import compiletime._ - import reflect.Mirror + import reflect.{Mirror, Generic} inline def tryEql[T](x: T, y: T) = implicit match { case eq: Eq[T] => eq.eql(x, y) @@ -56,11 +56,11 @@ object typeclasses { false } - inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Eq[T] = new { + inline def derived[T](implicit ev: Generic[T]): Eq[T] = new { def eql(x: T, y: T): Boolean = { val xm = ev.reflect(x) val ym = ev.reflect(y) - inline erasedValue[S] match { + inline erasedValue[ev.Shape] match { case _: Shape.Cases[alts] => xm.ordinal == ym.ordinal && eqlCases[alts](xm, ym, 0) @@ -84,7 +84,7 @@ object typeclasses { object Pickler { import scala.compiletime.{erasedValue, constValue} import compiletime._ - import reflect.{Mirror, Reflected} + import reflect.{Mirror, Generic} def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) @@ -120,30 +120,30 @@ object typeclasses { case _: Unit => } - inline def unpickleCase[T, Elems <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { + inline def unpickleCase[T, Elems <: Tuple](gen: Generic[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { inline val size = constValue[Tuple.Size[Elems]] inline if (size == 0) - r.reify(r.common.mirror(ordinal)) + gen.reify(gen.common.mirror(ordinal)) else { val elems = new Array[Object](size) unpickleElems[Elems](buf, elems, 0) - r.reify(r.common.mirror(ordinal, elems)) + gen.reify(gen.common.mirror(ordinal, elems)) } } - inline def unpickleCases[T, Alts <: Tuple](r: Reflected[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = + inline def unpickleCases[T, Alts <: Tuple](gen: Generic[T], buf: mutable.ListBuffer[Int], ordinal: Int, n: Int): T = inline erasedValue[Alts] match { case _: (Shape.Case[_, elems] *: alts1) => - if (n == ordinal) unpickleCase[T, elems](r, buf, ordinal) - else unpickleCases[T, alts1](r, buf, ordinal, n + 1) + if (n == ordinal) unpickleCase[T, elems](gen, buf, ordinal) + else unpickleCases[T, alts1](gen, buf, ordinal, n + 1) case _ => throw new IndexOutOfBoundsException(s"unexpected ordinal number: $ordinal") } - inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Pickler[T] = new { + inline def derived[T](implicit ev: Generic[T]): Pickler[T] = new { def pickle(buf: mutable.ListBuffer[Int], x: T): Unit = { val xm = ev.reflect(x) - inline erasedValue[S] match { + inline erasedValue[ev.Shape] match { case _: Shape.Cases[alts] => buf += xm.ordinal pickleCases[alts](buf, xm, 0) @@ -151,12 +151,13 @@ object typeclasses { pickleElems[elems](buf, xm, 0) } } - def unpickle(buf: mutable.ListBuffer[Int]): T = inline erasedValue[S] match { - case _: Shape.Cases[alts] => - unpickleCases[T, alts](ev, buf, nextInt(buf), 0) - case _: Shape.Case[_, elems] => - unpickleCase[T, elems](ev, buf, 0) - } + def unpickle(buf: mutable.ListBuffer[Int]): T = + inline erasedValue[ev.Shape] match { + case _: Shape.Cases[alts] => + unpickleCases[T, alts](ev, buf, nextInt(buf), 0) + case _: Shape.Case[_, elems] => + unpickleCase[T, elems](ev, buf, 0) + } } implicit object IntPickler extends Pickler[Int] { @@ -172,7 +173,7 @@ object typeclasses { object Show { import scala.compiletime.erasedValue import compiletime._ - import reflect.Mirror + import reflect.{Mirror, Generic} inline def tryShow[T](x: T): String = implicit match { case s: Show[T] => s.show(x) @@ -197,10 +198,10 @@ object typeclasses { throw new MatchError(xm) } - inline def derived[T, S <: Shape](implicit ev: Shaped[T, S]): Show[T] = new { + inline def derived[T](implicit ev: Generic[T]): Show[T] = new { def show(x: T): String = { val xm = ev.reflect(x) - val args = inline erasedValue[S] match { + val args = inline erasedValue[ev.Shape] match { case _: Shape.Cases[alts] => showCases[alts](xm, 0) case _: Shape.Case[_, elems] => From 04c3ae7b2075cd8e376e09f87ebf7e560571e617 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 8 Dec 2018 19:29:14 +0100 Subject: [PATCH 27/56] Fix order of Child annotations. Child annotations are stored in reverse order of textual occurrence. We thus have a stable ordering that we can use for typeclass derivations. --- .../dotty/tools/dotc/transform/SymUtils.scala | 4 +-- .../src/dotty/tools/dotc/typer/Namer.scala | 25 ++++++++++++++++--- .../src/scala/annotation/internal/Child.scala | 9 +++++-- 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index feaa7e5e340a..74e14348c46e 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -133,13 +133,13 @@ class SymUtils(val self: Symbol) extends AnyVal { def isInaccessibleChildOf(cls: Symbol)(implicit ctx: Context): Boolean = self.isLocal && !cls.topLevelClass.isLinkedWith(self.topLevelClass) - /** If this is a sealed class, its known children */ + /** If this is a sealed class, its known children in the order of textual occurrence */ def children(implicit ctx: Context): List[Symbol] = { if (self.isType) self.setFlag(ChildrenQueried) self.annotations.collect { case Annotation.Child(child) => child - } + }.reverse } def hasAnonymousChild(implicit ctx: Context): Boolean = diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 16bc6609f91d..c81be44704b7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -500,12 +500,31 @@ class Namer { typer: Typer => vd.mods.is(JavaEnumValue) // && ownerHasEnumFlag } + /** Add child annotation for `child` to annotations of `cls`. The annotation + * is added at the correct insertion point, so that Child annotations appear + * in reverse order of their start positions. + * @pre `child` must have a position. + */ + final def addChild(cls: Symbol, child: Symbol)(implicit ctx: Context): Unit = { + val childStart = child.pos.start + def insertInto(annots: List[Annotation]): List[Annotation] = + annots.find(_.symbol == defn.ChildAnnot) match { + case Some(Annotation.Child(other)) if childStart <= other.pos.start => + assert(childStart != other.pos.start, "duplicate child annotation $child / $other") + val (prefix, otherAnnot :: rest) = annots.span(_.symbol != defn.ChildAnnot) + prefix ::: otherAnnot :: insertInto(rest) + case _ => + Annotation.Child(child) :: annots + } + cls.annotations = insertInto(cls.annotations) + } + /** Add java enum constants */ def addEnumConstants(mdef: DefTree, sym: Symbol)(implicit ctx: Context): Unit = mdef match { case vdef: ValDef if (isEnumConstant(vdef)) => val enumClass = sym.owner.linkedClass if (!(enumClass is Flags.Sealed)) enumClass.setFlag(Flags.AbstractSealed) - enumClass.addAnnotation(Annotation.Child(sym)) + addChild(enumClass, sym) case _ => } @@ -815,9 +834,9 @@ class Namer { typer: Typer => val cls = parent.classSymbol if (cls.is(Sealed)) { if ((child.isInaccessibleChildOf(cls) || child.isAnonymousClass) && !sym.hasAnonymousChild) - cls.addAnnotation(Annotation.Child(cls)) + addChild(cls, cls) else if (!cls.is(ChildrenQueried)) - cls.addAnnotation(Annotation.Child(child)) + addChild(cls, child) else ctx.error(em"""children of ${cls} were already queried before $sym was discovered. |As a remedy, you could move $sym on the same nesting level as $cls.""", diff --git a/library/src/scala/annotation/internal/Child.scala b/library/src/scala/annotation/internal/Child.scala index 208f471135d5..78a6be5d2b36 100644 --- a/library/src/scala/annotation/internal/Child.scala +++ b/library/src/scala/annotation/internal/Child.scala @@ -10,8 +10,13 @@ import scala.annotation.Annotation * case class C() extends A * * Then the class symbol `A` would carry the annotations - * `@Child[Bref] @Child[Cref]` where `Bref`, `Cref` are TypeRefs - * referring to the class symbols of `B` and `C` + * `@Child[Cref]`, @Child[Bref] where `Bref`, `Cref` are TypeRefs + * referring to the class symbols of `B` and `C`. + * + * Child annotations always appear in reverse order of textual occurrence. + * I.e. in the example above, it is guaranteed that the child annotation for `C` + * appears before the one for `B`. + * * TODO: This should be `Child[T <: AnyKind]` */ class Child[T] extends Annotation From d81ce917229bec5870505879bc14a80f5354c1d2 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 9 Dec 2018 11:21:01 +0100 Subject: [PATCH 28/56] Synthesize Generic instances on the fly Allow to synthesize Generic instances on the fly for ADTs that do not have a derives clause. --- .../src/dotty/tools/dotc/typer/Deriving.scala | 54 +++++++++++++------ .../dotty/tools/dotc/typer/Implicits.scala | 22 ++++++-- tests/run/typeclass-derivation3.check | 4 ++ tests/run/typeclass-derivation3.scala | 19 +++++++ 4 files changed, 79 insertions(+), 20 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index c64a41746d19..0c85348fb814 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -15,23 +15,24 @@ import config.Printers.typr import Inferencing._ import transform.TypeUtils._ import transform.SymUtils._ +import ErrorReporting.errorTree /** A typer mixin that implements typeclass derivation functionality */ trait Deriving { this: Typer => /** A helper class to derive type class instances for one class or object - * @param cls The class symbol of the class or object with a `derives` clause - * @param templateStartPos The default position that should be given to generic - * synthesized infrastructure code that is not connected with a - * `derives` instance. + * @param cls The class symbol of the class or object with a `derives` clause + * @param codePos The default position that should be given to generic + * synthesized infrastructure code that is not connected with a + * `derives` instance. */ - class Deriver(cls: ClassSymbol, templateStartPos: Position)(implicit ctx: Context) { + class Deriver(cls: ClassSymbol, codePos: Position)(implicit ctx: Context) { /** A buffer for synthesized symbols */ private var synthetics = new mutable.ListBuffer[Symbol] /** the children of `cls` ordered by textual occurrence */ - lazy val children = cls.children.sortBy(_.pos.start) + lazy val children = cls.children /** The shape (of type Shape.Case) of a case given by `sym`. `sym` is either `cls` * itself, or a subclass of `cls`, or an instance of `cls`. @@ -40,7 +41,9 @@ trait Deriving { this: Typer => val (constr, elems) = sym match { case caseClass: ClassSymbol => - caseClass.primaryConstructor.info match { + if (caseClass.is(Module)) + (caseClass.sourceModule.termRef, Nil) + else caseClass.primaryConstructor.info match { case info: PolyType => def instantiate(implicit ctx: Context) = { val poly = constrained(info, untpd.EmptyTree)._1 @@ -83,6 +86,13 @@ trait Deriving { this: Typer => else if (cls.is(Sealed)) sealedShape else NoType + private def shapeOfType(tp: Type) = { + val shape0 = shapeWithClassParams + val clsType = tp.baseType(cls) + if (clsType.exists) shape0.subst(cls.typeParams, clsType.argInfos) + else clsType + } + private def add(sym: Symbol): sym.type = { ctx.enter(sym) synthetics += sym @@ -167,7 +177,7 @@ trait Deriving { this: Typer => */ private def addGenericClass(): Unit = if (!ctx.denotNamed(nme.genericClass).exists) { - add(newSymbol(nme.genericClass, defn.GenericClassType, templateStartPos)) + add(newSymbol(nme.genericClass, defn.GenericClassType, codePos)) } private def addGeneric(): Unit = { @@ -181,7 +191,7 @@ trait Deriving { this: Typer => denot.info = PolyType.fromParams(cls.typeParams, resultType).ensureMethodic } } - addDerivedInstance(defn.GenericType.name, genericCompleter, templateStartPos, reportErrors = false) + addDerivedInstance(defn.GenericType.name, genericCompleter, codePos, reportErrors = false) } /** Create symbols for derived instances and infrastructure, @@ -240,12 +250,12 @@ trait Deriving { this: Typer => case ShapeCase(pat, elems) => val patCls = pat.widen.classSymbol val patLabel = patCls.name.stripModuleClassSuffix.toString - val elemLabels = patCls.caseAccessors.map(_.name.toString) + val elemLabels = patCls.caseAccessors.filterNot(_.is(PrivateLocal)).map(_.name.toString) (patLabel :: elemLabels).mkString("\u0000") } /** The RHS of the `genericClass` value definition */ - private def genericClassRHS = + def genericClassRHS = New(defn.GenericClassType, List(Literal(Constant(cls.typeRef)), Literal(Constant(labelString(shapeWithClassParams))))) @@ -272,7 +282,7 @@ trait Deriving { this: Typer => * def common = genericClass * } */ - private def genericRHS(genericType: Type)(implicit ctx: Context) = { + def genericRHS(genericType: Type, genericClassRef: Tree)(implicit ctx: Context) = { val RefinedType( genericInstance @ AppliedType(_, clsArg :: Nil), tpnme.Shape, @@ -280,7 +290,7 @@ trait Deriving { this: Typer => val shape = shapeArg.dealias val implClassSym = ctx.newNormalizedClassSymbol( - ctx.owner, tpnme.ANON_CLASS, EmptyFlags, genericInstance :: Nil, coord = templateStartPos) + ctx.owner, tpnme.ANON_CLASS, EmptyFlags, genericInstance :: Nil, coord = codePos) val implClassCtx = ctx.withOwner(implClassSym) val implClassConstr = newMethod(nme.CONSTRUCTOR, MethodType(Nil, implClassSym.typeRef))(implClassCtx).entered @@ -299,7 +309,7 @@ trait Deriving { this: Typer => val mirror = defn.GenericClassType .member(nme.mirror) .suchThat(sym => args.tpes.corresponds(sym.info.firstParamTypes)(_ <:< _)) - ref(genericClass).select(mirror.symbol).appliedToArgs(args) + genericClassRef.select(mirror.symbol).appliedToArgs(args) } shape match { case ShapeCases(cases) => @@ -347,7 +357,7 @@ trait Deriving { this: Typer => val commonMethod: DefDef = { val meth = newMethod(nme.common, ExprType(defn.GenericClassType)).entered - tpd.DefDef(meth, ref(genericClass)) + tpd.DefDef(meth, genericClassRef) } List(shapeType, reflectMethod, reifyMethod, commonMethod) @@ -378,7 +388,7 @@ trait Deriving { this: Typer => val resultType = instantiated(sym.info) val (typeCls, companionRef) = classAndCompanionRef(resultType) if (typeCls == defn.GenericClass) - genericRHS(resultType) + genericRHS(resultType, ref(genericClass)) else { val module = untpd.ref(companionRef).withPos(sym.pos) val rhs = untpd.Select(module, nme.derived) @@ -404,5 +414,17 @@ trait Deriving { this: Typer => tpd.cpy.TypeDef(stat)( rhs = tpd.cpy.Template(templ)(body = templ.body ++ new Finalizer().syntheticDefs)) } + + /** Synthesized instance for `Generic[]` */ + def genericInstance(clsType: Type): tpd.Tree = { + val shape = shapeOfType(clsType) + if (shape.exists) { + val genericType = RefinedType(defn.GenericType.appliedTo(clsType), tpnme.Shape, TypeAlias(shape)) + val finalizer = new Finalizer + finalizer.genericRHS(genericType, finalizer.genericClassRHS) + } + else errorTree(tpd.EmptyTree.withPos(codePos), + i"cannot take shape of type $clsType", codePos) + } } } diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index f9463690872e..f5718cf9d72c 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -739,7 +739,20 @@ trait Implicits { self: Typer => assumedCanEqual(tp1, tp2) || !hasEq(tp1) && !hasEq(tp2) } - inferImplicit(formal, EmptyTree, span)(ctx) match { + /** If `formal` is of the form `scala.reflect.Generic[T]` for some class type `T`, + * synthesize an instance for it. + */ + def synthesizedGeneric(formal: Type): Tree = + formal.argTypes match { + case arg :: Nil => + val arg1 = fullyDefinedType(arg, "ClassTag argument", pos) + val clsType = checkClassType(arg1, pos, traitReq = false, stablePrefixReq = true) + new Deriver(clsType.classSymbol.asClass, pos).genericInstance(clsType) + case _ => + EmptyTree + } + + inferImplicit(formal, EmptyTree, pos)(ctx) match { case SearchSuccess(arg, _, _) => arg case fail @ SearchFailure(failed) => def trySpecialCase(cls: ClassSymbol, handler: Type => Tree, ifNot: => Tree) = { @@ -754,9 +767,10 @@ trait Implicits { self: Typer => else trySpecialCase(defn.ClassTagClass, synthesizedClassTag, trySpecialCase(defn.QuotedTypeClass, synthesizedTypeTag, - trySpecialCase(defn.TastyReflectionClass, synthesizedTastyContext, - trySpecialCase(defn.EqClass, synthesizedEq, - trySpecialCase(defn.ValueOfClass, synthesizedValueOf, failed))))) + trySpecialCase(defn.GenericClass, synthesizedGeneric, + trySpecialCase(defn.TastyReflectionClass, synthesizedTastyContext, + trySpecialCase(defn.EqClass, synthesizedEq, + trySpecialCase(defn.ValueOfClass, synthesizedValueOf, failed)))))) } } diff --git a/tests/run/typeclass-derivation3.check b/tests/run/typeclass-derivation3.check index fd3681599ffc..688c68e8ee41 100644 --- a/tests/run/typeclass-derivation3.check +++ b/tests/run/typeclass-derivation3.check @@ -8,3 +8,7 @@ Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil()))) Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Cons(hd = 33, tl = Nil()))), tl = Cons(hd = Cons(hd = 11, tl = Cons(hd = 22, tl = Nil())), tl = Nil())) Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil())) Cons(hd = Left(x = 1), tl = Cons(hd = Right(x = Pair(x = 2, y = 3)), tl = Nil())) +true +::(head = 1, tl$access$1 = ::(head = 2, tl$access$1 = ::(head = 3, tl$access$1 = Nil()))) +::(head = ::(head = 1, tl$access$1 = Nil()), tl$access$1 = ::(head = ::(head = 2, tl$access$1 = ::(head = 3, tl$access$1 = Nil())), tl$access$1 = Nil())) +::(head = Nil(), tl$access$1 = ::(head = ::(head = 1, tl$access$1 = Nil()), tl$access$1 = ::(head = ::(head = 2, tl$access$1 = ::(head = 3, tl$access$1 = Nil())), tl$access$1 = Nil()))) diff --git a/tests/run/typeclass-derivation3.scala b/tests/run/typeclass-derivation3.scala index ab9e6b886923..6d7bc6a0bb2c 100644 --- a/tests/run/typeclass-derivation3.scala +++ b/tests/run/typeclass-derivation3.scala @@ -294,4 +294,23 @@ object Test extends App { val zs1 = copy(zs) showPrintln(zs1) assert(eql(zs, zs1)) + + import scala.reflect.Generic + + val listGen = implicitly[Generic[scala.collection.immutable.List[Int]]] + implicit def listEq[T: Eq]: Eq[List[T]] = Eq.derived + val leq = implicitly[Eq[List[Int]]] + println(leq.eql(List(1, 2, 3), List(1, 2, 3))) + + implicit def listShow[T: Show]: Show[List[T]] = Show.derived + println(implicitly[Show[List[Int]]].show(List(1, 2, 3))) + println(implicitly[Show[List[List[Int]]]].show(List(List(1), List(2, 3)))) + + implicit def listPickler[T: Pickler]: Pickler[List[T]] = Pickler.derived + val pklList = implicitly[Pickler[List[List[Int]]]] + val zss = List(Nil, List(1), List(2, 3)) + pklList.pickle(buf, zss) + val zss1 = pklList.unpickle(buf) + assert(eql(zss, zss1)) + showPrintln(zss1) } \ No newline at end of file From f1467f31063e8d6233056087722ac21fa15a0eab Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 9 Dec 2018 12:07:41 +0100 Subject: [PATCH 29/56] Handle duplicate children --- compiler/src/dotty/tools/dotc/typer/Namer.scala | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index c81be44704b7..96466be3ed05 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -510,9 +510,13 @@ class Namer { typer: Typer => def insertInto(annots: List[Annotation]): List[Annotation] = annots.find(_.symbol == defn.ChildAnnot) match { case Some(Annotation.Child(other)) if childStart <= other.pos.start => - assert(childStart != other.pos.start, "duplicate child annotation $child / $other") - val (prefix, otherAnnot :: rest) = annots.span(_.symbol != defn.ChildAnnot) - prefix ::: otherAnnot :: insertInto(rest) + if (child == other) + annots // can happen if a class has several inaccessible children + else { + assert(childStart != other.pos.start, i"duplicate child annotation $child / $other") + val (prefix, otherAnnot :: rest) = annots.span(_.symbol != defn.ChildAnnot) + prefix ::: otherAnnot :: insertInto(rest) + } case _ => Annotation.Child(child) :: annots } From dffa71b895134bb00af5c340e38ad9f04e6ef750 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 9 Dec 2018 12:50:34 +0100 Subject: [PATCH 30/56] Allow to derive directly from Generic In this case only the generic infrastructure will be created. Also, fix a bug related to monomorphic cases and add a new `derive` printer for debugging. --- compiler/src/dotty/tools/dotc/config/Printers.scala | 1 + compiler/src/dotty/tools/dotc/typer/Deriving.scala | 13 ++++++++----- tests/run/derive-generic.check | 1 + tests/run/derive-generic.scala | 13 +++++++++++++ 4 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 tests/run/derive-generic.check create mode 100644 tests/run/derive-generic.scala diff --git a/compiler/src/dotty/tools/dotc/config/Printers.scala b/compiler/src/dotty/tools/dotc/config/Printers.scala index 74739b612ff9..d4d869e37da4 100644 --- a/compiler/src/dotty/tools/dotc/config/Printers.scala +++ b/compiler/src/dotty/tools/dotc/config/Printers.scala @@ -18,6 +18,7 @@ object Printers { val config: Printer = noPrinter val cyclicErrors: Printer = noPrinter val debug = noPrinter + val derive = new Printer val dottydoc: Printer = noPrinter val exhaustivity: Printer = noPrinter val gadts: Printer = noPrinter diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index 0c85348fb814..2b7885dde6d9 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -11,7 +11,7 @@ import ProtoTypes._ import util.Positions._ import collection.mutable import Constants.Constant -import config.Printers.typr +import config.Printers.derive import Inferencing._ import transform.TypeUtils._ import transform.SymUtils._ @@ -59,9 +59,9 @@ trait Deriving { this: Typer => } instantiate(ctx.fresh.setExploreTyperState().setOwner(caseClass)) case info: MethodType => - (cls.typeRef, info.paramInfos) + (caseClass.typeRef, info.paramInfos) case _ => - (cls.typeRef, Nil) + (caseClass.typeRef, Nil) } case _ => (sym.termRef, Nil) @@ -81,10 +81,11 @@ trait Deriving { this: Typer => * of abstracting over them. * Returns NoType if `cls` is neither sealed nor a case class or object. */ - lazy val shapeWithClassParams: Type = + lazy val shapeWithClassParams: Type = { if (cls.is(Case)) caseShape(cls) else if (cls.is(Sealed)) sealedShape else NoType + }.reporting(res => i"shape of $cls = $res", derive) private def shapeOfType(tp: Type) = { val shape0 = shapeWithClassParams @@ -155,7 +156,9 @@ trait Deriving { this: Typer => val underlyingType = underlyingClassRef(originalType) val derivedType = checkClassType(underlyingType, derived.pos, traitReq = false, stablePrefixReq = true) val nparams = derivedType.classSymbol.typeParams.length - if (nparams == 1) { + if (derivedType.isRef(defn.GenericClass)) + () // do nothing, a Generic instance will be created anyway by `addGeneric` + else if (nparams == 1) { val typeClass = derivedType.classSymbol val firstKindedParams = cls.typeParams.filterNot(_.info.isLambdaSub) val evidenceParamInfos = diff --git a/tests/run/derive-generic.check b/tests/run/derive-generic.check new file mode 100644 index 000000000000..357884f9ecae --- /dev/null +++ b/tests/run/derive-generic.check @@ -0,0 +1 @@ +Array(Array(B), Array(C, x, y)) diff --git a/tests/run/derive-generic.scala b/tests/run/derive-generic.scala new file mode 100644 index 000000000000..ee95a622c36b --- /dev/null +++ b/tests/run/derive-generic.scala @@ -0,0 +1,13 @@ +import reflect.Generic + +object Test extends App { + sealed trait A derives Generic + + object A { + case class B() extends A + case class C(x: Int, y: Int) extends A + } + + println(implicitly[Generic[A]].common.label.deep) +} + From 5eca9393d47d52bd94a87d01eabbff92d978c3a0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 9 Dec 2018 13:40:49 +0100 Subject: [PATCH 31/56] Improve robustness in the face of errors --- .../dotty/tools/dotc/config/Printers.scala | 2 +- .../src/dotty/tools/dotc/typer/Deriving.scala | 30 ++++++++++++------- tests/neg/deriving.scala | 25 ++++++++++++++++ 3 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 tests/neg/deriving.scala diff --git a/compiler/src/dotty/tools/dotc/config/Printers.scala b/compiler/src/dotty/tools/dotc/config/Printers.scala index d4d869e37da4..984c9d13b41c 100644 --- a/compiler/src/dotty/tools/dotc/config/Printers.scala +++ b/compiler/src/dotty/tools/dotc/config/Printers.scala @@ -18,7 +18,7 @@ object Printers { val config: Printer = noPrinter val cyclicErrors: Printer = noPrinter val debug = noPrinter - val derive = new Printer + val derive = noPrinter val dottydoc: Printer = noPrinter val exhaustivity: Printer = noPrinter val gadts: Printer = noPrinter diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index 2b7885dde6d9..2209523b837d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -34,6 +34,9 @@ trait Deriving { this: Typer => /** the children of `cls` ordered by textual occurrence */ lazy val children = cls.children + private def shapeError(explanation: => String): Unit = + ctx.error(i"cannot take shape of $cls\n$explanation", codePos) + /** The shape (of type Shape.Case) of a case given by `sym`. `sym` is either `cls` * itself, or a subclass of `cls`, or an instance of `cls`. */ @@ -41,7 +44,13 @@ trait Deriving { this: Typer => val (constr, elems) = sym match { case caseClass: ClassSymbol => - if (caseClass.is(Module)) + if (!caseClass.is(Case)) { + shapeError( + if (caseClass == cls) "it has anonymous or inaccessible subclasses" + else i"its subclass $caseClass is not a case class") + return NoType + } + else if (caseClass.is(Module)) (caseClass.sourceModule.termRef, Nil) else caseClass.primaryConstructor.info match { case info: PolyType => @@ -72,7 +81,7 @@ trait Deriving { this: Typer => /** The shape of `cls` if `cls` is sealed */ private def sealedShape: Type = { - val cases = children.map(caseShape) + val cases = children.map(caseShape).filter(_.exists) val casesShape = (cases :\ (defn.UnitType: Type))(defn.PairType.appliedTo(_, _)) defn.ShapeCasesType.appliedTo(casesShape) } @@ -84,7 +93,10 @@ trait Deriving { this: Typer => lazy val shapeWithClassParams: Type = { if (cls.is(Case)) caseShape(cls) else if (cls.is(Sealed)) sealedShape - else NoType + else { + shapeError(i"it is neither sealed nor a case class") + defn.ShapeCasesType.appliedTo(defn.UnitType) + } }.reporting(res => i"shape of $cls = $res", derive) private def shapeOfType(tp: Type) = { @@ -410,13 +422,11 @@ trait Deriving { this: Typer => def syntheticDefs: List[Tree] = synthetics.map(syntheticDef).toList } - def finalize(stat: tpd.TypeDef): tpd.Tree = - if (ctx.reporter.hasErrors) stat - else { - val templ @ Template(_, _, _, _) = stat.rhs - tpd.cpy.TypeDef(stat)( - rhs = tpd.cpy.Template(templ)(body = templ.body ++ new Finalizer().syntheticDefs)) - } + def finalize(stat: tpd.TypeDef): tpd.Tree = { + val templ @ Template(_, _, _, _) = stat.rhs + tpd.cpy.TypeDef(stat)( + rhs = tpd.cpy.Template(templ)(body = templ.body ++ new Finalizer().syntheticDefs)) + } /** Synthesized instance for `Generic[]` */ def genericInstance(clsType: Type): tpd.Tree = { diff --git a/tests/neg/deriving.scala b/tests/neg/deriving.scala new file mode 100644 index 000000000000..04fbe10f4144 --- /dev/null +++ b/tests/neg/deriving.scala @@ -0,0 +1,25 @@ +import reflect.Generic + +sealed trait A derives Generic // error: cannot take shape, it has anonymous or inaccessible subclasses + +object A { + def f() = { + println(new A {}) + println(new A {}) + } +} + +sealed trait B derives Generic // error: cannot take shape, its subclass class D is not a case class + +class D(x: Int, y: String) extends B + + +class E derives Generic // error: cannot take shape, it is neither sealed nor a case class + +sealed trait F derives Generic // error: cannot take shape, it has anonymous or inaccessible subclasses + +object G { + def f() = { + case class H() extends F + } +} From 12609e4f8d99c573550bfcdabe9d44acf27b86e9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 9 Dec 2018 13:44:45 +0100 Subject: [PATCH 32/56] Drop redundant check `shapeWithClassType` does not return a NoType anymore. --- compiler/src/dotty/tools/dotc/typer/Deriving.scala | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index 2209523b837d..89c40d13e392 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -431,13 +431,9 @@ trait Deriving { this: Typer => /** Synthesized instance for `Generic[]` */ def genericInstance(clsType: Type): tpd.Tree = { val shape = shapeOfType(clsType) - if (shape.exists) { - val genericType = RefinedType(defn.GenericType.appliedTo(clsType), tpnme.Shape, TypeAlias(shape)) - val finalizer = new Finalizer - finalizer.genericRHS(genericType, finalizer.genericClassRHS) - } - else errorTree(tpd.EmptyTree.withPos(codePos), - i"cannot take shape of type $clsType", codePos) + val genericType = RefinedType(defn.GenericType.appliedTo(clsType), tpnme.Shape, TypeAlias(shape)) + val finalizer = new Finalizer + finalizer.genericRHS(genericType, finalizer.genericClassRHS) } } } From 4d52930f93a5fae2ee2f97079d42e82b1a835d8b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 9 Dec 2018 15:30:22 +0100 Subject: [PATCH 33/56] Fixes to positions - harden addChild to also work if existing children don't have positions (this can happen if they came from other compilation units) - tweak position of Deriver, so that it always points to the deriving class template. --- .../src/dotty/tools/dotc/ast/Desugar.scala | 19 +++++++++++++++---- .../src/dotty/tools/dotc/typer/Namer.scala | 15 +++++++-------- compiler/test/dotc/run-from-tasty.blacklist | 1 + .../test/dotc/run-test-pickling.blacklist | 1 + tests/neg/deriving.scala | 2 +- 5 files changed, 25 insertions(+), 13 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index 5c17b164c7b4..c6d08a2bd148 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -23,7 +23,13 @@ object desugar { /** If a Select node carries this attachment, suppress the check * that its type refers to an acessible symbol. */ - val SuppressAccessCheck = new Property.Key[Unit] + val SuppressAccessCheck: Property.Key[Unit] = new Property.Key + + /** An attachment for companion modules of classes that have a `derives` clause. + * The position value indicates the start position of the template of the + * deriving class. + */ + val DerivingCompanion: Property.Key[Position] = new Property.Key /** Info of a variable in a pattern: The named tree and its type */ private type VarInfo = (NameTree, Tree) @@ -564,12 +570,17 @@ object desugar { // The thicket which is the desugared version of the companion object // synthetic object C extends parentTpt derives class-derived { defs } - def companionDefs(parentTpt: Tree, defs: List[Tree]) = - moduleDef( + def companionDefs(parentTpt: Tree, defs: List[Tree]) = { + val mdefs = moduleDef( ModuleDef( className.toTermName, Template(emptyConstructor, parentTpt :: Nil, companionDerived, EmptyValDef, defs)) .withMods(companionMods | Synthetic)) - .withSpan(cdef.span).toList + .withSpan(cdef.span).toList + if (companionDerived.nonEmpty) + for (modClsDef @ TypeDef(_, _) <- mdefs) + modClsDef.putAttachment(DerivingCompanion, impl.pos.startPos) + mdefs + } val companionMembers = defaultGetters ::: eqInstances ::: enumCases diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 96466be3ed05..8c95276c7cf7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -196,7 +196,6 @@ class Namer { typer: Typer => val TypedAhead: Property.Key[tpd.Tree] = new Property.Key val ExpandedTree: Property.Key[untpd.Tree] = new Property.Key val SymOfTree: Property.Key[Symbol] = new Property.Key - val DerivingCompanion: Property.Key[Unit] = new Property.Key val Deriver: Property.Key[typer.Deriver] = new Property.Key /** A partial map from unexpanded member and pattern defs and to their expansions. @@ -509,7 +508,7 @@ class Namer { typer: Typer => val childStart = child.pos.start def insertInto(annots: List[Annotation]): List[Annotation] = annots.find(_.symbol == defn.ChildAnnot) match { - case Some(Annotation.Child(other)) if childStart <= other.pos.start => + case Some(Annotation.Child(other)) if other.pos.exists && childStart <= other.pos.start => if (child == other) annots // can happen if a class has several inaccessible children else { @@ -575,7 +574,7 @@ class Namer { typer: Typer => if (fromTempl.derived.nonEmpty) { if (modTempl.derived.nonEmpty) ctx.error(em"a class and its companion cannot both have `derives' clauses", mdef.pos) - res.putAttachment(DerivingCompanion, ()) + res.putAttachment(desugar.DerivingCompanion, fromTempl.pos.startPos) } res } @@ -1013,11 +1012,11 @@ class Namer { typer: Typer => typr.println(i"completing $denot, parents = $parents%, %, parentTypes = $parentTypes%, %") if (impl.derived.nonEmpty) { - val derivingClass = - if (original.removeAttachment(DerivingCompanion).isDefined || - original.mods.is(Synthetic)) cls.companionClass.asClass - else cls - val deriver = new Deriver(derivingClass, impl.pos.startPos)(localCtx) + val (derivingClass, derivePos) = original.removeAttachment(desugar.DerivingCompanion) match { + case Some(pos) => (cls.companionClass.asClass, pos) + case None => (cls, impl.pos.startPos) + } + val deriver = new Deriver(derivingClass, derivePos)(localCtx) deriver.enterDerived(impl.derived) original.putAttachment(Deriver, deriver) } diff --git a/compiler/test/dotc/run-from-tasty.blacklist b/compiler/test/dotc/run-from-tasty.blacklist index 3c0fa861ad21..9a51ed515139 100644 --- a/compiler/test/dotc/run-from-tasty.blacklist +++ b/compiler/test/dotc/run-from-tasty.blacklist @@ -10,5 +10,6 @@ typeclass-derivation1.scala typeclass-derivation2.scala typeclass-derivation2a.scala typeclass-derivation3.scala +derive-generic.scala deriving-interesting-prefixes.scala diff --git a/compiler/test/dotc/run-test-pickling.blacklist b/compiler/test/dotc/run-test-pickling.blacklist index 699b810f50ac..6f412a5d041a 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -8,4 +8,5 @@ tuples1a.scala typeclass-derivation2.scala typeclass-derivation2a.scala typeclass-derivation3.scala +derive-generic.scala deriving-interesting-prefixes.scala diff --git a/tests/neg/deriving.scala b/tests/neg/deriving.scala index 04fbe10f4144..6dd84d6e84c3 100644 --- a/tests/neg/deriving.scala +++ b/tests/neg/deriving.scala @@ -1,6 +1,6 @@ import reflect.Generic -sealed trait A derives Generic // error: cannot take shape, it has anonymous or inaccessible subclasses +sealed trait A derives Generic // error: cannot take shape, it has anonymous or inaccessible subclasses object A { def f() = { From f3d901e84adb4afbbbb93c6e1d6a3826c6257b51 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 9 Dec 2018 16:12:33 +0100 Subject: [PATCH 34/56] Revert changes to CheckRealizable Since Shape is no longer generated separately we do not need to make an exception for synthesized members. --- compiler/src/dotty/tools/dotc/core/CheckRealizable.scala | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala b/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala index fe4a431fa6c1..6c6716bb1af3 100644 --- a/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala +++ b/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala @@ -114,11 +114,7 @@ class CheckRealizable(implicit ctx: Context) { /** `Realizable` if `tp` has good bounds, a `HasProblem...` instance * pointing to a bad bounds member otherwise. "Has good bounds" means: * - * - all non-synthetic type members have good bounds. - * Synthetic members are unchecked, for two reasons: - * - synthetic opaque aliases do have conflicting bounds, but this is OK - * - we should not force synthesized Shape types because that might - * query the `children` annotation too early. + * - all type members have good bounds (except for opaque helpers) * - all refinements of the underlying type have good bounds (except for opaque companions) * - all base types are class types, and if their arguments are wildcards * they have good bounds. @@ -136,8 +132,7 @@ class CheckRealizable(implicit ctx: Context) { val memberProblems = for { mbr <- tp.nonClassTypeMembers - if !mbr.symbol.is(Synthetic) - if !(mbr.info.loBound <:< mbr.info.hiBound) + if !(mbr.info.loBound <:< mbr.info.hiBound) && !mbr.symbol.isOpaqueHelper } yield new HasProblemBounds(mbr.name, mbr.info) From 465ffcc411755b59f55758eb7e20c83985fc4b17 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 9 Dec 2018 16:37:06 +0100 Subject: [PATCH 35/56] Fix patmat exhaustivity check files Children now appear in the order of their definition, where before there was no guaranteed order and the actual order was more likely the reverse of definition order. --- tests/patmat/3333.check | 2 +- tests/patmat/aliasing.check | 2 +- tests/patmat/andtype-opentype-interaction.check | 12 ++++++------ tests/patmat/andtype-refinedtype-interaction.check | 2 +- tests/patmat/enum/expected.check | 4 ++-- tests/patmat/exhausting.check | 2 +- tests/patmat/gadt.check | 2 +- tests/patmat/i2253.check | 2 +- tests/patmat/i3645b.check | 2 +- tests/patmat/i3645c.check | 2 +- tests/patmat/i3645d.check | 2 +- tests/patmat/i4226b.check | 2 +- tests/patmat/i4880.check | 2 +- tests/patmat/nontrivial-andtype.check | 2 +- tests/patmat/patmat-adt.check | 4 ++-- tests/patmat/patmat-ortype.check | 2 +- tests/patmat/patmatexhaust-huge.check | 2 +- tests/patmat/patmatexhaust.check | 2 +- tests/patmat/sealed-java-enums.check | 2 +- tests/patmat/t10019.check | 2 +- tests/patmat/t6420.check | 2 +- tests/patmat/t7746.check | 2 +- tests/patmat/t8430.check | 2 +- tests/patmat/t8511.check | 2 +- tests/patmat/t9232.check | 4 ++-- 25 files changed, 33 insertions(+), 33 deletions(-) diff --git a/tests/patmat/3333.check b/tests/patmat/3333.check index 488cf35a2b70..78ff92ec240b 100644 --- a/tests/patmat/3333.check +++ b/tests/patmat/3333.check @@ -1 +1 @@ -10: Pattern Match Exhaustivity: _: IntNumber, NotNaN \ No newline at end of file +10: Pattern Match Exhaustivity: NotNaN, _: IntNumber diff --git a/tests/patmat/aliasing.check b/tests/patmat/aliasing.check index ff0659d29566..d7c21e8d0605 100644 --- a/tests/patmat/aliasing.check +++ b/tests/patmat/aliasing.check @@ -1,3 +1,3 @@ -14: Pattern Match Exhaustivity: _: Clazz & Test.Alias1, _: Trait & Test.Alias1 +14: Pattern Match Exhaustivity: _: Trait & Test.Alias1, _: Clazz & Test.Alias1 19: Pattern Match Exhaustivity: _: Trait & Test.Alias2 23: Pattern Match Exhaustivity: _: Trait & (Test.Alias2 & OpenTrait2){x: Int} diff --git a/tests/patmat/andtype-opentype-interaction.check b/tests/patmat/andtype-opentype-interaction.check index ed0bddb70363..1d09f03d61e5 100644 --- a/tests/patmat/andtype-opentype-interaction.check +++ b/tests/patmat/andtype-opentype-interaction.check @@ -1,6 +1,6 @@ -23: Pattern Match Exhaustivity: _: SealedAbstractClass & OpenTrait, _: SealedClass & OpenTrait, _: SealedTrait & OpenTrait, _: AbstractClass & OpenTrait, _: Clazz & OpenTrait, _: Trait & OpenTrait -27: Pattern Match Exhaustivity: _: SealedAbstractClass & OpenTrait & OpenTrait2, _: SealedClass & OpenTrait & OpenTrait2, _: SealedTrait & OpenTrait & OpenTrait2, _: AbstractClass & OpenTrait & OpenTrait2, _: Clazz & OpenTrait & OpenTrait2, _: Trait & OpenTrait & OpenTrait2 -31: Pattern Match Exhaustivity: _: SealedTrait & OpenClass, _: Trait & OpenClass -35: Pattern Match Exhaustivity: _: SealedTrait & OpenTrait & OpenClass, _: Trait & OpenTrait & OpenClass -43: Pattern Match Exhaustivity: _: SealedTrait & OpenAbstractClass, _: Trait & OpenAbstractClass -47: Pattern Match Exhaustivity: _: SealedTrait & OpenClass & (OpenTrait & OpenClassSubclass), _: Trait & OpenClass & (OpenTrait & OpenClassSubclass) +23: Pattern Match Exhaustivity: _: Trait & OpenTrait, _: Clazz & OpenTrait, _: AbstractClass & OpenTrait, _: SealedTrait & OpenTrait, _: SealedClass & OpenTrait, _: SealedAbstractClass & OpenTrait +27: Pattern Match Exhaustivity: _: Trait & OpenTrait & OpenTrait2, _: Clazz & OpenTrait & OpenTrait2, _: AbstractClass & OpenTrait & OpenTrait2, _: SealedTrait & OpenTrait & OpenTrait2, _: SealedClass & OpenTrait & OpenTrait2, _: SealedAbstractClass & OpenTrait & OpenTrait2 +31: Pattern Match Exhaustivity: _: Trait & OpenClass, _: SealedTrait & OpenClass +35: Pattern Match Exhaustivity: _: Trait & OpenTrait & OpenClass, _: SealedTrait & OpenTrait & OpenClass +43: Pattern Match Exhaustivity: _: Trait & OpenAbstractClass, _: SealedTrait & OpenAbstractClass +47: Pattern Match Exhaustivity: _: Trait & OpenClass & (OpenTrait & OpenClassSubclass), _: SealedTrait & OpenClass & (OpenTrait & OpenClassSubclass) \ No newline at end of file diff --git a/tests/patmat/andtype-refinedtype-interaction.check b/tests/patmat/andtype-refinedtype-interaction.check index acee175e13b4..b4bf99c88274 100644 --- a/tests/patmat/andtype-refinedtype-interaction.check +++ b/tests/patmat/andtype-refinedtype-interaction.check @@ -1,5 +1,5 @@ 32: Pattern Match Exhaustivity: _: Trait & C1{x: Int} -48: Pattern Match Exhaustivity: _: Clazz & (C1 | (C2 | T1)){x: Int} & (C3 | (C4 | T2)){x: Int}, _: Trait & (C1 | (C2 | T1)){x: Int} & (C3 | (C4 | T2)){x: Int} +48: Pattern Match Exhaustivity: _: Trait & (C1 | (C2 | T1)){x: Int} & (C3 | (C4 | T2)){x: Int}, _: Clazz & (C1 | (C2 | T1)){x: Int} & (C3 | (C4 | T2)){x: Int} 54: Pattern Match Exhaustivity: _: Trait & (C1 | (C2 | T1)){x: Int} & C3{x: Int} 65: Pattern Match Exhaustivity: _: Trait & (C1 | C2){x: Int} & (C3 | SubC1){x: Int} 72: Pattern Match Exhaustivity: _: Trait & (T1 & (C1 | SubC2)){x: Int} & (T2 & (C2 | C3 | SubC1)){x: Int} & diff --git a/tests/patmat/enum/expected.check b/tests/patmat/enum/expected.check index 296cf8bb2a75..1eac8b0a6075 100644 --- a/tests/patmat/enum/expected.check +++ b/tests/patmat/enum/expected.check @@ -1,2 +1,2 @@ -4: Pattern Match Exhaustivity: SATURDAY, FRIDAY, THURSDAY, SUNDAY -15: Pattern Match Exhaustivity: SATURDAY, FRIDAY, THURSDAY +4: Pattern Match Exhaustivity: SUNDAY, THURSDAY, FRIDAY, SATURDAY +15: Pattern Match Exhaustivity: THURSDAY, FRIDAY, SATURDAY diff --git a/tests/patmat/exhausting.check b/tests/patmat/exhausting.check index 86ec004acac6..ad7dff8cb964 100644 --- a/tests/patmat/exhausting.check +++ b/tests/patmat/exhausting.check @@ -1,4 +1,4 @@ -21: Pattern Match Exhaustivity: List(_), List(_, _, _, _: _*) +21: Pattern Match Exhaustivity: List(_, _, _, _: _*), List(_) 27: Pattern Match Exhaustivity: Nil 32: Pattern Match Exhaustivity: List(_, _: _*) 39: Pattern Match Exhaustivity: Bar3 diff --git a/tests/patmat/gadt.check b/tests/patmat/gadt.check index 71860e52a9e3..624c0cd7c999 100644 --- a/tests/patmat/gadt.check +++ b/tests/patmat/gadt.check @@ -1,4 +1,4 @@ 13: Pattern Match Exhaustivity: IntLit(_) 22: Pattern Match Exhaustivity: Or(_, _) -45: Pattern Match Exhaustivity: BooleanLit(_), IntLit(_) +45: Pattern Match Exhaustivity: IntLit(_), BooleanLit(_) 55: Pattern Match Exhaustivity: Sum(_, _) diff --git a/tests/patmat/i2253.check b/tests/patmat/i2253.check index d9534890a800..2f95d0968c3a 100644 --- a/tests/patmat/i2253.check +++ b/tests/patmat/i2253.check @@ -1,3 +1,3 @@ -28: Pattern Match Exhaustivity: HasIntXIntM, HasIntXStringM +28: Pattern Match Exhaustivity: HasIntXStringM, HasIntXIntM 29: Pattern Match Exhaustivity: HasIntXIntM 30: Pattern Match Exhaustivity: HasIntXIntM diff --git a/tests/patmat/i3645b.check b/tests/patmat/i3645b.check index 3d4d48cd44aa..ac81ddffb0f8 100644 --- a/tests/patmat/i3645b.check +++ b/tests/patmat/i3645b.check @@ -1 +1 @@ -21: Pattern Match Exhaustivity: K3, K2 +21: Pattern Match Exhaustivity: K2, K3 diff --git a/tests/patmat/i3645c.check b/tests/patmat/i3645c.check index 3d4d48cd44aa..ac81ddffb0f8 100644 --- a/tests/patmat/i3645c.check +++ b/tests/patmat/i3645c.check @@ -1 +1 @@ -21: Pattern Match Exhaustivity: K3, K2 +21: Pattern Match Exhaustivity: K2, K3 diff --git a/tests/patmat/i3645d.check b/tests/patmat/i3645d.check index 3d4d48cd44aa..ac81ddffb0f8 100644 --- a/tests/patmat/i3645d.check +++ b/tests/patmat/i3645d.check @@ -1 +1 @@ -21: Pattern Match Exhaustivity: K3, K2 +21: Pattern Match Exhaustivity: K2, K3 diff --git a/tests/patmat/i4226b.check b/tests/patmat/i4226b.check index 1528dd515904..591201a83049 100644 --- a/tests/patmat/i4226b.check +++ b/tests/patmat/i4226b.check @@ -1 +1 @@ -11: Pattern Match Exhaustivity: _: Empty, Just(_) +11: Pattern Match Exhaustivity: Just(_), _: Empty diff --git a/tests/patmat/i4880.check b/tests/patmat/i4880.check index da0a6804ac08..468dc57681a2 100644 --- a/tests/patmat/i4880.check +++ b/tests/patmat/i4880.check @@ -1 +1 @@ -10: Pattern Match Exhaustivity: _: ZipArchive, _: VirtualFile, _: PlainFile +10: Pattern Match Exhaustivity: _: PlainFile, _: VirtualFile, _: ZipArchive diff --git a/tests/patmat/nontrivial-andtype.check b/tests/patmat/nontrivial-andtype.check index 64094518583d..8fa825128dbf 100644 --- a/tests/patmat/nontrivial-andtype.check +++ b/tests/patmat/nontrivial-andtype.check @@ -1 +1 @@ -16: Pattern Match Exhaustivity: D, _: C, B(), _: A +16: Pattern Match Exhaustivity: _: A, B(), _: C, D diff --git a/tests/patmat/patmat-adt.check b/tests/patmat/patmat-adt.check index 7a938792fee8..88bc99f5ddc7 100644 --- a/tests/patmat/patmat-adt.check +++ b/tests/patmat/patmat-adt.check @@ -1,5 +1,5 @@ -7: Pattern Match Exhaustivity: Bad(Good(_)), Good(Bad(_)) +7: Pattern Match Exhaustivity: Good(Bad(_)), Bad(Good(_)) 19: Pattern Match Exhaustivity: Some(_) 24: Pattern Match Exhaustivity: (_, Some(_)) 29: Pattern Match Exhaustivity: (None, None), (Some(_), Some(_)) -50: Pattern Match Exhaustivity: LetL(BooleanLit), LetL(IntLit) +50: Pattern Match Exhaustivity: LetL(IntLit), LetL(BooleanLit) diff --git a/tests/patmat/patmat-ortype.check b/tests/patmat/patmat-ortype.check index 0bd790437769..f36978068485 100644 --- a/tests/patmat/patmat-ortype.check +++ b/tests/patmat/patmat-ortype.check @@ -1,3 +1,3 @@ 8: Pattern Match Exhaustivity: _: String -18: Pattern Match Exhaustivity: Some(_: String), None +18: Pattern Match Exhaustivity: None, Some(_: String) 36: Pattern Match Exhaustivity: Some(_: String) diff --git a/tests/patmat/patmatexhaust-huge.check b/tests/patmat/patmatexhaust-huge.check index f622622decf3..29e94edbaf2b 100644 --- a/tests/patmat/patmatexhaust-huge.check +++ b/tests/patmat/patmatexhaust-huge.check @@ -1 +1 @@ -404: Pattern Match Exhaustivity: C397, C392 +404: Pattern Match Exhaustivity: C392, C397 diff --git a/tests/patmat/patmatexhaust.check b/tests/patmat/patmatexhaust.check index 28927e2fedeb..79b700ea37f6 100644 --- a/tests/patmat/patmatexhaust.check +++ b/tests/patmat/patmatexhaust.check @@ -6,5 +6,5 @@ 75: Pattern Match Exhaustivity: _: B 87: Pattern Match Exhaustivity: _: C1 100: Pattern Match Exhaustivity: _: C1 -114: Pattern Match Exhaustivity: D2(), D1 +114: Pattern Match Exhaustivity: D1, D2() 126: Pattern Match Exhaustivity: _: C1 diff --git a/tests/patmat/sealed-java-enums.check b/tests/patmat/sealed-java-enums.check index 86e73f0da35e..06ff48acdf1d 100644 --- a/tests/patmat/sealed-java-enums.check +++ b/tests/patmat/sealed-java-enums.check @@ -1 +1 @@ -5: Pattern Match Exhaustivity: TERMINATED, TIMED_WAITING, BLOCKED +5: Pattern Match Exhaustivity: BLOCKED, TIMED_WAITING, TERMINATED diff --git a/tests/patmat/t10019.check b/tests/patmat/t10019.check index 49f5b152d1fb..b3799122e3fd 100644 --- a/tests/patmat/t10019.check +++ b/tests/patmat/t10019.check @@ -1,2 +1,2 @@ -2: Pattern Match Exhaustivity: (List(_, _: _*), Nil), (List(_, _: _*), List(_, _, _: _*)), (Nil, List(_, _: _*)), (List(_, _, _: _*), List(_, _: _*)) +2: Pattern Match Exhaustivity: (List(_, _: _*), List(_, _, _: _*)), (List(_, _: _*), Nil), (List(_, _, _: _*), List(_, _: _*)), (Nil, List(_, _: _*)) 11: Pattern Match Exhaustivity: (Foo(None), Foo(_)) diff --git a/tests/patmat/t6420.check b/tests/patmat/t6420.check index cacdd5c8c92e..021a88adc6c3 100644 --- a/tests/patmat/t6420.check +++ b/tests/patmat/t6420.check @@ -1 +1 @@ -5: Pattern Match Exhaustivity: (_: List, Nil), (_: List, List(true, _: _*)), (_: List, List(false, _: _*)) +5: Pattern Match Exhaustivity: (_: List, List(true, _: _*)), (_: List, List(false, _: _*)), (_: List, Nil) diff --git a/tests/patmat/t7746.check b/tests/patmat/t7746.check index cdba0449f0ab..327d64b9c2b6 100644 --- a/tests/patmat/t7746.check +++ b/tests/patmat/t7746.check @@ -1 +1 @@ -2: Pattern Match Exhaustivity: Some(_), None +2: Pattern Match Exhaustivity: None, Some(_) diff --git a/tests/patmat/t8430.check b/tests/patmat/t8430.check index d72de523733e..e5baca74304f 100644 --- a/tests/patmat/t8430.check +++ b/tests/patmat/t8430.check @@ -1 +1 @@ -15: Pattern Match Exhaustivity: LetF, LetC, LetP, LetL(UnitLit), LetL(BooleanLit), LetL(IntLit) +15: Pattern Match Exhaustivity: LetL(IntLit), LetL(BooleanLit), LetL(UnitLit), LetP, LetC, LetF diff --git a/tests/patmat/t8511.check b/tests/patmat/t8511.check index 121e0ab874e0..57dc8538f40a 100644 --- a/tests/patmat/t8511.check +++ b/tests/patmat/t8511.check @@ -1 +1 @@ -18: Pattern Match Exhaustivity: EatsExhaustiveWarning(_), Baz(), Bar(_) +18: Pattern Match Exhaustivity: Bar(_), Baz(), EatsExhaustiveWarning(_) diff --git a/tests/patmat/t9232.check b/tests/patmat/t9232.check index fdf9df06c5f3..2c944e45da7f 100644 --- a/tests/patmat/t9232.check +++ b/tests/patmat/t9232.check @@ -1,3 +1,3 @@ 13: Pattern Match Exhaustivity: Node2() -17: Pattern Match Exhaustivity: Node2(), Node1(Foo(Nil)), Node1(Foo(List(_, _: _*))) -21: Pattern Match Exhaustivity: Node2(), Node1(Foo(Nil)) +17: Pattern Match Exhaustivity: Node1(Foo(List(_, _: _*))), Node1(Foo(Nil)), Node2() +21: Pattern Match Exhaustivity: Node1(Foo(Nil)), Node2() From d4025d6822fab1155362f6fd429e607920aed1a0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 9 Dec 2018 16:51:07 +0100 Subject: [PATCH 36/56] Polishings --- compiler/src/dotty/tools/dotc/typer/Implicits.scala | 2 +- compiler/src/dotty/tools/dotc/typer/Inliner.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index f5718cf9d72c..250aba998fd7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -745,7 +745,7 @@ trait Implicits { self: Typer => def synthesizedGeneric(formal: Type): Tree = formal.argTypes match { case arg :: Nil => - val arg1 = fullyDefinedType(arg, "ClassTag argument", pos) + val arg1 = fullyDefinedType(arg, "Generic argument", pos) val clsType = checkClassType(arg1, pos, traitReq = false, stablePrefixReq = true) new Deriver(clsType.classSymbol.asClass, pos).genericInstance(clsType) case _ => diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 34504a43cf47..b81e9e8387a6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -991,7 +991,7 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { | patterns : ${tree.cases.map(patStr).mkString("\n ")}""" else em"""cannot reduce inline match with - | scrutinee: $sel : ${selType} : ${selType.underlyingIfProxy} + | scrutinee: $sel : ${selType} | patterns : ${tree.cases.map(patStr).mkString("\n ")}""" errorTree(tree, msg) } From 1a9c5c0c7b4689908c34ed715e93962698a50573 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 9 Dec 2018 18:32:11 +0100 Subject: [PATCH 37/56] Tweaks to docs --- docs/docs/reference/derivation.md | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/docs/docs/reference/derivation.md b/docs/docs/reference/derivation.md index d5ec958a9b22..2039209cbb0b 100644 --- a/docs/docs/reference/derivation.md +++ b/docs/docs/reference/derivation.md @@ -3,15 +3,14 @@ layout: doc-page title: Typeclass Derivation --- -Implicit instances for some typeclass traits can be derived automatically. Example: +Typeclass derivation is a way to generate instances of certain type classes automatically or with minimal code hints. A type class in this sense is any trait or class with a type parameter that describes the type being operated on. Commonly used examples are `Eq`, `Ordering`, `Show`, or `Pickling`. Example: ```scala enum Tree[T] derives Eq, Ordering, Pickling { case Branch(left: Tree[T], right: Tree[T]) case Leaf(elem: T) } ``` -The derives clause automatically generates typeclass instances for -`Eq`, `Ordering`, and `Pickling` in the companion object `Tree`: +The `derives` clause generates implicit instances of the `Eq`, `Ordering`, and `Pickling` traits in the companion object `Tree`: ```scala impl [T: Eq] of Eq[Tree[T]] = Eq.derived impl [T: Ordering] of Ordering[Tree[T]] = Ordering.derived @@ -39,21 +38,22 @@ case object None extends Option[Nothing] The generated typeclass instances are placed in the companion objects `Labelled` and `Option`, respectively. -### Derivable Traits +### Derivable Types -A trait can appear in a `derives` clause as long as +A trait or class can appear in a `derives` clause if - - it has a single type parameter, + - it has a single type parameter, and - its companion object defines a method named `derived`. These two conditions ensure that the synthesized derived instances for the trait are well-formed. The type and implementation of a `derived` method are arbitrary, but typically it has a definition like this: -``` +```scala def derived[T] with (ev: Generic[T]) = ... ``` -That is, the `derived` method takes an implicit parameter of type `Generic` that determines the _shape_ of the deriving type `T` and it computes the typeclass implementation according to that shape. Implicit `Generic` instances are generated automatically for all types that have a `derives` clause. - -This is all a user of typeclass derivation has to know. The rest of this page contains information needed to be able to write a typeclass that can be used in a `derives` clause. In particular, it details the means provided for the implementation of data generic `derived` methods. - +That is, the `derived` method takes an implicit parameter of type `Generic` that determines the _shape_ of the deriving type `T` and it computes the typeclass implementation according to that shape. Implicit `Generic` instances are generated automatically for all types that have a `derives` clause. One can also derive `Generic` alone, which means a `Generic` instance is generated without any other type class instances. E.g.: +```scala +sealed trait ParseResult[T] derives Generic +``` +This is all a user of typeclass derivation has to know. The rest of this page contains information needed to be able to write a typeclass that can appear in a `derives` clause. In particular, it details the means provided for the implementation of data generic `derived` methods. ### The Shape Type From 1a58222851b9e4146221cc38e29e47a1b149e0e9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 10 Dec 2018 10:19:34 +0100 Subject: [PATCH 38/56] Fix typo --- docs/docs/reference/derivation.md | 2 +- tests/run/typeclass-derivation3.scala | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/docs/reference/derivation.md b/docs/docs/reference/derivation.md index 2039209cbb0b..914a5228c92f 100644 --- a/docs/docs/reference/derivation.md +++ b/docs/docs/reference/derivation.md @@ -91,7 +91,7 @@ Case[Labelled[T], (T, String)] And here is the one for `Option[T]`: ```scala Cases[ - Case[Some[T], T * Unit], + Case[Some[T], T *: Unit], Case[None.type, Unit] ] ``` diff --git a/tests/run/typeclass-derivation3.scala b/tests/run/typeclass-derivation3.scala index 6d7bc6a0bb2c..519b28d7ab1e 100644 --- a/tests/run/typeclass-derivation3.scala +++ b/tests/run/typeclass-derivation3.scala @@ -19,8 +19,6 @@ object datatypes { sealed trait Either[+L, +R] extends Product derives Eq, Pickler, Show case class Left[L](x: L) extends Either[L, Nothing] case class Right[R](x: R) extends Either[Nothing, R] - - //object Either {} } object typeclasses { From 7fccb61990edb52a596956c992ffbcf49a390ad8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 10 Dec 2018 10:20:54 +0100 Subject: [PATCH 39/56] Revert to standard implicit syntax in docs Better not to mix different proposals. --- docs/docs/reference/derivation.md | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/docs/docs/reference/derivation.md b/docs/docs/reference/derivation.md index 914a5228c92f..d5c57f8839c9 100644 --- a/docs/docs/reference/derivation.md +++ b/docs/docs/reference/derivation.md @@ -12,9 +12,9 @@ enum Tree[T] derives Eq, Ordering, Pickling { ``` The `derives` clause generates implicit instances of the `Eq`, `Ordering`, and `Pickling` traits in the companion object `Tree`: ```scala -impl [T: Eq] of Eq[Tree[T]] = Eq.derived -impl [T: Ordering] of Ordering[Tree[T]] = Ordering.derived -impl [T: Pickling] of Pickling[Tree[T]] = Pickling.derived +implicit def derived$Eq [T: Eq]: Eq[Tree[T]] = Eq.derived +implicit def derived$Ordering [T: Ordering]: Ordering[Tree[T]] = Ordering.derived +implicit def derived$Pickling [T: Pickling]: Pickling[Tree[T]] = Pickling.derived ``` **Note**: This page uses the new syntax proposed for implicits that is explored in #5448. This is not yet an endorsement of that syntax, but rather a way to experiment with it. @@ -47,7 +47,7 @@ A trait or class can appear in a `derives` clause if These two conditions ensure that the synthesized derived instances for the trait are well-formed. The type and implementation of a `derived` method are arbitrary, but typically it has a definition like this: ```scala - def derived[T] with (ev: Generic[T]) = ... + def derived[T](implicit ev: Generic[T]) = ... ``` That is, the `derived` method takes an implicit parameter of type `Generic` that determines the _shape_ of the deriving type `T` and it computes the typeclass implementation according to that shape. Implicit `Generic` instances are generated automatically for all types that have a `derives` clause. One can also derive `Generic` alone, which means a `Generic` instance is generated without any other type class instances. E.g.: ```scala @@ -100,9 +100,15 @@ is represented as `T *: Unit` since there is no direct syntax for such tuples: ` ### The Generic TypeClass -For every class `C[T_1,...,T_n]` with a `derives` clause, the compiler generates in the companion object of `C` a type class instance like this: +For every class `C[T_1,...,T_n]` with a `derives` clause, the compiler generates in the companion object of `C` an implicit instance of `Generic[C[T_1,...,T_n]]` that follows +the outline below: ```scala -impl [T_1, ..., T_n] of Generic[C[T_1,...,T_n]] { type Shape = ... } = ... +class derived$Generic[T_1, ..., T_n] extends Generic[C[T_1,...,T_n]] { + type Shape = ... + ... +} +implicit def derived$Generic[T_1,...,T_n]]: derived$Generic[T_1,...,T_n]] = + new derived$Generic[T_1,...,T_n]] ``` where the right hand side of `Shape` is the shape type of `C[T_1,...,T_n]`. For instance, the definition @@ -117,13 +123,15 @@ would produce: object Result { import scala.compiletime.Shape._ - impl [T, E] of Generic[Result[T, E]] { + class derived$Generic[T, E] extends Generic[Result[T, E]] { type Shape = Cases[( Case[Ok[T], T *: Unit], Case[Err[E], E *: Unit] )] ... } + implicit def derived$Generic[T, E]: derived$Generic[T, E] = + new derived$Generic[T, E] } ``` The `Generic` class is defined in package `scala.reflect`. @@ -219,7 +227,7 @@ trait Eq[T] { We need to implement a method `Eq.derived` that produces an instance of `Eq[T]` provided there exists evidence of type `Generic[T]`. Here's a possible solution: ```scala - inline def derived[T, S <: Shape] with (ev: Generic[T]): Eq[T] = new Eq[T] { + inline def derived[T, S <: Shape](implicit ev: Generic[T]): Eq[T] = new Eq[T] { def eql(x: T, y: T): Boolean = { val mx = ev.reflect(x) // (1) val my = ev.reflect(y) // (2) @@ -316,7 +324,7 @@ calling the `error` method defined in `scala.compiletime`. **Example:** Here is a slightly polished and compacted version of the code that's generated by inline expansion for the derived `Eq` instance of class `Tree`. ```scala -impl Eq_Tree_impl[T] with (elemEq: Eq[T]) of Eq[Tree[T]] { +implicit def Eq_Tree_impl[T](implicit elemEq: Eq[T]): Eq[T] = new Eq[Tree[T]] { def eql(x: Tree[T], y: Tree[T]): Boolean = { val ev = implOf[Generic[Tree[T]]] val mx = ev.reflect(x) @@ -342,7 +350,7 @@ One important difference between this approach and Scala-2 typeclass derivation Sometimes one would like to derive a typeclass instance for an ADT after the ADT is defined, without being able to change the code of the ADT itself. To do this, simply define an instance with the `derived` method of the typeclass as right hand side. E.g, to implement `Ordering` for `Option`, define: ```scala -impl [T: Ordering] of Ordering[Option[T]] = Ordering.derived +implicit def derived$Ordering[T: Ordering]: Ordering[Option[T]] = Ordering.derived ``` Usually, the `Ordering.derived` clause has an implicit parameter of type `Generic[Option[T]]`. Since the `Option` trait has a `derives` clause, From 46e4b19ae1f282a9a143d9aa2a37b340051a7739 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 10 Dec 2018 19:07:04 +0100 Subject: [PATCH 40/56] Polishings --- docs/docs/reference/derivation.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/docs/reference/derivation.md b/docs/docs/reference/derivation.md index d5c57f8839c9..e9d9fba1e000 100644 --- a/docs/docs/reference/derivation.md +++ b/docs/docs/reference/derivation.md @@ -17,8 +17,6 @@ implicit def derived$Ordering [T: Ordering]: Ordering[Tree[T]] = Ordering.derive implicit def derived$Pickling [T: Pickling]: Pickling[Tree[T]] = Pickling.derived ``` -**Note**: This page uses the new syntax proposed for implicits that is explored in #5448. This is not yet an endorsement of that syntax, but rather a way to experiment with it. - ### Deriving Types Besides for `enums`, typeclasses can also be derived for other sets of classes and objects that form an algebraic data type. These are: @@ -324,7 +322,7 @@ calling the `error` method defined in `scala.compiletime`. **Example:** Here is a slightly polished and compacted version of the code that's generated by inline expansion for the derived `Eq` instance of class `Tree`. ```scala -implicit def Eq_Tree_impl[T](implicit elemEq: Eq[T]): Eq[T] = new Eq[Tree[T]] { +implicit def derived$Eq[T](implicit elemEq: Eq[T]): Eq[T] = new Eq[Tree[T]] { def eql(x: Tree[T], y: Tree[T]): Boolean = { val ev = implOf[Generic[Tree[T]]] val mx = ev.reflect(x) @@ -350,7 +348,7 @@ One important difference between this approach and Scala-2 typeclass derivation Sometimes one would like to derive a typeclass instance for an ADT after the ADT is defined, without being able to change the code of the ADT itself. To do this, simply define an instance with the `derived` method of the typeclass as right hand side. E.g, to implement `Ordering` for `Option`, define: ```scala -implicit def derived$Ordering[T: Ordering]: Ordering[Option[T]] = Ordering.derived +implicit def myOptionOrdering[T: Ordering]: Ordering[Option[T]] = Ordering.derived ``` Usually, the `Ordering.derived` clause has an implicit parameter of type `Generic[Option[T]]`. Since the `Option` trait has a `derives` clause, From 5182b8bf3599608c605221821f69d6d40dc83663 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 11 Dec 2018 17:49:43 +0100 Subject: [PATCH 41/56] Fix erasure of *: and NonEmptyTuple The erasure of these classes should be Product not Object. Otherwise we cannot discriminate in pattern matching between pairs and (). --- compiler/src/dotty/tools/dotc/core/Definitions.scala | 9 ++++++++- compiler/src/dotty/tools/dotc/core/TypeErasure.scala | 10 +++++----- compiler/src/dotty/tools/dotc/transform/Erasure.scala | 4 ++-- .../dotty/tools/dotc/transform/GenericSignatures.scala | 4 ++-- library/src-bootstrapped/scala/Tuple.scala | 4 ++-- 5 files changed, 19 insertions(+), 12 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index d20f3151a9aa..343ee1215362 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -8,6 +8,7 @@ import unpickleScala2.Scala2Unpickler.ensureConstructor import scala.collection.mutable import collection.mutable import Denotations.SingleDenotation +import util.SimpleIdentityMap object Definitions { @@ -1273,7 +1274,13 @@ class Definitions { def isValueSubClass(sym1: Symbol, sym2: Symbol): Boolean = valueTypeEnc(sym2.asClass.name) % valueTypeEnc(sym1.asClass.name) == 0 - lazy val erasedToObject: Set[Symbol] = Set(AnyClass, AnyValClass, TupleClass, NonEmptyTupleClass, SingletonClass) + lazy val specialErasure: SimpleIdentityMap[Symbol, ClassSymbol] = + SimpleIdentityMap.Empty[Symbol] + .updated(AnyClass, ObjectClass) + .updated(AnyValClass, ObjectClass) + .updated(SingletonClass, ObjectClass) + .updated(TupleClass, ObjectClass) + .updated(NonEmptyTupleClass, ProductClass) // ----- Initialization --------------------------------------------------- diff --git a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala index 9318c9e063b2..fafc82cf956f 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeErasure.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeErasure.scala @@ -48,7 +48,7 @@ object TypeErasure { val sym = tp.symbol sym.isClass && !erasureDependsOnArgs(sym) && - !defn.erasedToObject.contains(sym) && + !defn.specialErasure.contains(sym) && !defn.isSyntheticFunctionClass(sym) case _: TermRef => true @@ -441,7 +441,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean if ((cls eq defn.ObjectClass) || cls.isPrimitiveValueClass) Nil else parents.mapConserve(eraseParent) match { case tr :: trs1 => - assert(!tr.classSymbol.is(Trait), cls) + assert(!tr.classSymbol.is(Trait), i"$cls has bad parents $parents%, %") val tr1 = if (cls is Trait) defn.ObjectType else tr tr1 :: trs1.filterNot(_ isRef defn.ObjectClass) case nil => nil @@ -467,7 +467,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean private def erasePair(tp: Type)(implicit ctx: Context): Type = { val arity = tp.tupleArity - if (arity < 0) defn.ObjectType + if (arity < 0) defn.ProductType else if (arity <= Definitions.MaxTupleArity) defn.TupleType(arity) else defn.TupleXXLType } @@ -524,8 +524,8 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean private def normalizeClass(cls: ClassSymbol)(implicit ctx: Context): ClassSymbol = { if (cls.owner == defn.ScalaPackageClass) { - if (defn.erasedToObject.contains(cls)) - return defn.ObjectClass + if (defn.specialErasure.contains(cls)) + return defn.specialErasure(cls) if (cls == defn.UnitClass) return defn.BoxedUnitClass } diff --git a/compiler/src/dotty/tools/dotc/transform/Erasure.scala b/compiler/src/dotty/tools/dotc/transform/Erasure.scala index 3e13e0ae3f1a..09276076840a 100644 --- a/compiler/src/dotty/tools/dotc/transform/Erasure.scala +++ b/compiler/src/dotty/tools/dotc/transform/Erasure.scala @@ -397,9 +397,9 @@ object Erasure { def mapOwner(sym: Symbol): Symbol = { def recur(owner: Symbol): Symbol = - if (defn.erasedToObject.contains(owner)) { + if (defn.specialErasure.contains(owner)) { assert(sym.isConstructor, s"${sym.showLocated}") - defn.ObjectClass + defn.specialErasure(owner) } else if (defn.isSyntheticFunctionClass(owner)) defn.erasedFunctionClass(owner) else diff --git a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala index baa1cbe926c5..76408f3f14ac 100644 --- a/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala +++ b/compiler/src/dotty/tools/dotc/transform/GenericSignatures.scala @@ -198,8 +198,8 @@ object GenericSignatures { assert(!sym.isAliasType, "Unexpected alias type: " + sym) typeParamSig(sym.name.lastPart) } - else if (defn.erasedToObject.contains(sym)) - jsig(defn.ObjectType) + else if (defn.specialErasure.contains(sym)) + jsig(defn.specialErasure(sym).typeRef) else if (sym == defn.UnitClass || sym == defn.BoxedUnitModule) jsig(defn.BoxedUnitType) else if (sym == defn.NothingClass) diff --git a/library/src-bootstrapped/scala/Tuple.scala b/library/src-bootstrapped/scala/Tuple.scala index e98ad4df5444..06de54bedf5c 100644 --- a/library/src-bootstrapped/scala/Tuple.scala +++ b/library/src-bootstrapped/scala/Tuple.scala @@ -307,7 +307,7 @@ object Tuple { } } -abstract sealed class NonEmptyTuple extends Tuple { +abstract trait NonEmptyTuple extends Tuple { import Tuple._ import NonEmptyTuple._ @@ -473,7 +473,7 @@ object NonEmptyTuple { } @showAsInfix -sealed class *:[+H, +T <: Tuple] extends NonEmptyTuple +sealed class *:[+H, +T <: Tuple] extends Object with NonEmptyTuple object *: { inline def unapply[H, T <: Tuple](x: H *: T) = (x.head, x.tail) From efb3e5fb16b25bf780a2e16b949ee51d904611cf Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 13 Dec 2018 21:09:01 +0100 Subject: [PATCH 42/56] Make NonEmptyTuple `sealed`. --- library/src-bootstrapped/scala/Tuple.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src-bootstrapped/scala/Tuple.scala b/library/src-bootstrapped/scala/Tuple.scala index 06de54bedf5c..dfd35827e1ed 100644 --- a/library/src-bootstrapped/scala/Tuple.scala +++ b/library/src-bootstrapped/scala/Tuple.scala @@ -307,7 +307,7 @@ object Tuple { } } -abstract trait NonEmptyTuple extends Tuple { +sealed trait NonEmptyTuple extends Tuple { import Tuple._ import NonEmptyTuple._ From 4e594eda52e779cab5d2f527e83c1f7f69fca806 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 14 Dec 2018 17:33:11 +0100 Subject: [PATCH 43/56] More tests --- tests/run/tuple-typetests.check | 2 ++ tests/run/tuple-typetests.scala | 10 ++++++++++ 2 files changed, 12 insertions(+) create mode 100644 tests/run/tuple-typetests.check create mode 100644 tests/run/tuple-typetests.scala diff --git a/tests/run/tuple-typetests.check b/tests/run/tuple-typetests.check new file mode 100644 index 000000000000..1d474d525571 --- /dev/null +++ b/tests/run/tuple-typetests.check @@ -0,0 +1,2 @@ +false +true diff --git a/tests/run/tuple-typetests.scala b/tests/run/tuple-typetests.scala new file mode 100644 index 000000000000..c36ad810efe1 --- /dev/null +++ b/tests/run/tuple-typetests.scala @@ -0,0 +1,10 @@ +object Test extends App { + + def nonEmpty(x: Any) = x match { + case _: (h *: t) => true + case () => false + } + + println(nonEmpty(())) + println(nonEmpty(1, 2,3)) +} \ No newline at end of file From 8e98772993a3fa4e237322022cc6ee158b3a646b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 18 Dec 2018 17:30:41 +0100 Subject: [PATCH 44/56] Fix typos --- docs/docs/reference/derivation.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/docs/reference/derivation.md b/docs/docs/reference/derivation.md index e9d9fba1e000..18f3cf2de1fa 100644 --- a/docs/docs/reference/derivation.md +++ b/docs/docs/reference/derivation.md @@ -57,10 +57,10 @@ This is all a user of typeclass derivation has to know. The rest of this page co For every class with a `derives` clause, the compiler computes the shape of that class as a type. For instance, here is the shape type for the `Tree[T]` enum: ```scala -Cases[ +Cases[( Case[Branch[T], (Tree[T], Tree[T])], Case[Leaf[T], T *: Unit] -] +)] ``` Informally, this states that @@ -88,10 +88,10 @@ Case[Labelled[T], (T, String)] ``` And here is the one for `Option[T]`: ```scala -Cases[ +Cases[( Case[Some[T], T *: Unit], Case[None.type, Unit] -] +)] ``` Note that an empty element tuple is represented as type `Unit`. A single-element tuple is represented as `T *: Unit` since there is no direct syntax for such tuples: `(T)` is just `T` in parentheses, not a tuple. @@ -225,7 +225,7 @@ trait Eq[T] { We need to implement a method `Eq.derived` that produces an instance of `Eq[T]` provided there exists evidence of type `Generic[T]`. Here's a possible solution: ```scala - inline def derived[T, S <: Shape](implicit ev: Generic[T]): Eq[T] = new Eq[T] { + inline def derived[T](implicit ev: Generic[T]): Eq[T] = new Eq[T] { def eql(x: T, y: T): Boolean = { val mx = ev.reflect(x) // (1) val my = ev.reflect(y) // (2) @@ -322,9 +322,9 @@ calling the `error` method defined in `scala.compiletime`. **Example:** Here is a slightly polished and compacted version of the code that's generated by inline expansion for the derived `Eq` instance of class `Tree`. ```scala -implicit def derived$Eq[T](implicit elemEq: Eq[T]): Eq[T] = new Eq[Tree[T]] { +implicit def derived$Eq[T](implicit elemEq: Eq[T]): Eq[Tree[T]] = new Eq[Tree[T]] { def eql(x: Tree[T], y: Tree[T]): Boolean = { - val ev = implOf[Generic[Tree[T]]] + val ev = implicitly[Generic[Tree[T]]] val mx = ev.reflect(x) val my = ev.reflect(y) mx.ordinal == my.ordinal && { From 683b93b8a1c653471e40ac4f97fffbe50dc41bff Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 6 Jan 2019 10:37:23 +0100 Subject: [PATCH 45/56] Add derived field to Tasty reflect --- .../tools/dotc/tastyreflect/TreeOpsImpl.scala | 9 +++--- .../src/scala/tasty/reflect/Printers.scala | 30 +++++++++++++------ library/src/scala/tasty/reflect/TreeOps.scala | 5 ++-- .../src/scala/tasty/reflect/TreeUtils.scala | 6 ++-- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala index 69788d1efcb4..4e400c84f3fa 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala @@ -32,6 +32,7 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers private def rhs = cdef.rhs.asInstanceOf[tpd.Template] def constructor(implicit ctx: Context): DefDef = rhs.constr def parents(implicit ctx: Context): List[TermOrTypeTree] = rhs.parents + def derived(implicit ctx: Context): List[TypeTree] = rhs.derived.asInstanceOf[List[TypeTree]] def self(implicit ctx: Context): Option[tpd.ValDef] = optional(rhs.self) def body(implicit ctx: Context): List[Statement] = rhs.body def symbol(implicit ctx: Context): ClassSymbol = cdef.symbol.asClass @@ -250,14 +251,14 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with CoreImpl with Helpers object ClassDef extends ClassDefModule { - def copy(original: ClassDef)(name: String, constr: DefDef, parents: List[TermOrTypeTree], selfOpt: Option[ValDef], body: List[Statement])(implicit ctx: Context): ClassDef = { + def copy(original: ClassDef)(name: String, constr: DefDef, parents: List[TermOrTypeTree], derived: List[TypeTree], selfOpt: Option[ValDef], body: List[Statement])(implicit ctx: Context): ClassDef = { val Trees.TypeDef(_, originalImpl: tpd.Template) = original - tpd.cpy.TypeDef(original)(name.toTypeName, tpd.cpy.Template(originalImpl)(constr, parents, selfOpt.getOrElse(tpd.EmptyValDef), body)) + tpd.cpy.TypeDef(original)(name.toTypeName, tpd.cpy.Template(originalImpl)(constr, parents, derived, selfOpt.getOrElse(tpd.EmptyValDef), body)) } - def unapply(tree: Tree)(implicit ctx: Context): Option[(String, DefDef, List[TermOrTypeTree], Option[ValDef], List[Statement])] = tree match { + def unapply(tree: Tree)(implicit ctx: Context): Option[(String, DefDef, List[TermOrTypeTree], List[TypeTree], Option[ValDef], List[Statement])] = tree match { case Trees.TypeDef(name, impl: tpd.Template) => - Some((name.toString, impl.constr, impl.parents, optional(impl.self), impl.body)) + Some((name.toString, impl.constr, impl.parents, impl.derived.asInstanceOf[List[TypeTree]], optional(impl.self), impl.body)) case _ => None } } diff --git a/library/src/scala/tasty/reflect/Printers.scala b/library/src/scala/tasty/reflect/Printers.scala index 2c6706f248fe..6264cb074fb6 100644 --- a/library/src/scala/tasty/reflect/Printers.scala +++ b/library/src/scala/tasty/reflect/Printers.scala @@ -186,9 +186,10 @@ trait Printers this += "DefDef(\"" += name += "\", " ++= typeParams += ", " +++= paramss += ", " += returnTpt += ", " += rhs += ")" case TypeDef(name, rhs) => this += "TypeDef(\"" += name += "\", " += rhs += ")" - case ClassDef(name, constr, parents, self, body) => + case ClassDef(name, constr, parents, derived, self, body) => this += "ClassDef(\"" += name += "\", " += constr += ", " visitList[TermOrTypeTree](parents, visitTermOrTypeTree) + visitList[TypeTree](derived, visitTypeTree) this += ", " += self += ", " ++= body += ")" case PackageDef(name, owner) => this += "PackageDef(\"" += name += "\", " += owner += ")" @@ -569,7 +570,7 @@ trait Printers this += "." printImportSelectors(selectors) - case IsClassDef(cdef @ ClassDef(name, DefDef(_, targs, argss, _, _), parents, self, stats)) => + case IsClassDef(cdef @ ClassDef(name, DefDef(_, targs, argss, _, _), parents, derived, self, stats)) => printDefAnnotations(cdef) val flags = cdef.symbol.flags @@ -625,6 +626,11 @@ trait Printers } printSeparated(parents1) + if (derived.nonEmpty) { + this += highlightKeyword(" derives ", color) + printTypeTrees(derived, ", ") + } + def keepDefinition(d: Definition): Boolean = { val flags = d.symbol.flags def isCaseClassUnOverridableMethod: Boolean = { @@ -1004,19 +1010,25 @@ trait Printers printSeparated(stats) } - def printTrees(trees: List[Tree], sep: String): Buffer = { - def printSeparated(list: List[Tree]): Unit = list match { + def printList[T](xs: List[T], sep: String, print: T => Buffer): Buffer = { + def printSeparated(list: List[T]): Unit = list match { case Nil => - case x :: Nil => printTree(x) + case x :: Nil => print(x) case x :: xs => - printTree(x) + print(x) this += sep printSeparated(xs) } - printSeparated(trees) + printSeparated(xs) this } + def printTrees(trees: List[Tree], sep: String): Buffer = + printList(trees, sep, printTree) + + def printTypeTrees(trees: List[TypeTree], sep: String): Buffer = + printList(trees, sep, printTypeTree) + def printTypes(trees: List[Type], sep: String): Buffer = { def printSeparated(list: List[Type]): Unit = list match { case Nil => @@ -1201,7 +1213,7 @@ trait Printers val name = arg.name arg.symbol.owner match { case IsDefSymbol(sym) if sym.name == "" => - val ClassDef(_, _, _, _, body) = sym.owner.asClass.tree + val ClassDef(_, _, _, _, _, body) = sym.owner.asClass.tree body.collectFirst { case IsValDef(vdef @ ValDef(`name`, _, _)) if vdef.symbol.flags.is(Flags.ParamAccessor) => if (!vdef.symbol.flags.is(Flags.Local)) { @@ -1552,7 +1564,7 @@ trait Printers def printDefinitionName(sym: Definition): Buffer = sym match { case ValDef(name, _, _) => this += highlightValDef(name, color) case DefDef(name, _, _, _, _) => this += highlightValDef(name, color) - case ClassDef(name, _, _, _, _) => this += highlightTypeDef(name.stripSuffix("$"), color) + case ClassDef(name, _, _, _, _, _) => this += highlightTypeDef(name.stripSuffix("$"), color) case TypeDef(name, _) => this += highlightTypeDef(name, color) case PackageDef(name, _) => this += highlightTypeDef(name, color) } diff --git a/library/src/scala/tasty/reflect/TreeOps.scala b/library/src/scala/tasty/reflect/TreeOps.scala index 5d0a40747907..5708aa545ed5 100644 --- a/library/src/scala/tasty/reflect/TreeOps.scala +++ b/library/src/scala/tasty/reflect/TreeOps.scala @@ -109,13 +109,14 @@ trait TreeOps extends Core { val ClassDef: ClassDefModule abstract class ClassDefModule { // TODO def apply(name: String, constr: DefDef, parents: List[TermOrTypeTree], selfOpt: Option[ValDef], body: List[Statement])(implicit ctx: Context): ClassDef - def copy(original: ClassDef)(name: String, constr: DefDef, parents: List[TermOrTypeTree], selfOpt: Option[ValDef], body: List[Statement])(implicit ctx: Context): ClassDef - def unapply(tree: Tree)(implicit ctx: Context): Option[(String, DefDef, List[TermOrTypeTree], Option[ValDef], List[Statement])] + def copy(original: ClassDef)(name: String, constr: DefDef, parents: List[TermOrTypeTree], derived: List[TypeTree], selfOpt: Option[ValDef], body: List[Statement])(implicit ctx: Context): ClassDef + def unapply(tree: Tree)(implicit ctx: Context): Option[(String, DefDef, List[TermOrTypeTree], List[TypeTree], Option[ValDef], List[Statement])] } trait ClassDefAPI { def constructor(implicit ctx: Context): DefDef def parents(implicit ctx: Context): List[TermOrTypeTree] + def derived(implicit ctx: Context): List[TypeTree] def self(implicit ctx: Context): Option[ValDef] def body(implicit ctx: Context): List[Statement] diff --git a/library/src/scala/tasty/reflect/TreeUtils.scala b/library/src/scala/tasty/reflect/TreeUtils.scala index 5abd2bb78586..9b821f24b202 100644 --- a/library/src/scala/tasty/reflect/TreeUtils.scala +++ b/library/src/scala/tasty/reflect/TreeUtils.scala @@ -77,9 +77,9 @@ trait TreeUtils case IsDefinition(tdef @ TypeDef(_, rhs)) => implicit val ctx = localCtx(tdef) foldTypeTree(x, rhs) - case IsDefinition(cdef @ ClassDef(_, constr, parents, self, body)) => + case IsDefinition(cdef @ ClassDef(_, constr, parents, derived, self, body)) => implicit val ctx = localCtx(cdef) - foldTrees(foldTrees(foldParents(foldTree(x, constr), parents), self), body) + foldTrees(foldTrees(foldTypeTrees(foldParents(foldTree(x, constr), parents), derived), self), body) case Import(expr, selectors) => foldTree(x, expr) case IsPackageClause(clause @ PackageClause(pid, stats)) => @@ -183,7 +183,7 @@ trait TreeUtils implicit val ctx = localCtx(tree) TypeDef.copy(tree)(tree.name, transformTypeOrBoundsTree(tree.rhs)) case IsClassDef(tree) => - ClassDef.copy(tree)(tree.name, tree.constructor, tree.parents, tree.self, tree.body) + ClassDef.copy(tree)(tree.name, tree.constructor, tree.parents, tree.derived, tree.self, tree.body) case IsImport(tree) => Import.copy(tree)(transformTerm(tree.expr), tree.selectors) } From 569f918c5e3248405f9b63164255d4356d179a92 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 6 Jan 2019 10:44:59 +0100 Subject: [PATCH 46/56] Drop failing assertion Cases in a match could be empty in case of errors --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 187399bdaaf6..3e5a93e28589 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -506,7 +506,6 @@ object Trees { /** selector match { cases } */ case class Match[-T >: Untyped] private[ast] (selector: Tree[T], cases: List[CaseDef[T]])(implicit @constructorOnly src: SourceFile) extends TermTree[T] { - assert(cases.nonEmpty) type ThisTree[-T >: Untyped] = Match[T] def isInline = false } From e3f74e3dda9548dc5451db44a94c1619dda06c0f Mon Sep 17 00:00:00 2001 From: Nicolas Stucki Date: Sun, 6 Jan 2019 17:50:40 +0100 Subject: [PATCH 47/56] Add missing comma and update checkfiles --- .../src/scala/tasty/reflect/Printers.scala | 1 + .../tasty-extractors-owners.check | 10 ++-- tests/run/tasty-extractors-2.check | 46 +++++++++---------- 3 files changed, 29 insertions(+), 28 deletions(-) diff --git a/library/src/scala/tasty/reflect/Printers.scala b/library/src/scala/tasty/reflect/Printers.scala index 6264cb074fb6..7ca162e91e80 100644 --- a/library/src/scala/tasty/reflect/Printers.scala +++ b/library/src/scala/tasty/reflect/Printers.scala @@ -189,6 +189,7 @@ trait Printers case ClassDef(name, constr, parents, derived, self, body) => this += "ClassDef(\"" += name += "\", " += constr += ", " visitList[TermOrTypeTree](parents, visitTermOrTypeTree) + this += ", " visitList[TypeTree](derived, visitTypeTree) this += ", " += self += ", " ++= body += ")" case PackageDef(name, owner) => diff --git a/tests/run-custom-args/Yretain-trees/tasty-extractors-owners.check b/tests/run-custom-args/Yretain-trees/tasty-extractors-owners.check index 364296141757..114de84021de 100644 --- a/tests/run-custom-args/Yretain-trees/tasty-extractors-owners.check +++ b/tests/run-custom-args/Yretain-trees/tasty-extractors-owners.check @@ -1,5 +1,5 @@ foo -DefDef("main", Nil, List(List(ValDef("args", TypeTree.Applied(TypeTree.Ident("Array"), List(TypeTree.Ident("String"))), None))), TypeTree.Ident("Unit"), Some(Term.Block(Nil, Term.Inlined(Some(TypeTree.Ident("Macros$")), Nil, Term.Typed(Term.Select(Term.Apply(Term.Apply(Term.TypeApply(Term.Ident("impl"), List(TypeTree.Inferred())), List(Term.Apply(Term.TypeApply(Term.Ident("apply"), List(TypeTree.Inferred())), List(Term.Inlined(None, Nil, Term.Block(List(DefDef("foo", Nil, Nil, TypeTree.Inferred(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Inferred())))), ValDef("foo2", TypeTree.Inferred(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Inferred())))), ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(6))))))), Term.Literal(Constant.Unit()))))))), List(Term.Ident("macroContext"))), "unary_~"), TypeTree.Ident("Unit")))))) +DefDef("main", Nil, List(List(ValDef("args", TypeTree.Applied(TypeTree.Ident("Array"), List(TypeTree.Ident("String"))), None))), TypeTree.Ident("Unit"), Some(Term.Block(Nil, Term.Inlined(Some(TypeTree.Ident("Macros$")), Nil, Term.Typed(Term.Select(Term.Apply(Term.Apply(Term.TypeApply(Term.Ident("impl"), List(TypeTree.Inferred())), List(Term.Apply(Term.TypeApply(Term.Ident("apply"), List(TypeTree.Inferred())), List(Term.Inlined(None, Nil, Term.Block(List(DefDef("foo", Nil, Nil, TypeTree.Inferred(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Inferred())))), ValDef("foo2", TypeTree.Inferred(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Inferred())))), ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(6))))))), Term.Literal(Constant.Unit()))))))), List(Term.Ident("macroContext"))), "unary_~"), TypeTree.Ident("Unit")))))) bar DefDef("foo", Nil, Nil, TypeTree.Inferred(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Inferred())))) @@ -8,7 +8,7 @@ bar2 DefDef("foo", Nil, Nil, TypeTree.Inferred(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Inferred())))) foo2 -DefDef("main", Nil, List(List(ValDef("args", TypeTree.Applied(TypeTree.Ident("Array"), List(TypeTree.Ident("String"))), None))), TypeTree.Ident("Unit"), Some(Term.Block(Nil, Term.Inlined(Some(TypeTree.Ident("Macros$")), Nil, Term.Typed(Term.Select(Term.Apply(Term.Apply(Term.TypeApply(Term.Ident("impl"), List(TypeTree.Inferred())), List(Term.Apply(Term.TypeApply(Term.Ident("apply"), List(TypeTree.Inferred())), List(Term.Inlined(None, Nil, Term.Block(List(DefDef("foo", Nil, Nil, TypeTree.Inferred(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Inferred())))), ValDef("foo2", TypeTree.Inferred(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Inferred())))), ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(6))))))), Term.Literal(Constant.Unit()))))))), List(Term.Ident("macroContext"))), "unary_~"), TypeTree.Ident("Unit")))))) +DefDef("main", Nil, List(List(ValDef("args", TypeTree.Applied(TypeTree.Ident("Array"), List(TypeTree.Ident("String"))), None))), TypeTree.Ident("Unit"), Some(Term.Block(Nil, Term.Inlined(Some(TypeTree.Ident("Macros$")), Nil, Term.Typed(Term.Select(Term.Apply(Term.Apply(Term.TypeApply(Term.Ident("impl"), List(TypeTree.Inferred())), List(Term.Apply(Term.TypeApply(Term.Ident("apply"), List(TypeTree.Inferred())), List(Term.Inlined(None, Nil, Term.Block(List(DefDef("foo", Nil, Nil, TypeTree.Inferred(), Some(Term.Block(List(DefDef("bar", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(1)))), ValDef("bar2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(2))))), Term.Typed(Term.Ident("bar"), TypeTree.Inferred())))), ValDef("foo2", TypeTree.Inferred(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Inferred())))), ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(6))))))), Term.Literal(Constant.Unit()))))))), List(Term.Ident("macroContext"))), "unary_~"), TypeTree.Ident("Unit")))))) baz ValDef("foo2", TypeTree.Inferred(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Inferred())))) @@ -17,11 +17,11 @@ baz2 ValDef("foo2", TypeTree.Inferred(), Some(Term.Block(List(DefDef("baz", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(3)))), ValDef("baz2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(4))))), Term.Typed(Term.Ident("baz"), TypeTree.Inferred())))) -ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(6)))))) +ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(6)))))) b -ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(6)))))) +ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(6)))))) b2 -ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(6)))))) +ClassDef("A", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(TypeDef("B", TypeTree.Ident("Int")), DefDef("b", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(5)))), ValDef("b2", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(6)))))) diff --git a/tests/run/tasty-extractors-2.check b/tests/run/tasty-extractors-2.check index 841acca6ab5c..d0efac019f59 100644 --- a/tests/run/tasty-extractors-2.check +++ b/tests/run/tasty-extractors-2.check @@ -22,10 +22,10 @@ Type.AndType(Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPacka Term.Inlined(None, Nil, Term.Typed(Term.Literal(Constant.Int(1)), TypeTree.Or(TypeTree.Ident("Int"), TypeTree.Ident("String")))) Type.OrType(Type.SymRef(IsClassSymbol(), Type.SymRef(IsPackageSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(<>), NoPrefix())))), Type.SymRef(IsTypeSymbol(), Type.SymRef(IsValSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, Nil)), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, Nil)), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ValDef("Foo", TypeTree.Ident("Foo$"), Some(Term.Apply(Term.Select(Term.New(TypeTree.Ident("Foo$")), ""), Nil))), ClassDef("Foo$", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Some(ValDef("_", TypeTree.Singleton(Term.Ident("Foo")), None)), Nil)), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ValDef("Foo", TypeTree.Ident("Foo$"), Some(Term.Apply(Term.Select(Term.New(TypeTree.Ident("Foo$")), ""), Nil))), ClassDef("Foo$", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, Some(ValDef("_", TypeTree.Singleton(Term.Ident("Foo")), None)), Nil)), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) Term.Inlined(None, Nil, Term.Block(List(TypeDef("Foo", TypeBoundsTree(TypeTree.Inferred(), TypeTree.Inferred()))), Term.Literal(Constant.Unit()))) @@ -37,67 +37,67 @@ Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymb Term.Inlined(None, Nil, Term.Block(List(TypeDef("Foo", TypeBoundsTree(TypeTree.Ident("Null"), TypeTree.Ident("Object")))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(ValDef("a", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(0)))), DefDef("a_=", Nil, List(List(ValDef("x$1", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Literal(Constant.Unit())))))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(ValDef("a", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(0)))), DefDef("a_=", Nil, List(List(ValDef("x$1", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Literal(Constant.Unit())))))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(DefDef("a", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(0))))))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(DefDef("a", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(0))))))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(DefDef("a", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(0))))))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(DefDef("a", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(0))))))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(DefDef("a", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(0))))))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(DefDef("a", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(0))))))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil), TypeTree.Select(Term.Select(Term.Ident("_root_"), "scala"), "Product")), None, List(DefDef("productElementName", Nil, List(List(ValDef("x$1", TypeTree.Select(Term.Select(Term.Ident("_root_"), "scala"), "Int"), None))), TypeTree.Select(Term.Select(Term.Ident("java"), "lang"), "String"), Some(Term.Match(Term.Ident("x$1"), List(CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Apply(Term.Ident("throw"), List(Term.Apply(Term.Select(Term.New(TypeTree.Select(Term.Select(Term.Ident("java"), "lang"), "IndexOutOfBoundsException")), ""), List(Term.Apply(Term.Select(Term.Select(Term.Select(Term.Ident("java"), "lang"), "String"), "valueOf"), List(Term.Ident("x$1")))))))))))), DefDef("copy", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil))), DefDef("hashCode", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Literal(Constant.Int(394005536)))), DefDef("equals", Nil, List(List(ValDef("x$0", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.Apply(Term.Select(Term.This(Some(Id("Foo"))), "eq"), List(Term.TypeApply(Term.Select(Term.Ident("x$0"), "asInstanceOf"), List(TypeTree.Inferred())))), "||"), List(Term.Match(Term.Ident("x$0"), List(CaseDef(Pattern.Bind("x$0", Pattern.TypeTest(TypeTree.Inferred())), None, Term.Literal(Constant.Boolean(true))), CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Literal(Constant.Boolean(false))))))))), DefDef("toString", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Ident("_toString"), List(Term.This(Some(Id("Foo"))))))), DefDef("canEqual", Nil, List(List(ValDef("that", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.TypeApply(Term.Select(Term.Ident("that"), "isInstanceOf"), List(TypeTree.Inferred())))), DefDef("productArity", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(0)))), DefDef("productPrefix", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.String("Foo")))), DefDef("productElement", Nil, List(List(ValDef("n", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Match(Term.Ident("n"), List(CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Apply(Term.Ident("throw"), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), List(Term.Apply(Term.Select(Term.Ident("n"), "toString"), Nil)))))))))))), ValDef("Foo", TypeTree.Ident("Foo$"), Some(Term.Apply(Term.Select(Term.New(TypeTree.Ident("Foo$")), ""), Nil))), ClassDef("Foo$", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil), TypeTree.Applied(TypeTree.Inferred(), List(TypeTree.Inferred()))), Some(ValDef("_", TypeTree.Singleton(Term.Ident("Foo")), None)), List(DefDef("apply", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil))), DefDef("unapply", Nil, List(List(ValDef("x$1", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Literal(Constant.Boolean(true))))))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil), TypeTree.Select(Term.Select(Term.Ident("_root_"), "scala"), "Product")), Nil, None, List(DefDef("productElementName", Nil, List(List(ValDef("x$1", TypeTree.Select(Term.Select(Term.Ident("_root_"), "scala"), "Int"), None))), TypeTree.Select(Term.Select(Term.Ident("java"), "lang"), "String"), Some(Term.Match(Term.Ident("x$1"), List(CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Apply(Term.Ident("throw"), List(Term.Apply(Term.Select(Term.New(TypeTree.Select(Term.Select(Term.Ident("java"), "lang"), "IndexOutOfBoundsException")), ""), List(Term.Apply(Term.Select(Term.Select(Term.Select(Term.Ident("java"), "lang"), "String"), "valueOf"), List(Term.Ident("x$1")))))))))))), DefDef("copy", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil))), DefDef("hashCode", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Literal(Constant.Int(394005536)))), DefDef("equals", Nil, List(List(ValDef("x$0", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.Apply(Term.Select(Term.This(Some(Id("Foo"))), "eq"), List(Term.TypeApply(Term.Select(Term.Ident("x$0"), "asInstanceOf"), List(TypeTree.Inferred())))), "||"), List(Term.Match(Term.Ident("x$0"), List(CaseDef(Pattern.Bind("x$0", Pattern.TypeTest(TypeTree.Inferred())), None, Term.Literal(Constant.Boolean(true))), CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Literal(Constant.Boolean(false))))))))), DefDef("toString", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Ident("_toString"), List(Term.This(Some(Id("Foo"))))))), DefDef("canEqual", Nil, List(List(ValDef("that", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.TypeApply(Term.Select(Term.Ident("that"), "isInstanceOf"), List(TypeTree.Inferred())))), DefDef("productArity", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(0)))), DefDef("productPrefix", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.String("Foo")))), DefDef("productElement", Nil, List(List(ValDef("n", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Match(Term.Ident("n"), List(CaseDef(Pattern.Value(Term.Ident("_")), None, Term.Apply(Term.Ident("throw"), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), List(Term.Apply(Term.Select(Term.Ident("n"), "toString"), Nil)))))))))))), ValDef("Foo", TypeTree.Ident("Foo$"), Some(Term.Apply(Term.Select(Term.New(TypeTree.Ident("Foo$")), ""), Nil))), ClassDef("Foo$", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil), TypeTree.Applied(TypeTree.Inferred(), List(TypeTree.Inferred()))), Nil, Some(ValDef("_", TypeTree.Singleton(Term.Ident("Foo")), None)), List(DefDef("apply", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil))), DefDef("unapply", Nil, List(List(ValDef("x$1", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Literal(Constant.Boolean(true))))))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo1", DefDef("", Nil, List(List(ValDef("a", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(ValDef("a", TypeTree.Inferred(), None)))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo1", DefDef("", Nil, List(List(ValDef("a", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(ValDef("a", TypeTree.Inferred(), None)))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo2", DefDef("", Nil, List(List(ValDef("b", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(ValDef("b", TypeTree.Inferred(), None)))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo2", DefDef("", Nil, List(List(ValDef("b", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(ValDef("b", TypeTree.Inferred(), None)))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo3", DefDef("", Nil, List(List(ValDef("a", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(ValDef("a", TypeTree.Inferred(), None))), ValDef("Foo3", TypeTree.Ident("Foo3$"), Some(Term.Apply(Term.Select(Term.New(TypeTree.Ident("Foo3$")), ""), Nil))), ClassDef("Foo3$", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Some(ValDef("_", TypeTree.Singleton(Term.Ident("Foo3")), None)), List(DefDef("$lessinit$greater$default$1", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(5))))))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo3", DefDef("", Nil, List(List(ValDef("a", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(ValDef("a", TypeTree.Inferred(), None))), ValDef("Foo3", TypeTree.Ident("Foo3$"), Some(Term.Apply(Term.Select(Term.New(TypeTree.Ident("Foo3$")), ""), Nil))), ClassDef("Foo3$", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, Some(ValDef("_", TypeTree.Singleton(Term.Ident("Foo3")), None)), List(DefDef("$lessinit$greater$default$1", Nil, Nil, TypeTree.Inferred(), Some(Term.Literal(Constant.Int(5))))))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo4", DefDef("", Nil, List(List(ValDef("a", TypeTree.Ident("Int"), None)), List(ValDef("b", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(ValDef("a", TypeTree.Inferred(), None), ValDef("b", TypeTree.Inferred(), None)))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo4", DefDef("", Nil, List(List(ValDef("a", TypeTree.Ident("Int"), None)), List(ValDef("b", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(ValDef("a", TypeTree.Inferred(), None), ValDef("b", TypeTree.Inferred(), None)))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo5", DefDef("", Nil, List(List(ValDef("a", TypeTree.Ident("Int"), None)), List(ValDef("b", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(ValDef("a", TypeTree.Inferred(), None), ValDef("b", TypeTree.Inferred(), None))), ValDef("Foo5", TypeTree.Ident("Foo5$"), Some(Term.Apply(Term.Select(Term.New(TypeTree.Ident("Foo5$")), ""), Nil))), ClassDef("Foo5$", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Some(ValDef("_", TypeTree.Singleton(Term.Ident("Foo5")), None)), List(DefDef("$lessinit$greater$default$2", Nil, List(List(ValDef("a", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), Some(Term.Ident("a")))))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo5", DefDef("", Nil, List(List(ValDef("a", TypeTree.Ident("Int"), None)), List(ValDef("b", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(ValDef("a", TypeTree.Inferred(), None), ValDef("b", TypeTree.Inferred(), None))), ValDef("Foo5", TypeTree.Ident("Foo5$"), Some(Term.Apply(Term.Select(Term.New(TypeTree.Ident("Foo5$")), ""), Nil))), ClassDef("Foo5$", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, Some(ValDef("_", TypeTree.Singleton(Term.Ident("Foo5")), None)), List(DefDef("$lessinit$greater$default$2", Nil, List(List(ValDef("a", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), Some(Term.Ident("a")))))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo6", DefDef("", Nil, List(List(ValDef("a", TypeTree.Ident("Int"), None)), List(ValDef("b", TypeTree.Singleton(Term.Ident("a")), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(ValDef("a", TypeTree.Inferred(), None), ValDef("b", TypeTree.Inferred(), None)))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo6", DefDef("", Nil, List(List(ValDef("a", TypeTree.Ident("Int"), None)), List(ValDef("b", TypeTree.Singleton(Term.Ident("a")), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(ValDef("a", TypeTree.Inferred(), None), ValDef("b", TypeTree.Inferred(), None)))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo7", DefDef("", Nil, List(List(ValDef("a", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(ValDef("a", TypeTree.Inferred(), None), DefDef("", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Block(List(Term.Apply(Term.Select(Term.This(Some(Id("Foo7"))), ""), List(Term.Literal(Constant.Int(6))))), Term.Literal(Constant.Unit()))))))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo7", DefDef("", Nil, List(List(ValDef("a", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(ValDef("a", TypeTree.Inferred(), None), DefDef("", Nil, List(Nil), TypeTree.Inferred(), Some(Term.Block(List(Term.Apply(Term.Select(Term.This(Some(Id("Foo7"))), ""), List(Term.Literal(Constant.Int(6))))), Term.Literal(Constant.Unit()))))))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo8", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(Term.Apply(Term.Ident("println"), List(Term.Literal(Constant.Int(0))))))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo8", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(Term.Apply(Term.Ident("println"), List(Term.Literal(Constant.Int(0))))))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo10", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(ValDef("a", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(9))))))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo10", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(ValDef("a", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(9))))))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo11", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(ValDef("a", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(10)))), DefDef("a_=", Nil, List(List(ValDef("x$1", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Literal(Constant.Unit())))))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo11", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(ValDef("a", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(10)))), DefDef("a_=", Nil, List(List(ValDef("x$1", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Literal(Constant.Unit())))))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo12", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(ValDef("a", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(11))))))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo12", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(ValDef("a", TypeTree.Inferred(), Some(Term.Literal(Constant.Int(11))))))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, Nil), ClassDef("Bar", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Ident("Foo")), ""), Nil)), None, Nil)), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, Nil), ClassDef("Bar", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Ident("Foo")), ""), Nil)), Nil, None, Nil)), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo2", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(TypeTree.Inferred()), None, Nil), ClassDef("Bar", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil), TypeTree.Ident("Foo2")), None, Nil)), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo2", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(TypeTree.Inferred()), Nil, None, Nil), ClassDef("Bar", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil), TypeTree.Ident("Foo2")), Nil, None, Nil)), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(List(ValDef("i", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(ValDef("i", TypeTree.Inferred(), None))), ClassDef("Bar", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Ident("Foo")), ""), List(Term.Literal(Constant.Int(1))))), None, Nil)), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(List(ValDef("i", TypeTree.Ident("Int"), None))), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(ValDef("i", TypeTree.Inferred(), None))), ClassDef("Bar", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Ident("Foo")), ""), List(Term.Literal(Constant.Int(1))))), Nil, None, Nil)), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(TypeDef("X", TypeTree.Ident("Int")))), DefDef("f", Nil, List(List(ValDef("a", TypeTree.Ident("Foo"), None))), TypeTree.Select(Term.Ident("a"), "X"), Some(Term.Ident("???")))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(TypeDef("X", TypeTree.Ident("Int")))), DefDef("f", Nil, List(List(ValDef("a", TypeTree.Ident("Foo"), None))), TypeTree.Select(Term.Ident("a"), "X"), Some(Term.Ident("???")))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) -Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(TypeDef("X", TypeBoundsTree(TypeTree.Inferred(), TypeTree.Inferred())))), DefDef("f", Nil, List(List(ValDef("a", TypeTree.Refined(TypeTree.Ident("Foo"), List(TypeDef("X", TypeTree.Ident("Int")))), None))), TypeTree.Select(Term.Ident("a"), "X"), Some(Term.Ident("???")))), Term.Literal(Constant.Unit()))) +Term.Inlined(None, Nil, Term.Block(List(ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(TypeDef("X", TypeBoundsTree(TypeTree.Inferred(), TypeTree.Inferred())))), DefDef("f", Nil, List(List(ValDef("a", TypeTree.Refined(TypeTree.Ident("Foo"), List(TypeDef("X", TypeTree.Ident("Int")))), None))), TypeTree.Select(Term.Ident("a"), "X"), Some(Term.Ident("???")))), Term.Literal(Constant.Unit()))) Type.SymRef(IsClassSymbol(), Type.ThisType(Type.SymRef(IsPackageSymbol(), NoPrefix()))) Term.Inlined(None, Nil, Term.Block(List(ValDef("lambda", TypeTree.Applied(TypeTree.Inferred(), List(TypeTree.Ident("Int"), TypeTree.Ident("Int"))), Some(Term.Block(List(DefDef("$anonfun", Nil, List(List(ValDef("x", TypeTree.Inferred(), None))), TypeTree.Inferred(), Some(Term.Ident("x")))), Term.Lambda(Term.Ident("$anonfun"), None))))), Term.Literal(Constant.Unit()))) From d9be6e2c8eeaf50bb91ff0d5e22496b81891207d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 7 Jan 2019 18:19:47 +0100 Subject: [PATCH 48/56] Update another check file --- tests/run-with-compiler/tasty-consumer.check | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-with-compiler/tasty-consumer.check b/tests/run-with-compiler/tasty-consumer.check index 8140980ca00d..1c5321918586 100644 --- a/tests/run-with-compiler/tasty-consumer.check +++ b/tests/run-with-compiler/tasty-consumer.check @@ -1,4 +1,4 @@ -ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), None, List(ValDef("foo", TypeTree.Ident("Int"), Some(Term.Literal(Constant.Int(2)))), DefDef("bar", Nil, List(List(ValDef("i", TypeTree.Ident("Int"), None))), TypeTree.Ident("Int"), Some(Term.Literal(Constant.Int(3)))))) +ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil))Nil, None, List(ValDef("foo", TypeTree.Ident("Int"), Some(Term.Literal(Constant.Int(2)))), DefDef("bar", Nil, List(List(ValDef("i", TypeTree.Ident("Int"), None))), TypeTree.Ident("Int"), Some(Term.Literal(Constant.Int(3)))))) DefDef("", Nil, List(Nil), TypeTree.Inferred(), None) ValDef("foo", TypeTree.Ident("Int"), Some(Term.Literal(Constant.Int(2)))) DefDef("bar", Nil, List(List(ValDef("i", TypeTree.Ident("Int"), None))), TypeTree.Ident("Int"), Some(Term.Literal(Constant.Int(3)))) From caece0e5fc52895ebcf50e84cf234ca4f46b0162 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 7 Jan 2019 19:04:31 +0100 Subject: [PATCH 49/56] Fix rebase breakage --- tests/pos/i5574.scala | 2 +- tests/pos/inline-match-gadt-nested.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/pos/i5574.scala b/tests/pos/i5574.scala index 85ec4e9121b2..9c360f8b81c8 100644 --- a/tests/pos/i5574.scala +++ b/tests/pos/i5574.scala @@ -1,4 +1,4 @@ -import scala.typelevel._ +import scala.compiletime._ object i5574 { class Box[F[_]] diff --git a/tests/pos/inline-match-gadt-nested.scala b/tests/pos/inline-match-gadt-nested.scala index 2570e7f898fe..1f8cc2b8233e 100644 --- a/tests/pos/inline-match-gadt-nested.scala +++ b/tests/pos/inline-match-gadt-nested.scala @@ -1,5 +1,5 @@ object `inline-match-gadt-nested` { - import scala.typelevel._ + import scala.compiletime._ enum Gadt[A, B] { case Nested(gadt: Gadt[A, Int]) extends Gadt[A, Int] From 9d87241e3796679938a34ce4f6f566519e7a5e0d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 7 Jan 2019 20:41:38 +0100 Subject: [PATCH 50/56] Fix test --- tests/pos/implicit-match-and-inline-match.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/pos/implicit-match-and-inline-match.scala b/tests/pos/implicit-match-and-inline-match.scala index 987b04ed81b0..fb76d2f64338 100644 --- a/tests/pos/implicit-match-and-inline-match.scala +++ b/tests/pos/implicit-match-and-inline-match.scala @@ -1,5 +1,5 @@ object `implicit-match-and-inline-match` { - import scala.typelevel._ + import scala.compiletime._ case class Box[T](value: T) implicit val ibox: Box[Int] = Box(0) From 248971b01eb7dbee2ee1072b054bbaf23e034688 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 7 Jan 2019 21:17:39 +0100 Subject: [PATCH 51/56] Another check file fix --- tests/run-with-compiler/tasty-consumer.check | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/run-with-compiler/tasty-consumer.check b/tests/run-with-compiler/tasty-consumer.check index 1c5321918586..b73593445b16 100644 --- a/tests/run-with-compiler/tasty-consumer.check +++ b/tests/run-with-compiler/tasty-consumer.check @@ -1,4 +1,4 @@ -ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil))Nil, None, List(ValDef("foo", TypeTree.Ident("Int"), Some(Term.Literal(Constant.Int(2)))), DefDef("bar", Nil, List(List(ValDef("i", TypeTree.Ident("Int"), None))), TypeTree.Ident("Int"), Some(Term.Literal(Constant.Int(3)))))) +ClassDef("Foo", DefDef("", Nil, List(Nil), TypeTree.Inferred(), None), List(Term.Apply(Term.Select(Term.New(TypeTree.Inferred()), ""), Nil)), Nil, None, List(ValDef("foo", TypeTree.Ident("Int"), Some(Term.Literal(Constant.Int(2)))), DefDef("bar", Nil, List(List(ValDef("i", TypeTree.Ident("Int"), None))), TypeTree.Ident("Int"), Some(Term.Literal(Constant.Int(3)))))) DefDef("", Nil, List(Nil), TypeTree.Inferred(), None) ValDef("foo", TypeTree.Ident("Int"), Some(Term.Literal(Constant.Int(2)))) DefDef("bar", Nil, List(List(ValDef("i", TypeTree.Ident("Int"), None))), TypeTree.Ident("Int"), Some(Term.Literal(Constant.Int(3)))) From 49901fe456573c046bbdcbbed65460cf3a78a70f Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Tue, 15 Jan 2019 15:06:40 +0100 Subject: [PATCH 52/56] Fix typos in documentation --- docs/docs/reference/derivation.md | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/docs/docs/reference/derivation.md b/docs/docs/reference/derivation.md index 18f3cf2de1fa..7439be868cc4 100644 --- a/docs/docs/reference/derivation.md +++ b/docs/docs/reference/derivation.md @@ -176,7 +176,7 @@ class Mirror(val adtClass: GenericClass, val ordinal: Int, val elems: Product) { def caseLabel: String = adtClass.label(ordinal)(0) /** The label of the `n`'th element of the case reflected by this mirror */ - def elementLabel(n: Int) = adtClass.label(ordinal)(n + 1) + def elementLabel(n: Int): String = adtClass.label(ordinal)(n + 1) } ``` @@ -207,6 +207,7 @@ class GenericClass(val runtimeClass: Class[_], labelsStr: String) { * Each row of the array contains a case label, followed by the labels of the elements of that case. */ val label: Array[Array[String]] = ... +} ``` The class provides four overloaded methods to create mirrors. The first of these is invoked by the `reify` method that maps an ADT instance to its mirror. It simply passes the @@ -216,7 +217,7 @@ a mirror over that array, and finally uses the `reify` method in `Reflected` to ### How to Write Generic Typeclasses Based on the machinery developed so far it becomes possible to define type classes generically. This means that the `derived` method will compute a type class instance for any ADT that has a `Generic` instance, recursively. -The implementation of these methods typically uses three new typelevel constructs in Dotty: inline methods, inline matches and implicit matches. As an example, here is one possible implementation of a generic `Eq` type class, with explanations. Let's assume `Eq` is defined by the following trait: +The implementation of these methods typically uses three new type-level constructs in Dotty: inline methods, inline matches, and implicit matches. As an example, here is one possible implementation of a generic `Eq` type class, with explanations. Let's assume `Eq` is defined by the following trait: ```scala trait Eq[T] { def eql(x: T, y: T): Boolean @@ -239,11 +240,11 @@ there exists evidence of type `Generic[T]`. Here's a possible solution: } } ``` -The implementation of the inline method `derived` creates an instance of `Eq[T]` and implements its `eql` method. The right hand side of `eql` mixes compile-time and runtime elements. In the code above, runtime elements are marked with a number in parentheses, i.e +The implementation of the inline method `derived` creates an instance of `Eq[T]` and implements its `eql` method. The right-hand side of `eql` mixes compile-time and runtime elements. In the code above, runtime elements are marked with a number in parentheses, i.e `(1)`, `(2)`, `(3)`. Compile-time calls that expand to runtime code are marked with a number in brackets, i.e. `[4]`, `[5]`. The implementation of `eql` consists of the following steps. 1. Map the compared values `x` and `y` to their mirrors using the `reflect` method of the implicitly passed `Generic` evidence `(1)`, `(2)`. - 2. Match at compile-time against the shape of the ADT given in `ev.Shape`. Dotty does not have a construct for matching types directly, buy we can emulate it using an `inline` match over an `erasedValue`. Depending on the actual type `ev.Shape`, the match will reduce at compile time to one of its two alternatives. + 2. Match at compile-time against the shape of the ADT given in `ev.Shape`. Dotty does not have a construct for matching types directly, but we can emulate it using an `inline` match over an `erasedValue`. Depending on the actual type `ev.Shape`, the match will reduce at compile time to one of its two alternatives. 3. If `ev.Shape` is of the form `Cases[alts]` for some tuple `alts` of alternative types, the equality test consists of comparing the ordinal values of the two mirrors `(3)` and, if they are equal, comparing the elements of the case indicated by that ordinal value. That second step is performed by code that results from the compile-time expansion of the `eqlCases` call `[4]`. 4. If `ev.Shape` is of the form `Case[elems]` for some tuple `elems` for element types, the elements of the case are compared by code that results from the compile-time expansion of the `eqlElems` call `[5]`. @@ -260,7 +261,7 @@ Here is a possible implementation of `eqlCases`: throw new MatchError(mx.ordinal) // (9) } ``` -The inline method `eqlCases` takes as type arguments the alternatives of the ADT that remain to be tested. It takes as value arguments mirrors of the two instances `x` and `y` to be compared and an integer `n` that indicates the ordinal number of the case that is tested next. Its produces an expression that compares these two values. +The inline method `eqlCases` takes as type arguments the alternatives of the ADT that remain to be tested. It takes as value arguments mirrors of the two instances `x` and `y` to be compared and an integer `n` that indicates the ordinal number of the case that is tested next. It produces an expression that compares these two values. If the list of alternatives `Alts` consists of a case of type `Case[_, elems]`, possibly followed by further cases in `alts1`, we generate the following code: @@ -291,7 +292,7 @@ implementation: true // (14) } ``` -`eqlElems` takes as arguments the two mirrors of the elements to compare and a compile-time index `n`, indicating the index of the next element to test. It is defined in terms of an another compile-time match, this time over the tuple type `Elems` of all element types that remain to be tested. If that type is +`eqlElems` takes as arguments the two mirrors of the elements to compare and a compile-time index `n`, indicating the index of the next element to test. It is defined in terms of another compile-time match, this time over the tuple type `Elems` of all element types that remain to be tested. If that type is non-empty, say of form `elem *: elems1`, the following code is produced: 1. Access the `n`'th elements of both mirrors and cast them to the current element type `elem` @@ -314,10 +315,10 @@ The last, and in a sense most interesting part of the derivation is the comparis } ``` `tryEql` is an inline method that takes an element type `T` and two element values of that type as arguments. It is defined using an `inline match` that tries to find an implicit instance of `Eq[T]`. If an instance `ev` is found, it proceeds by comparing the arguments using `ev.eql`. On the other hand, if no instance is found -this signals a compilation error: the user tried a generic derivation of `Eq` for a class with an element type that does not support an `Eq` instance itself. The error is signalled by +this signals a compilation error: the user tried a generic derivation of `Eq` for a class with an element type that does not support an `Eq` instance itself. The error is signaled by calling the `error` method defined in `scala.compiletime`. -**Note:** At the moment our error diagnostics for meta programming does not support yet interpolated string arguments for the `scala.compiletime.error` method that is called in the second case above. As an alternative, one can simply leave off the second case, then a missing typeclass would result in a "failure to reduce match" error. +**Note:** At the moment our error diagnostics for metaprogramming does not support yet interpolated string arguments for the `scala.compiletime.error` method that is called in the second case above. As an alternative, one can simply leave off the second case, then a missing typeclass would result in a "failure to reduce match" error. **Example:** Here is a slightly polished and compacted version of the code that's generated by inline expansion for the derived `Eq` instance of class `Tree`. @@ -346,7 +347,7 @@ One important difference between this approach and Scala-2 typeclass derivation ### Derived Instances Elsewhere Sometimes one would like to derive a typeclass instance for an ADT after the ADT is defined, without being able to change the code of the ADT itself. -To do this, simply define an instance with the `derived` method of the typeclass as right hand side. E.g, to implement `Ordering` for `Option`, define: +To do this, simply define an instance with the `derived` method of the typeclass as right-hand side. E.g, to implement `Ordering` for `Option`, define: ```scala implicit def myOptionOrdering[T: Ordering]: Ordering[Option[T]] = Ordering.derived ``` @@ -388,4 +389,4 @@ are correctly written these casts will never fail. It could make sense to explore a higher-level framework that encapsulates all casts in the framework. This could give more guidance to the typeclass implementer. It also seems quite possible to put such a framework on top of the lower-level -mechanisms presented here. \ No newline at end of file +mechanisms presented here. From 4cd81babf67ad7d0f4bb6b53452186ae69a5ea1a Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Tue, 15 Jan 2019 17:18:50 +0100 Subject: [PATCH 53/56] Fix spacing and add return types --- .../src/dotty/tools/dotc/typer/Deriving.scala | 44 +++++++++---------- .../dotty/tools/dotc/typer/Implicits.scala | 12 ++--- .../src/dotty/tools/dotc/typer/Namer.scala | 12 ++--- docs/docs/internals/syntax.md | 2 + library/src-bootstrapped/scala/Tuple.scala | 2 +- .../scala/reflect/Generic.scala | 4 +- .../scala/reflect/Mirror.scala | 4 +- 7 files changed, 41 insertions(+), 39 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index 89c40d13e392..e34052dea2bc 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -32,7 +32,7 @@ trait Deriving { this: Typer => private var synthetics = new mutable.ListBuffer[Symbol] /** the children of `cls` ordered by textual occurrence */ - lazy val children = cls.children + lazy val children: List[Symbol] = cls.children private def shapeError(explanation: => String): Unit = ctx.error(i"cannot take shape of $cls\n$explanation", codePos) @@ -115,7 +115,7 @@ trait Deriving { this: Typer => /** Create a synthetic symbol owned by current owner */ private def newSymbol(name: Name, info: Type, pos: Position = ctx.owner.pos, - flags: FlagSet = EmptyFlags)(implicit ctx: Context) = + flags: FlagSet = EmptyFlags)(implicit ctx: Context): Symbol = ctx.newSymbol(ctx.owner, name, flags | Synthetic, info, coord = pos) /** Create a synthetic method owned by current owner */ @@ -145,24 +145,24 @@ trait Deriving { this: Typer => else add(newMethod(instanceName, info, pos, Implicit)) } - /* Check derived type tree `derived` for the following well-formedness conditions: - * (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix) - * (2) It must have exactly one type parameter - * If it passes the checks, enter a typeclass instance for it in the current scope. - * Given - * - * class C[Ts] .... derives ... D ... - * - * where `T_1, ..., T_n` are the first-kinded type parameters in `Ts`, - * the typeclass instance has the form - * - * implicit def derived$D(implicit ev_1: D[T1], ..., ev_n: D[T_n]): D[C[Ts]] = D.derived - * - * See test run/typeclass-derivation2 for examples that spell out what would be generated. - * Note that the name of the derived method containd the name in the derives clause, not - * the underlying class name. This allows one to disambiguate derivations of type classes - * that have the same name but different prefixes through selective aliasing. - */ + /** Check derived type tree `derived` for the following well-formedness conditions: + * (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix) + * (2) It must have exactly one type parameter + * If it passes the checks, enter a typeclass instance for it in the current scope. + * Given + * + * class C[Ts] .... derives ... D ... + * + * where `T_1, ..., T_n` are the first-kinded type parameters in `Ts`, + * the typeclass instance has the form + * + * implicit def derived$D(implicit ev_1: D[T_1], ..., ev_n: D[T_n]): D[C[Ts]] = D.derived + * + * See test run/typeclass-derivation2 for examples that spell out what would be generated. + * Note that the name of the derived method containd the name in the derives clause, not + * the underlying class name. This allows one to disambiguate derivations of type classes + * that have the same name but different prefixes through selective aliasing. + */ private def processDerivedInstance(derived: untpd.Tree): Unit = { val originalType = typedAheadType(derived, AnyTypeConstructorProto).tpe val underlyingType = underlyingClassRef(originalType) @@ -237,7 +237,7 @@ trait Deriving { this: Typer => } /** Extractor for the `pattern` and `elements` in a `Shaped.Case(pattern, elements)` shape */ - private object ShapeCase { + private object ShapeCase { def unapply(shape: Type): Option[(Type, List[Type])] = shape match { case AppliedType(fn, pat :: elems :: Nil) if fn.classSymbol == defn.ShapeCaseClass => Some((pat, tupleElems(elems))) @@ -383,7 +383,7 @@ trait Deriving { this: Typer => } /** The type class instance definition with symbol `sym` */ - private def typeclassInstance(sym: Symbol)(implicit ctx: Context) = + private def typeclassInstance(sym: Symbol)(implicit ctx: Context): List[Type] => (List[List[tpd.Tree]] => tpd.Tree) = (tparamRefs: List[Type]) => (paramRefss: List[List[tpd.Tree]]) => { val tparams = tparamRefs.map(_.typeSymbol.asType) val params = if (paramRefss.isEmpty) Nil else paramRefss.head.map(_.symbol.asTerm) diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 250aba998fd7..095fecab2072 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -155,7 +155,7 @@ object Implicits { def valueTypeCandidateKind(tpw: Type): Candidate.Kind = tpw.stripPoly match { case tpw: MethodType => - if (tpw.isImplicitMethod) Candidate.Value else Candidate.None + if (tpw.isImplicitMethod) Candidate.Value else Candidate.None case _ => Candidate.Value } @@ -195,7 +195,7 @@ object Implicits { else ref val refNorm = normalize(refAdjusted, pt) if (!NoViewsAllowed.isCompatible(refNorm, ptNorm)) - ckind = Candidate.None + ckind = Candidate.None } ckind } @@ -384,10 +384,10 @@ object Implicits { class NoMatchingImplicits(val expectedType: Type, val argument: Tree, constraint: Constraint = OrderingConstraint.empty) extends SearchFailureType { - /** Replace all type parameters in constraint by their bounds, to make it clearer - * what was expected - */ - override def clarify(tp: Type)(implicit ctx: Context) = { + /** Replace all type parameters in constraint by their bounds, to make it clearer + * what was expected + */ + override def clarify(tp: Type)(implicit ctx: Context): Type = { val map = new TypeMap { def apply(t: Type): Type = t match { case t: TypeParamRef => diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 8c95276c7cf7..cc32ae39d81f 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -841,7 +841,7 @@ class Namer { typer: Typer => else if (!cls.is(ChildrenQueried)) addChild(cls, child) else - ctx.error(em"""children of ${cls} were already queried before $sym was discovered. + ctx.error(em"""children of $cls were already queried before $sym was discovered. |As a remedy, you could move $sym on the same nesting level as $cls.""", child.pos) } @@ -947,11 +947,11 @@ class Namer { typer: Typer => } } - /* Check parent type tree `parent` for the following well-formedness conditions: - * (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix) - * (2) If may not derive from itself - * (3) The class is not final - * (4) If the class is sealed, it is defined in the same compilation unit as the current class + /** Check parent type tree `parent` for the following well-formedness conditions: + * (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix) + * (2) If may not derive from itself + * (3) The class is not final + * (4) If the class is sealed, it is defined in the same compilation unit as the current class */ def checkedParentType(parent: untpd.Tree): Type = { val ptype = parentType(parent)(ctx.superCallContext).dealiasKeepAnnots diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index e2a22dd53040..8d16b95b48a2 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -101,8 +101,10 @@ true try type val var while with yield ### Soft keywords +``` derives inline opaque ~ * | & + - +``` ## Context-free Syntax diff --git a/library/src-bootstrapped/scala/Tuple.scala b/library/src-bootstrapped/scala/Tuple.scala index dfd35827e1ed..de9fcfbc72db 100644 --- a/library/src-bootstrapped/scala/Tuple.scala +++ b/library/src-bootstrapped/scala/Tuple.scala @@ -473,7 +473,7 @@ object NonEmptyTuple { } @showAsInfix -sealed class *:[+H, +T <: Tuple] extends Object with NonEmptyTuple +sealed class *:[+H, +T <: Tuple] extends Object with NonEmptyTuple object *: { inline def unapply[H, T <: Tuple](x: H *: T) = (x.head, x.tail) diff --git a/library/src-bootstrapped/scala/reflect/Generic.scala b/library/src-bootstrapped/scala/reflect/Generic.scala index 24be8a686322..6b0ed8967774 100644 --- a/library/src-bootstrapped/scala/reflect/Generic.scala +++ b/library/src-bootstrapped/scala/reflect/Generic.scala @@ -1,8 +1,8 @@ package scala.reflect /** A class for mapping between an ADT value and - * the case mirror that represents the value. - */ + * the case mirror that represents the value. + */ abstract class Generic[T] { type Shape <: scala.compiletime.Shape diff --git a/library/src-bootstrapped/scala/reflect/Mirror.scala b/library/src-bootstrapped/scala/reflect/Mirror.scala index 37afde30b118..0b2aed2c9645 100644 --- a/library/src-bootstrapped/scala/reflect/Mirror.scala +++ b/library/src-bootstrapped/scala/reflect/Mirror.scala @@ -1,7 +1,7 @@ package scala.reflect /** A generic representation of a case in an ADT - * @param reflected The common class-speficic part of this mirror + * @param reflected The common class-specific part of this mirror * @param ordinal The ordinal value of the case in the list of the ADT's cases * @param elems The elements of the case */ @@ -14,5 +14,5 @@ class Mirror(val adtClass: GenericClass, val ordinal: Int, val elems: Product) { def caseLabel: String = adtClass.label(ordinal)(0) /** The label of the `n`'th element of the case reflected by this mirror */ - def elementLabel(n: Int) = adtClass.label(ordinal)(n + 1) + def elementLabel(n: Int): String = adtClass.label(ordinal)(n + 1) } From 296958e607a1c2f59428b893ac0040c531cb1abb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 16 Jan 2019 10:51:56 +0100 Subject: [PATCH 54/56] Fix failing fuzzy test --- compiler/src/dotty/tools/dotc/typer/Namer.scala | 2 +- library/src-bootstrapped/scala/Tuple.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index cc32ae39d81f..4e99a7bc538d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -505,7 +505,7 @@ class Namer { typer: Typer => * @pre `child` must have a position. */ final def addChild(cls: Symbol, child: Symbol)(implicit ctx: Context): Unit = { - val childStart = child.pos.start + val childStart = if (child.pos.exists) child.pos.start else -1 def insertInto(annots: List[Annotation]): List[Annotation] = annots.find(_.symbol == defn.ChildAnnot) match { case Some(Annotation.Child(other)) if other.pos.exists && childStart <= other.pos.start => diff --git a/library/src-bootstrapped/scala/Tuple.scala b/library/src-bootstrapped/scala/Tuple.scala index de9fcfbc72db..07b524050612 100644 --- a/library/src-bootstrapped/scala/Tuple.scala +++ b/library/src-bootstrapped/scala/Tuple.scala @@ -473,7 +473,7 @@ object NonEmptyTuple { } @showAsInfix -sealed class *:[+H, +T <: Tuple] extends Object with NonEmptyTuple +sealed class *:[+H, +T <: Tuple] extends NonEmptyTuple object *: { inline def unapply[H, T <: Tuple](x: H *: T) = (x.head, x.tail) From bbb54cb95919173f0ad6843276a251554fed280b Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 19 Jan 2019 15:35:10 +0100 Subject: [PATCH 55/56] Fix rebase breakage --- .../src/dotty/tools/dotc/ast/Desugar.scala | 6 ++-- compiler/src/dotty/tools/dotc/ast/untpd.scala | 2 +- .../dotty/tools/dotc/config/Printers.scala | 2 +- .../dotty/tools/dotc/parsing/Scanners.scala | 7 +++-- .../dotty/tools/dotc/typer/Applications.scala | 2 +- .../src/dotty/tools/dotc/typer/Checking.scala | 16 +++++----- .../src/dotty/tools/dotc/typer/Deriving.scala | 31 ++++++++++--------- .../dotty/tools/dotc/typer/Implicits.scala | 5 +-- .../src/dotty/tools/dotc/typer/Namer.scala | 16 +++++----- .../src/dotty/tools/dotc/typer/Typer.scala | 8 ++--- 10 files changed, 49 insertions(+), 46 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index c6d08a2bd148..5e10dd53bdd6 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -8,7 +8,7 @@ import Symbols._, StdNames._, Trees._ import Decorators._, transform.SymUtils._ import NameKinds.{UniqueName, EvidenceParamName, DefaultGetterName} import typer.FrontEnd -import util.{Property, SourceFile} +import util.{Property, SourceFile, SourcePosition} import collection.mutable.ListBuffer import reporting.diagnostic.messages._ import reporting.trace @@ -29,7 +29,7 @@ object desugar { * The position value indicates the start position of the template of the * deriving class. */ - val DerivingCompanion: Property.Key[Position] = new Property.Key + val DerivingCompanion: Property.Key[SourcePosition] = new Property.Key /** Info of a variable in a pattern: The named tree and its type */ private type VarInfo = (NameTree, Tree) @@ -578,7 +578,7 @@ object desugar { .withSpan(cdef.span).toList if (companionDerived.nonEmpty) for (modClsDef @ TypeDef(_, _) <- mdefs) - modClsDef.putAttachment(DerivingCompanion, impl.pos.startPos) + modClsDef.putAttachment(DerivingCompanion, impl.sourcePos.startPos) mdefs } diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index ddb6f6ab121b..ea562d3d77cb 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -315,7 +315,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { def ValDef(name: TermName, tpt: Tree, rhs: LazyTree)(implicit src: SourceFile): ValDef = new ValDef(name, tpt, rhs) def DefDef(name: TermName, tparams: List[TypeDef], vparamss: List[List[ValDef]], tpt: Tree, rhs: LazyTree)(implicit src: SourceFile): DefDef = new DefDef(name, tparams, vparamss, tpt, rhs) def TypeDef(name: TypeName, rhs: Tree)(implicit src: SourceFile): TypeDef = new TypeDef(name, rhs) - def Template(constr: DefDef, parents: List[Tree], self: ValDef, body: LazyTreeList)(implicit src: SourceFile): Template = + def Template(constr: DefDef, parents: List[Tree], derived: List[Tree], self: ValDef, body: LazyTreeList)(implicit src: SourceFile): Template = if (derived.isEmpty) new Template(constr, parents, self, body) else new DerivingTemplate(constr, parents ++ derived, self, body, derived.length) def Import(expr: Tree, selectors: List[Tree])(implicit src: SourceFile): Import = new Import(expr, selectors) diff --git a/compiler/src/dotty/tools/dotc/config/Printers.scala b/compiler/src/dotty/tools/dotc/config/Printers.scala index 984c9d13b41c..cf10ab175937 100644 --- a/compiler/src/dotty/tools/dotc/config/Printers.scala +++ b/compiler/src/dotty/tools/dotc/config/Printers.scala @@ -18,7 +18,7 @@ object Printers { val config: Printer = noPrinter val cyclicErrors: Printer = noPrinter val debug = noPrinter - val derive = noPrinter + val derive: Printer = noPrinter val dottydoc: Printer = noPrinter val exhaustivity: Printer = noPrinter val gadts: Printer = noPrinter diff --git a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index 1394c88766fe..ec5f0bcf4181 100644 --- a/compiler/src/dotty/tools/dotc/parsing/Scanners.scala +++ b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala @@ -8,6 +8,7 @@ import util.SourceFile import java.lang.Character.isDigit import scala.tasty.util.Chars._ import util.NameTransformer.avoidIllegalChars +import util.Spans.Span import Tokens._ import scala.annotation.{ switch, tailrec } import scala.collection.mutable @@ -251,9 +252,9 @@ object Scanners { } /** A migration warning if in Scala-2 mode, an error otherwise */ - def errorOrMigrationWarning(msg: String, pos: Position = Position(offset)): Unit = - if (isScala2Mode) ctx.migrationWarning(msg, source.atPos(pos)) - else ctx.error(msg, source.atPos(pos)) + def errorOrMigrationWarning(msg: String, span: Span = Span(offset)): Unit = + if (isScala2Mode) ctx.migrationWarning(msg, source.atSpan(span)) + else ctx.error(msg, source.atSpan(span)) // Get next token ------------------------------------------------------------ diff --git a/compiler/src/dotty/tools/dotc/typer/Applications.scala b/compiler/src/dotty/tools/dotc/typer/Applications.scala index 8969a0088739..6fe0ac7c9a2a 100644 --- a/compiler/src/dotty/tools/dotc/typer/Applications.scala +++ b/compiler/src/dotty/tools/dotc/typer/Applications.scala @@ -907,7 +907,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic => if (typedArgs.length <= pt.paramInfos.length && !isNamed) if (typedFn.symbol == defn.Predef_classOf && typedArgs.nonEmpty) { val arg = typedArgs.head - checkClassType(arg.tpe, arg.posd, traitReq = false, stablePrefixReq = false) + checkClassType(arg.tpe, arg.sourcePos, traitReq = false, stablePrefixReq = false) } case _ => } diff --git a/compiler/src/dotty/tools/dotc/typer/Checking.scala b/compiler/src/dotty/tools/dotc/typer/Checking.scala index c815fd9b4c17..a195b81c86b6 100644 --- a/compiler/src/dotty/tools/dotc/typer/Checking.scala +++ b/compiler/src/dotty/tools/dotc/typer/Checking.scala @@ -578,8 +578,8 @@ trait Checking { } /** Check that type `tp` is stable. */ - def checkStable(tp: Type, posd: Positioned)(implicit ctx: Context): Unit = - if (!tp.isStable) ctx.error(ex"$tp is not stable", posd.sourcePos) + def checkStable(tp: Type, pos: SourcePosition)(implicit ctx: Context): Unit = + if (!tp.isStable) ctx.error(ex"$tp is not stable", pos) /** Check that all type members of `tp` have realizable bounds */ def checkRealizableBounds(cls: Symbol, pos: SourcePosition)(implicit ctx: Context): Unit = { @@ -594,14 +594,14 @@ trait Checking { * check that class prefix is stable. * @return `tp` itself if it is a class or trait ref, ObjectType if not. */ - def checkClassType(tp: Type, posd: Positioned, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type = + def checkClassType(tp: Type, pos: SourcePosition, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type = tp.underlyingClassRef(refinementOK = false) match { case tref: TypeRef => - if (traitReq && !(tref.symbol is Trait)) ctx.error(TraitIsExpected(tref.symbol), posd.sourcePos) - if (stablePrefixReq && ctx.phase <= ctx.refchecksPhase) checkStable(tref.prefix, posd) + if (traitReq && !(tref.symbol is Trait)) ctx.error(TraitIsExpected(tref.symbol), pos) + if (stablePrefixReq && ctx.phase <= ctx.refchecksPhase) checkStable(tref.prefix, pos) tp case _ => - ctx.error(ex"$tp is not a class type", posd.sourcePos) + ctx.error(ex"$tp is not a class type", pos) defn.ObjectType } @@ -985,8 +985,8 @@ trait NoChecking extends ReChecking { override def checkNonCyclic(sym: Symbol, info: TypeBounds, reportErrors: Boolean)(implicit ctx: Context): Type = info override def checkNonCyclicInherited(joint: Type, parents: List[Type], decls: Scope, posd: Positioned)(implicit ctx: Context): Unit = () override def checkValue(tree: Tree, proto: Type)(implicit ctx: Context): tree.type = tree - override def checkStable(tp: Type, posd: Positioned)(implicit ctx: Context): Unit = () - override def checkClassType(tp: Type, posd: Positioned, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type = tp + override def checkStable(tp: Type, pos: SourcePosition)(implicit ctx: Context): Unit = () + override def checkClassType(tp: Type, pos: SourcePosition, traitReq: Boolean, stablePrefixReq: Boolean)(implicit ctx: Context): Type = tp override def checkImplicitConversionDefOK(sym: Symbol)(implicit ctx: Context): Unit = () override def checkImplicitConversionUseOK(sym: Symbol, posd: Positioned)(implicit ctx: Context): Unit = () override def checkFeasibleParent(tp: Type, pos: SourcePosition, where: => String = "")(implicit ctx: Context): Type = tp diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index e34052dea2bc..5b22c562fba5 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -8,7 +8,8 @@ import ast.Trees._ import StdNames._ import Contexts._, Symbols._, Types._, SymDenotations._, Names._, NameOps._, Flags._, Decorators._ import ProtoTypes._ -import util.Positions._ +import util.Spans._ +import util.SourcePosition import collection.mutable import Constants.Constant import config.Printers.derive @@ -26,7 +27,7 @@ trait Deriving { this: Typer => * synthesized infrastructure code that is not connected with a * `derives` instance. */ - class Deriver(cls: ClassSymbol, codePos: Position)(implicit ctx: Context) { + class Deriver(cls: ClassSymbol, codePos: SourcePosition)(implicit ctx: Context) { /** A buffer for synthesized symbols */ private var synthetics = new mutable.ListBuffer[Symbol] @@ -114,15 +115,15 @@ trait Deriving { this: Typer => /** Create a synthetic symbol owned by current owner */ private def newSymbol(name: Name, info: Type, - pos: Position = ctx.owner.pos, + span: Span = ctx.owner.span, flags: FlagSet = EmptyFlags)(implicit ctx: Context): Symbol = - ctx.newSymbol(ctx.owner, name, flags | Synthetic, info, coord = pos) + ctx.newSymbol(ctx.owner, name, flags | Synthetic, info, coord = span) /** Create a synthetic method owned by current owner */ private def newMethod(name: TermName, info: Type, - pos: Position = ctx.owner.pos, + span: Span = ctx.owner.span, flags: FlagSet = EmptyFlags)(implicit ctx: Context): TermSymbol = - newSymbol(name, info, pos, flags | Method).asTerm + newSymbol(name, info, span, flags | Method).asTerm /** A version of Type#underlyingClassRef that works also for higher-kinded types */ private def underlyingClassRef(tp: Type): Type = tp match { @@ -137,12 +138,12 @@ trait Deriving { this: Typer => * an instance with the same name does not exist already. * @param reportErrors Report an error if an instance with the same name exists already */ - private def addDerivedInstance(clsName: Name, info: Type, pos: Position, reportErrors: Boolean) = { + private def addDerivedInstance(clsName: Name, info: Type, pos: SourcePosition, reportErrors: Boolean) = { val instanceName = s"derived$$$clsName".toTermName if (ctx.denotNamed(instanceName).exists) { if (reportErrors) ctx.error(i"duplicate typeclass derivation for $clsName", pos) } - else add(newMethod(instanceName, info, pos, Implicit)) + else add(newMethod(instanceName, info, pos.span, Implicit)) } /** Check derived type tree `derived` for the following well-formedness conditions: @@ -166,7 +167,7 @@ trait Deriving { this: Typer => private def processDerivedInstance(derived: untpd.Tree): Unit = { val originalType = typedAheadType(derived, AnyTypeConstructorProto).tpe val underlyingType = underlyingClassRef(originalType) - val derivedType = checkClassType(underlyingType, derived.pos, traitReq = false, stablePrefixReq = true) + val derivedType = checkClassType(underlyingType, derived.sourcePos, traitReq = false, stablePrefixReq = true) val nparams = derivedType.classSymbol.typeParams.length if (derivedType.isRef(defn.GenericClass)) () // do nothing, a Generic instance will be created anyway by `addGeneric` @@ -179,12 +180,12 @@ trait Deriving { this: Typer => val instanceInfo = if (cls.typeParams.isEmpty) ExprType(resultType) else PolyType.fromParams(cls.typeParams, ImplicitMethodType(evidenceParamInfos, resultType)) - addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.pos, reportErrors = true) + addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos, reportErrors = true) } else ctx.error( i"derived class $derivedType should have one type paramater but has $nparams", - derived.pos) + derived.sourcePos) } /** Add value corresponding to `val genericClass = new GenericClass(...)` @@ -192,7 +193,7 @@ trait Deriving { this: Typer => */ private def addGenericClass(): Unit = if (!ctx.denotNamed(nme.genericClass).exists) { - add(newSymbol(nme.genericClass, defn.GenericClassType, codePos)) + add(newSymbol(nme.genericClass, defn.GenericClassType, codePos.span)) } private def addGeneric(): Unit = { @@ -305,7 +306,7 @@ trait Deriving { this: Typer => val shape = shapeArg.dealias val implClassSym = ctx.newNormalizedClassSymbol( - ctx.owner, tpnme.ANON_CLASS, EmptyFlags, genericInstance :: Nil, coord = codePos) + ctx.owner, tpnme.ANON_CLASS, EmptyFlags, genericInstance :: Nil, coord = codePos.span) val implClassCtx = ctx.withOwner(implClassSym) val implClassConstr = newMethod(nme.CONSTRUCTOR, MethodType(Nil, implClassSym.typeRef))(implClassCtx).entered @@ -330,7 +331,7 @@ trait Deriving { this: Typer => case ShapeCases(cases) => val clauses = cases.zipWithIndex.map { case (ShapeCase(pat, elems), idx) => - val patVar = newSymbol(nme.syntheticParamName(0), pat, meth.pos) + val patVar = newSymbol(nme.syntheticParamName(0), pat, meth.span) CaseDef( Bind(patVar, Typed(untpd.Ident(nme.WILDCARD).withType(pat), TypeTree(pat))), EmptyTree, @@ -405,7 +406,7 @@ trait Deriving { this: Typer => if (typeCls == defn.GenericClass) genericRHS(resultType, ref(genericClass)) else { - val module = untpd.ref(companionRef).withPos(sym.pos) + val module = untpd.ref(companionRef).withSpan(sym.span) val rhs = untpd.Select(module, nme.derived) typed(rhs, resultType) } diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 095fecab2072..d06773eb5133 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -745,14 +745,15 @@ trait Implicits { self: Typer => def synthesizedGeneric(formal: Type): Tree = formal.argTypes match { case arg :: Nil => - val arg1 = fullyDefinedType(arg, "Generic argument", pos) + val pos = ctx.source.atSpan(span) + val arg1 = fullyDefinedType(arg, "Generic argument", span) val clsType = checkClassType(arg1, pos, traitReq = false, stablePrefixReq = true) new Deriver(clsType.classSymbol.asClass, pos).genericInstance(clsType) case _ => EmptyTree } - inferImplicit(formal, EmptyTree, pos)(ctx) match { + inferImplicit(formal, EmptyTree, span)(ctx) match { case SearchSuccess(arg, _, _) => arg case fail @ SearchFailure(failed) => def trySpecialCase(cls: ClassSymbol, handler: Type => Tree, ifNot: => Tree) = { diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index 4e99a7bc538d..cf21b6f3811d 100644 --- a/compiler/src/dotty/tools/dotc/typer/Namer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Namer.scala @@ -505,14 +505,14 @@ class Namer { typer: Typer => * @pre `child` must have a position. */ final def addChild(cls: Symbol, child: Symbol)(implicit ctx: Context): Unit = { - val childStart = if (child.pos.exists) child.pos.start else -1 + val childStart = if (child.span.exists) child.span.start else -1 def insertInto(annots: List[Annotation]): List[Annotation] = annots.find(_.symbol == defn.ChildAnnot) match { - case Some(Annotation.Child(other)) if other.pos.exists && childStart <= other.pos.start => + case Some(Annotation.Child(other)) if other.span.exists && childStart <= other.span.start => if (child == other) annots // can happen if a class has several inaccessible children else { - assert(childStart != other.pos.start, i"duplicate child annotation $child / $other") + assert(childStart != other.span.start, i"duplicate child annotation $child / $other") val (prefix, otherAnnot :: rest) = annots.span(_.symbol != defn.ChildAnnot) prefix ::: otherAnnot :: insertInto(rest) } @@ -573,8 +573,8 @@ class Namer { typer: Typer => body = fromTempl.body ++ modTempl.body)) if (fromTempl.derived.nonEmpty) { if (modTempl.derived.nonEmpty) - ctx.error(em"a class and its companion cannot both have `derives' clauses", mdef.pos) - res.putAttachment(desugar.DerivingCompanion, fromTempl.pos.startPos) + ctx.error(em"a class and its companion cannot both have `derives' clauses", mdef.sourcePos) + res.putAttachment(desugar.DerivingCompanion, fromTempl.sourcePos.startPos) } res } @@ -843,7 +843,7 @@ class Namer { typer: Typer => else ctx.error(em"""children of $cls were already queried before $sym was discovered. |As a remedy, you could move $sym on the same nesting level as $cls.""", - child.pos) + child.sourcePos) } } @@ -957,7 +957,7 @@ class Namer { typer: Typer => val ptype = parentType(parent)(ctx.superCallContext).dealiasKeepAnnots if (cls.isRefinementClass) ptype else { - val pt = checkClassType(ptype, parent.posd, + val pt = checkClassType(ptype, parent.sourcePos, traitReq = parent ne parents.head, stablePrefixReq = true) if (pt.derivesFrom(cls)) { val addendum = parent match { @@ -1014,7 +1014,7 @@ class Namer { typer: Typer => if (impl.derived.nonEmpty) { val (derivingClass, derivePos) = original.removeAttachment(desugar.DerivingCompanion) match { case Some(pos) => (cls.companionClass.asClass, pos) - case None => (cls, impl.pos.startPos) + case None => (cls, impl.sourcePos.startPos) } val deriver = new Deriver(derivingClass, derivePos)(localCtx) deriver.enterDerived(impl.derived) diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 4db9a4f4086d..31833cfd9ac7 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -434,7 +434,7 @@ class Typer extends Namer case _ => app } case qual1 => - if (tree.name.isTypeName) checkStable(qual1.tpe, qual1.posd) + if (tree.name.isTypeName) checkStable(qual1.tpe, qual1.sourcePos) val select = typedSelect(tree, pt, qual1) if (select.tpe ne TryDynamicCallType) ConstFold(checkStableIdentPattern(select, pt)) else if (pt.isInstanceOf[FunOrPolyProto] || pt == AssignProto) select @@ -517,7 +517,7 @@ class Typer extends Namer case TypeApplications.EtaExpansion(tycon) => tpt1 = tpt1.withType(tycon) case _ => } - if (checkClassType(tpt1.tpe, tpt1.posd, traitReq = false, stablePrefixReq = true) eq defn.ObjectType) + if (checkClassType(tpt1.tpe, tpt1.sourcePos, traitReq = false, stablePrefixReq = true) eq defn.ObjectType) tpt1 = TypeTree(defn.ObjectType).withSpan(tpt1.span) tpt1 match { @@ -1242,7 +1242,7 @@ class Typer extends Namer def typedSingletonTypeTree(tree: untpd.SingletonTypeTree)(implicit ctx: Context): SingletonTypeTree = track("typedSingletonTypeTree") { val ref1 = typedExpr(tree.ref) - checkStable(ref1.tpe, tree.posd) + checkStable(ref1.tpe, tree.sourcePos) assignType(cpy.SingletonTypeTree(tree)(ref1), ref1) } @@ -1748,7 +1748,7 @@ class Typer extends Namer def typedImport(imp: untpd.Import, sym: Symbol)(implicit ctx: Context): Import = track("typedImport") { val expr1 = typedExpr(imp.expr, AnySelectionProto) - checkStable(expr1.tpe, imp.expr.posd) + checkStable(expr1.tpe, imp.expr.sourcePos) if (!ctx.isAfterTyper) checkRealizable(expr1.tpe, imp.expr.posd) assignType(cpy.Import(imp)(expr1, imp.selectors), sym) } From 5e4fa24b6ef68b20f91541da8b6a779426a5cd03 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 19 Jan 2019 17:40:35 +0100 Subject: [PATCH 56/56] Fix indent --- compiler/src/dotty/tools/dotc/ast/Trees.scala | 392 +++++++++--------- 1 file changed, 196 insertions(+), 196 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/ast/Trees.scala b/compiler/src/dotty/tools/dotc/ast/Trees.scala index 3e5a93e28589..d4adec6800e3 100644 --- a/compiler/src/dotty/tools/dotc/ast/Trees.scala +++ b/compiler/src/dotty/tools/dotc/ast/Trees.scala @@ -1213,106 +1213,106 @@ object Trees { def localCtx = if (tree.hasType && tree.symbol.exists) ctx.withOwner(tree.symbol) else ctx - if (skipTransform(tree)) tree - else tree match { - case Ident(name) => - tree - case Select(qualifier, name) => - cpy.Select(tree)(transform(qualifier), name) - case This(qual) => - tree - case Super(qual, mix) => - cpy.Super(tree)(transform(qual), mix) - case Apply(fun, args) => - cpy.Apply(tree)(transform(fun), transform(args)) - case TypeApply(fun, args) => - cpy.TypeApply(tree)(transform(fun), transform(args)) - case Literal(const) => - tree - case New(tpt) => - cpy.New(tree)(transform(tpt)) - case Typed(expr, tpt) => - cpy.Typed(tree)(transform(expr), transform(tpt)) - case NamedArg(name, arg) => - cpy.NamedArg(tree)(name, transform(arg)) - case Assign(lhs, rhs) => - cpy.Assign(tree)(transform(lhs), transform(rhs)) - case Block(stats, expr) => - cpy.Block(tree)(transformStats(stats), transform(expr)) - case If(cond, thenp, elsep) => - cpy.If(tree)(transform(cond), transform(thenp), transform(elsep)) - case Closure(env, meth, tpt) => - cpy.Closure(tree)(transform(env), transform(meth), transform(tpt)) - case Match(selector, cases) => - cpy.Match(tree)(transform(selector), transformSub(cases)) - case CaseDef(pat, guard, body) => - cpy.CaseDef(tree)(transform(pat), transform(guard), transform(body)) - case Labeled(bind, expr) => - cpy.Labeled(tree)(transformSub(bind), transform(expr)) - case Return(expr, from) => - cpy.Return(tree)(transform(expr), transformSub(from)) - case WhileDo(cond, body) => - cpy.WhileDo(tree)(transform(cond), transform(body)) - case Try(block, cases, finalizer) => - cpy.Try(tree)(transform(block), transformSub(cases), transform(finalizer)) - case SeqLiteral(elems, elemtpt) => - cpy.SeqLiteral(tree)(transform(elems), transform(elemtpt)) - case Inlined(call, bindings, expansion) => - cpy.Inlined(tree)(call, transformSub(bindings), transform(expansion)(inlineContext(call))) - case TypeTree() => - tree - case SingletonTypeTree(ref) => - cpy.SingletonTypeTree(tree)(transform(ref)) - case AndTypeTree(left, right) => - cpy.AndTypeTree(tree)(transform(left), transform(right)) - case OrTypeTree(left, right) => - cpy.OrTypeTree(tree)(transform(left), transform(right)) - case RefinedTypeTree(tpt, refinements) => - cpy.RefinedTypeTree(tree)(transform(tpt), transformSub(refinements)) - case AppliedTypeTree(tpt, args) => - cpy.AppliedTypeTree(tree)(transform(tpt), transform(args)) - case LambdaTypeTree(tparams, body) => - implicit val ctx = localCtx - cpy.LambdaTypeTree(tree)(transformSub(tparams), transform(body)) - case MatchTypeTree(bound, selector, cases) => - cpy.MatchTypeTree(tree)(transform(bound), transform(selector), transformSub(cases)) - case ByNameTypeTree(result) => - cpy.ByNameTypeTree(tree)(transform(result)) - case TypeBoundsTree(lo, hi) => - cpy.TypeBoundsTree(tree)(transform(lo), transform(hi)) - case Bind(name, body) => - cpy.Bind(tree)(name, transform(body)) - case Alternative(trees) => - cpy.Alternative(tree)(transform(trees)) - case UnApply(fun, implicits, patterns) => - cpy.UnApply(tree)(transform(fun), transform(implicits), transform(patterns)) - case EmptyValDef => - tree - case tree @ ValDef(name, tpt, _) => - implicit val ctx = localCtx - val tpt1 = transform(tpt) - val rhs1 = transform(tree.rhs) - cpy.ValDef(tree)(name, tpt1, rhs1) - case tree @ DefDef(name, tparams, vparamss, tpt, _) => - implicit val ctx = localCtx - cpy.DefDef(tree)(name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt), transform(tree.rhs)) - case tree @ TypeDef(name, rhs) => - implicit val ctx = localCtx - cpy.TypeDef(tree)(name, transform(rhs)) - case tree @ Template(constr, parents, self, _) if tree.derived.isEmpty => - cpy.Template(tree)(transformSub(constr), transform(tree.parents), Nil, transformSub(self), transformStats(tree.body)) - case Import(expr, selectors) => - cpy.Import(tree)(transform(expr), selectors) - case PackageDef(pid, stats) => - cpy.PackageDef(tree)(transformSub(pid), transformStats(stats)(localCtx)) - case Annotated(arg, annot) => - cpy.Annotated(tree)(transform(arg), transform(annot)) - case Thicket(trees) => - val trees1 = transform(trees) - if (trees1 eq trees) tree else Thicket(trees1) - case _ => - transformMoreCases(tree) - } + if (skipTransform(tree)) tree + else tree match { + case Ident(name) => + tree + case Select(qualifier, name) => + cpy.Select(tree)(transform(qualifier), name) + case This(qual) => + tree + case Super(qual, mix) => + cpy.Super(tree)(transform(qual), mix) + case Apply(fun, args) => + cpy.Apply(tree)(transform(fun), transform(args)) + case TypeApply(fun, args) => + cpy.TypeApply(tree)(transform(fun), transform(args)) + case Literal(const) => + tree + case New(tpt) => + cpy.New(tree)(transform(tpt)) + case Typed(expr, tpt) => + cpy.Typed(tree)(transform(expr), transform(tpt)) + case NamedArg(name, arg) => + cpy.NamedArg(tree)(name, transform(arg)) + case Assign(lhs, rhs) => + cpy.Assign(tree)(transform(lhs), transform(rhs)) + case Block(stats, expr) => + cpy.Block(tree)(transformStats(stats), transform(expr)) + case If(cond, thenp, elsep) => + cpy.If(tree)(transform(cond), transform(thenp), transform(elsep)) + case Closure(env, meth, tpt) => + cpy.Closure(tree)(transform(env), transform(meth), transform(tpt)) + case Match(selector, cases) => + cpy.Match(tree)(transform(selector), transformSub(cases)) + case CaseDef(pat, guard, body) => + cpy.CaseDef(tree)(transform(pat), transform(guard), transform(body)) + case Labeled(bind, expr) => + cpy.Labeled(tree)(transformSub(bind), transform(expr)) + case Return(expr, from) => + cpy.Return(tree)(transform(expr), transformSub(from)) + case WhileDo(cond, body) => + cpy.WhileDo(tree)(transform(cond), transform(body)) + case Try(block, cases, finalizer) => + cpy.Try(tree)(transform(block), transformSub(cases), transform(finalizer)) + case SeqLiteral(elems, elemtpt) => + cpy.SeqLiteral(tree)(transform(elems), transform(elemtpt)) + case Inlined(call, bindings, expansion) => + cpy.Inlined(tree)(call, transformSub(bindings), transform(expansion)(inlineContext(call))) + case TypeTree() => + tree + case SingletonTypeTree(ref) => + cpy.SingletonTypeTree(tree)(transform(ref)) + case AndTypeTree(left, right) => + cpy.AndTypeTree(tree)(transform(left), transform(right)) + case OrTypeTree(left, right) => + cpy.OrTypeTree(tree)(transform(left), transform(right)) + case RefinedTypeTree(tpt, refinements) => + cpy.RefinedTypeTree(tree)(transform(tpt), transformSub(refinements)) + case AppliedTypeTree(tpt, args) => + cpy.AppliedTypeTree(tree)(transform(tpt), transform(args)) + case LambdaTypeTree(tparams, body) => + implicit val ctx = localCtx + cpy.LambdaTypeTree(tree)(transformSub(tparams), transform(body)) + case MatchTypeTree(bound, selector, cases) => + cpy.MatchTypeTree(tree)(transform(bound), transform(selector), transformSub(cases)) + case ByNameTypeTree(result) => + cpy.ByNameTypeTree(tree)(transform(result)) + case TypeBoundsTree(lo, hi) => + cpy.TypeBoundsTree(tree)(transform(lo), transform(hi)) + case Bind(name, body) => + cpy.Bind(tree)(name, transform(body)) + case Alternative(trees) => + cpy.Alternative(tree)(transform(trees)) + case UnApply(fun, implicits, patterns) => + cpy.UnApply(tree)(transform(fun), transform(implicits), transform(patterns)) + case EmptyValDef => + tree + case tree @ ValDef(name, tpt, _) => + implicit val ctx = localCtx + val tpt1 = transform(tpt) + val rhs1 = transform(tree.rhs) + cpy.ValDef(tree)(name, tpt1, rhs1) + case tree @ DefDef(name, tparams, vparamss, tpt, _) => + implicit val ctx = localCtx + cpy.DefDef(tree)(name, transformSub(tparams), vparamss mapConserve (transformSub(_)), transform(tpt), transform(tree.rhs)) + case tree @ TypeDef(name, rhs) => + implicit val ctx = localCtx + cpy.TypeDef(tree)(name, transform(rhs)) + case tree @ Template(constr, parents, self, _) if tree.derived.isEmpty => + cpy.Template(tree)(transformSub(constr), transform(tree.parents), Nil, transformSub(self), transformStats(tree.body)) + case Import(expr, selectors) => + cpy.Import(tree)(transform(expr), selectors) + case PackageDef(pid, stats) => + cpy.PackageDef(tree)(transformSub(pid), transformStats(stats)(localCtx)) + case Annotated(arg, annot) => + cpy.Annotated(tree)(transform(arg), transform(annot)) + case Thicket(trees) => + val trees1 = transform(trees) + if (trees1 eq trees) tree else Thicket(trees1) + case _ => + transformMoreCases(tree) + } } def transformStats(trees: List[Tree])(implicit ctx: Context): List[Tree] = @@ -1340,102 +1340,102 @@ object Trees { foldOver(x, tree)(ctx.withSource(tree.source)) else { Stats.record(s"TreeAccumulator.foldOver/$getClass") - def localCtx = - if (tree.hasType && tree.symbol.exists) ctx.withOwner(tree.symbol) else ctx - tree match { - case Ident(name) => - x - case Select(qualifier, name) => - this(x, qualifier) - case This(qual) => - x - case Super(qual, mix) => - this(x, qual) - case Apply(fun, args) => - this(this(x, fun), args) - case TypeApply(fun, args) => - this(this(x, fun), args) - case Literal(const) => - x - case New(tpt) => - this(x, tpt) - case Typed(expr, tpt) => - this(this(x, expr), tpt) - case NamedArg(name, arg) => - this(x, arg) - case Assign(lhs, rhs) => - this(this(x, lhs), rhs) - case Block(stats, expr) => - this(this(x, stats), expr) - case If(cond, thenp, elsep) => - this(this(this(x, cond), thenp), elsep) - case Closure(env, meth, tpt) => - this(this(this(x, env), meth), tpt) - case Match(selector, cases) => - this(this(x, selector), cases) - case CaseDef(pat, guard, body) => - this(this(this(x, pat), guard), body) - case Labeled(bind, expr) => - this(this(x, bind), expr) - case Return(expr, from) => - this(this(x, expr), from) - case WhileDo(cond, body) => - this(this(x, cond), body) - case Try(block, handler, finalizer) => - this(this(this(x, block), handler), finalizer) - case SeqLiteral(elems, elemtpt) => - this(this(x, elems), elemtpt) - case Inlined(call, bindings, expansion) => - this(this(x, bindings), expansion)(inlineContext(call)) - case TypeTree() => - x - case SingletonTypeTree(ref) => - this(x, ref) - case AndTypeTree(left, right) => - this(this(x, left), right) - case OrTypeTree(left, right) => - this(this(x, left), right) - case RefinedTypeTree(tpt, refinements) => - this(this(x, tpt), refinements) - case AppliedTypeTree(tpt, args) => - this(this(x, tpt), args) - case LambdaTypeTree(tparams, body) => - implicit val ctx = localCtx - this(this(x, tparams), body) - case MatchTypeTree(bound, selector, cases) => - this(this(this(x, bound), selector), cases) - case ByNameTypeTree(result) => - this(x, result) - case TypeBoundsTree(lo, hi) => - this(this(x, lo), hi) - case Bind(name, body) => - this(x, body) - case Alternative(trees) => - this(x, trees) - case UnApply(fun, implicits, patterns) => - this(this(this(x, fun), implicits), patterns) - case tree @ ValDef(name, tpt, _) => - implicit val ctx = localCtx - this(this(x, tpt), tree.rhs) - case tree @ DefDef(name, tparams, vparamss, tpt, _) => - implicit val ctx = localCtx - this(this((this(x, tparams) /: vparamss)(apply), tpt), tree.rhs) - case TypeDef(name, rhs) => - implicit val ctx = localCtx - this(x, rhs) - case tree @ Template(constr, parents, self, _) if tree.derived.isEmpty => - this(this(this(this(x, constr), parents), self), tree.body) - case Import(expr, selectors) => - this(x, expr) - case PackageDef(pid, stats) => - this(this(x, pid), stats)(localCtx) - case Annotated(arg, annot) => - this(this(x, arg), annot) - case Thicket(ts) => - this(x, ts) - case _ => - foldMoreCases(x, tree) - } + def localCtx = + if (tree.hasType && tree.symbol.exists) ctx.withOwner(tree.symbol) else ctx + tree match { + case Ident(name) => + x + case Select(qualifier, name) => + this(x, qualifier) + case This(qual) => + x + case Super(qual, mix) => + this(x, qual) + case Apply(fun, args) => + this(this(x, fun), args) + case TypeApply(fun, args) => + this(this(x, fun), args) + case Literal(const) => + x + case New(tpt) => + this(x, tpt) + case Typed(expr, tpt) => + this(this(x, expr), tpt) + case NamedArg(name, arg) => + this(x, arg) + case Assign(lhs, rhs) => + this(this(x, lhs), rhs) + case Block(stats, expr) => + this(this(x, stats), expr) + case If(cond, thenp, elsep) => + this(this(this(x, cond), thenp), elsep) + case Closure(env, meth, tpt) => + this(this(this(x, env), meth), tpt) + case Match(selector, cases) => + this(this(x, selector), cases) + case CaseDef(pat, guard, body) => + this(this(this(x, pat), guard), body) + case Labeled(bind, expr) => + this(this(x, bind), expr) + case Return(expr, from) => + this(this(x, expr), from) + case WhileDo(cond, body) => + this(this(x, cond), body) + case Try(block, handler, finalizer) => + this(this(this(x, block), handler), finalizer) + case SeqLiteral(elems, elemtpt) => + this(this(x, elems), elemtpt) + case Inlined(call, bindings, expansion) => + this(this(x, bindings), expansion)(inlineContext(call)) + case TypeTree() => + x + case SingletonTypeTree(ref) => + this(x, ref) + case AndTypeTree(left, right) => + this(this(x, left), right) + case OrTypeTree(left, right) => + this(this(x, left), right) + case RefinedTypeTree(tpt, refinements) => + this(this(x, tpt), refinements) + case AppliedTypeTree(tpt, args) => + this(this(x, tpt), args) + case LambdaTypeTree(tparams, body) => + implicit val ctx = localCtx + this(this(x, tparams), body) + case MatchTypeTree(bound, selector, cases) => + this(this(this(x, bound), selector), cases) + case ByNameTypeTree(result) => + this(x, result) + case TypeBoundsTree(lo, hi) => + this(this(x, lo), hi) + case Bind(name, body) => + this(x, body) + case Alternative(trees) => + this(x, trees) + case UnApply(fun, implicits, patterns) => + this(this(this(x, fun), implicits), patterns) + case tree @ ValDef(name, tpt, _) => + implicit val ctx = localCtx + this(this(x, tpt), tree.rhs) + case tree @ DefDef(name, tparams, vparamss, tpt, _) => + implicit val ctx = localCtx + this(this((this(x, tparams) /: vparamss)(apply), tpt), tree.rhs) + case TypeDef(name, rhs) => + implicit val ctx = localCtx + this(x, rhs) + case tree @ Template(constr, parents, self, _) if tree.derived.isEmpty => + this(this(this(this(x, constr), parents), self), tree.body) + case Import(expr, selectors) => + this(x, expr) + case PackageDef(pid, stats) => + this(this(x, pid), stats)(localCtx) + case Annotated(arg, annot) => + this(this(x, arg), annot) + case Thicket(ts) => + this(x, ts) + case _ => + foldMoreCases(x, tree) + } } def foldMoreCases(x: X, tree: Tree)(implicit ctx: Context): X = {