diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index f0221b47ad7b..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 @@ -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[SourcePosition] = new Property.Key /** Info of a variable in a pattern: The named tree and its type */ private type VarInfo = (NameTree, Tree) @@ -297,7 +303,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 +319,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,14 +564,23 @@ 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 } - def companionDefs(parentTpt: Tree, defs: List[Tree]) = - moduleDef( + // synthetic object C extends parentTpt derives class-derived { defs } + def companionDefs(parentTpt: Tree, defs: List[Tree]) = { + val mdefs = 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 + .withSpan(cdef.span).toList + if (companionDerived.nonEmpty) + for (modClsDef @ TypeDef(_, _) <- mdefs) + modClsDef.putAttachment(DerivingCompanion, impl.sourcePos.startPos) + mdefs + } val companionMembers = defaultGetters ::: eqInstances ::: enumCases @@ -613,10 +629,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 +691,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 +788,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 +1351,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..d4adec6800e3 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 } @@ -739,16 +738,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 +1150,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 +1189,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 */ @@ -1292,8 +1299,8 @@ object Trees { 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 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) => @@ -1416,7 +1423,7 @@ object Trees { case TypeDef(name, rhs) => implicit val ctx = localCtx this(x, rhs) - case tree @ Template(constr, parents, self, _) => + 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) 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..ea562d3d77cb 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], 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) 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/config/Printers.scala b/compiler/src/dotty/tools/dotc/config/Printers.scala index 74739b612ff9..cf10ab175937 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: Printer = noPrinter val dottydoc: Printer = noPrinter val exhaustivity: Printer = noPrinter val gadts: Printer = noPrinter diff --git a/compiler/src/dotty/tools/dotc/core/Definitions.scala b/compiler/src/dotty/tools/dotc/core/Definitions.scala index e44b02107266..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 { @@ -214,14 +215,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.typelevel.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 @@ -648,6 +649,18 @@ 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 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 GenericClassType: TypeRef = ctx.requiredClassRef("scala.reflect.GenericClass") + 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") @@ -905,8 +918,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 --------------------------------------------------- @@ -1261,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/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 | diff --git a/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala b/compiler/src/dotty/tools/dotc/core/OrderingConstraint.scala index c86ce2a271bc..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 { @@ -102,6 +103,9 @@ object OrderingConstraint { newConstraint(c.boundsMap, c.lowerMap, c.upperMap.updated(poly, entries)) def initial = Nil } + + @sharable + val empty = new OrderingConstraint(SimpleIdentityMap.Empty, SimpleIdentityMap.Empty, SimpleIdentityMap.Empty) } import OrderingConstraint._ diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index f618a91271c6..b0f3db0429aa 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" @@ -391,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" @@ -398,6 +400,8 @@ 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" val elem: N = "elem" @@ -424,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" @@ -480,6 +485,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" @@ -489,7 +495,7 @@ object StdNames { val productPrefix: N = "productPrefix" val raw_ : N = "raw" val readResolve: N = "readResolve" - val reflect : N = "reflect" + val reflect: N = "reflect" val reflectiveSelectable: N = "reflectiveSelectable" val reify : N = "reify" val rootMirror : N = "rootMirror" 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/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/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/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/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 63c36ba56e7c..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} */ - def qualId(): Tree = - dotSelectors(termIdent()) + def qualId(): Tree = dotSelectors(termIdent()) /** SimpleExpr ::= literal * | symbol @@ -1407,7 +1406,7 @@ object Parsers { } else simpleExpr() - /** SimpleExpr ::= new Template + /** SimpleExpr ::= ‘new’ (ConstrApp [TemplateBody] | TemplateBody) * | BlockExpr * | ‘'{’ BlockExprContents ‘}’ * | ‘'(’ ExprsInParens ‘)’ @@ -1451,15 +1450,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 +1480,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, Nil, isEnum = false))) + } + } + /** ExprsInParens ::= ExprInParens {`,' ExprInParens} */ def exprsInParensOpt(): List[Tree] = @@ -2414,18 +2432,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 InheritClauses 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) @@ -2467,7 +2485,7 @@ object Parsers { tokenSeparated(WITH, constrApp) } else Nil - Template(constr, parents, EmptyValDef, Nil) + Template(constr, parents, Nil, EmptyValDef, Nil) } /* -------- TEMPLATES ------------------------------------------- */ @@ -2481,41 +2499,72 @@ 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 + } + + /** InheritClauses ::= [‘extends’ ConstrApps] [‘derives’ QualId {‘,’ QualId}] + */ + def inheritClauses(): (List[Tree], List[Tree]) = { + val extended = + if (in.token == EXTENDS) { + in.nextToken() + if (in.token == LBRACE) { + in.errorOrMigrationWarning("`extends' must be followed by at least one parent") + Nil + } + else constrApps() + } + else Nil + val derived = + if (isIdent(nme.derives)) { + in.nextToken() + tokenSeparated(COMMA, () => convertToTypeId(qualId())) + } + else Nil + (extended, derived) + } + + /** Template ::= InheritClauses [TemplateBody] + */ + def template(constr: DefDef, isEnum: Boolean = false): Template = { + val (parents, derived) = inheritClauses() 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, derived, 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 || isIdent(nme.derives) || in.token == LBRACE) + template(constr, isEnum) + else + 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/parsing/Scanners.scala b/compiler/src/dotty/tools/dotc/parsing/Scanners.scala index ec1c3c28106f..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 @@ -250,6 +251,11 @@ object Scanners { isScala2Mode } + /** A migration warning if in Scala-2 mode, an error otherwise */ + 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 ------------------------------------------------------------ /** Are we directly in a string interpolation expression? 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..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) } @@ -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/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/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/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/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/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..74e14348c46e 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)) @@ -154,11 +133,14 @@ 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 */ - def children(implicit ctx: Context): List[Symbol] = + /** 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 = children.exists(_ `eq` self) 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 766ce1f7f5e0..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 } @@ -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 { @@ -996,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 new file mode 100644 index 000000000000..5b22c562fba5 --- /dev/null +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -0,0 +1,440 @@ +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 ProtoTypes._ +import util.Spans._ +import util.SourcePosition +import collection.mutable +import Constants.Constant +import config.Printers.derive +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 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, codePos: SourcePosition)(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: List[Symbol] = 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`. + */ + private def caseShape(sym: Symbol): Type = { + val (constr, elems) = + sym match { + case caseClass: ClassSymbol => + 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 => + def instantiate(implicit ctx: Context) = { + val poly = constrained(info, untpd.EmptyTree)._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.setExploreTyperState().setOwner(caseClass)) + case info: MethodType => + (caseClass.typeRef, info.paramInfos) + case _ => + (caseClass.typeRef, Nil) + } + case _ => + (sym.termRef, Nil) + } + val elemShape = (elems :\ (defn.UnitType: Type))(defn.PairType.appliedTo(_, _)) + defn.ShapeCaseType.appliedTo(constr, elemShape) + } + + /** The shape of `cls` if `cls` is sealed */ + private def sealedShape: Type = { + val cases = children.map(caseShape).filter(_.exists) + 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 { + 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) = { + 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 + sym + } + + /** Create a synthetic symbol owned by current owner */ + private def newSymbol(name: Name, info: Type, + span: Span = ctx.owner.span, + flags: FlagSet = EmptyFlags)(implicit ctx: Context): Symbol = + 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, + span: Span = ctx.owner.span, + flags: FlagSet = EmptyFlags)(implicit ctx: Context): TermSymbol = + 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 { + 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 + */ + 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.span, 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[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) + 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` + else 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 (cls.typeParams.isEmpty) ExprType(resultType) + else PolyType.fromParams(cls.typeParams, ImplicitMethodType(evidenceParamInfos, resultType)) + 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.sourcePos) + } + + /** Add value corresponding to `val genericClass = new GenericClass(...)` + * to `synthetics`, unless a definition of `genericClass` exists already. + */ + private def addGenericClass(): Unit = + if (!ctx.denotNamed(nme.genericClass).exists) { + add(newSymbol(nme.genericClass, defn.GenericClassType, codePos.span)) + } + + 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.GenericType.name, genericCompleter, codePos, 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(_)) + addGeneric() + addGenericClass() + } + + 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 + } + } + + /** 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 + } + } + + /** A helper class to create definition trees for `synthetics` */ + class Finalizer { + import tpd._ + + /** 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 `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) => + 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.filterNot(_.is(PrivateLocal)).map(_.name.toString) + (patLabel :: elemLabels).mkString("\u0000") + } + + /** The RHS of the `genericClass` value definition */ + def genericClassRHS = + New(defn.GenericClassType, + List(Literal(Constant(cls.typeRef)), + Literal(Constant(labelString(shapeWithClassParams))))) + + /** 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, where + * = Cases[(Case[Cons[T], (T, Lst[T])], Case[Nil.type, Unit])]: + * + * 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 genericRHS(genericType: Type, genericClassRef: Tree)(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, genericInstance :: Nil, coord = codePos.span) + 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.GenericClassType + .member(nme.mirror) + .suchThat(sym => args.tpes.corresponds(sym.info.firstParamTypes)(_ <:< _)) + genericClassRef.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.span) + 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.GenericClassType)).entered + tpd.DefDef(meth, genericClassRef) + } + + List(shapeType, reflectMethod, reifyMethod, commonMethod) + } + + val implClassDef = ClassDef(implClassSym, DefDef(implClassConstr), implClassStats(implClassCtx)) + Block(implClassDef :: Nil, New(implClassSym.typeRef, Nil)) + } + + /** The type class instance definition with symbol `sym` */ + 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) + 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.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, companionRef) = classAndCompanionRef(resultType) + if (typeCls == defn.GenericClass) + genericRHS(resultType, ref(genericClass)) + else { + val module = untpd.ref(companionRef).withSpan(sym.span) + 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, 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)) + } + + /** Synthesized instance for `Generic[]` */ + def genericInstance(clsType: Type): tpd.Tree = { + val shape = shapeOfType(clsType) + val genericType = RefinedType(defn.GenericType.appliedTo(clsType), tpnme.Shape, TypeAlias(shape)) + val finalizer = new Finalizer + finalizer.genericRHS(genericType, finalizer.genericClassRHS) + } + } +} diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index 44dcfdd15f3e..d06773eb5133 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 } @@ -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): Type = { + 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) @@ -713,6 +739,20 @@ trait Implicits { self: Typer => assumedCanEqual(tp1, tp2) || !hasEq(tp1) && !hasEq(tp2) } + /** 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 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, span)(ctx) match { case SearchSuccess(arg, _, _) => arg case fail @ SearchFailure(failed) => @@ -728,9 +768,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)))))) } } @@ -916,6 +957,8 @@ trait Implicits { self: Typer => } } else result + case NoMatchingImplicitsFailure => + SearchFailure(new NoMatchingImplicits(pt, argument, ctx.typerState.constraint)) case _ => result0 } diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 658ca818d807..b81e9e8387a6 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. diff --git a/compiler/src/dotty/tools/dotc/typer/Namer.scala b/compiler/src/dotty/tools/dotc/typer/Namer.scala index c107b091d4b5..cf21b6f3811d 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 => @@ -194,6 +196,7 @@ 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 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. @@ -496,12 +499,35 @@ 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 = 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.span.exists && childStart <= other.span.start => + if (child == other) + annots // can happen if a class has several inaccessible children + else { + assert(childStart != other.span.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 + } + 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 _ => } @@ -531,14 +557,25 @@ 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 synthetic 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 = 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.sourcePos) + res.putAttachment(desugar.DerivingCompanion, fromTempl.sourcePos.startPos) + } res } else tree @@ -559,7 +596,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) } @@ -739,7 +776,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 { @@ -787,6 +827,35 @@ 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) + addChild(cls, cls) + else if (!cls.is(ChildrenQueried)) + 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.""", + child.sourcePos) + } + } + + 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. */ @@ -838,7 +907,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 +919,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. @@ -877,17 +947,17 @@ 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 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 { @@ -926,19 +996,31 @@ 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%, %") + if (impl.derived.nonEmpty) { + val (derivingClass, derivePos) = original.removeAttachment(desugar.DerivingCompanion) match { + case Some(pos) => (cls.companionClass.asClass, pos) + case None => (cls, impl.sourcePos.startPos) + } + val deriver = new Deriver(derivingClass, derivePos)(localCtx) + deriver.enterDerived(impl.derived) + original.putAttachment(Deriver, deriver) + } + 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/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 0b726256c0b3..31833cfd9ac7 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 => _, _} @@ -433,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 @@ -516,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 { @@ -1241,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) } @@ -1556,7 +1557,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 +1627,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 +1642,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)) @@ -1672,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 @@ -1742,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) } @@ -2089,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/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/dotc/run-from-tasty.blacklist b/compiler/test/dotc/run-from-tasty.blacklist index a2fbf589a9c8..9a51ed515139 100644 --- a/compiler/test/dotc/run-from-tasty.blacklist +++ b/compiler/test/dotc/run-from-tasty.blacklist @@ -8,4 +8,8 @@ puzzle.scala implicitMatch.scala 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 aa1c42f1a07e..6f412a5d041a 100644 --- a/compiler/test/dotc/run-test-pickling.blacklist +++ b/compiler/test/dotc/run-test-pickling.blacklist @@ -6,3 +6,7 @@ t7374 tuples1.scala tuples1a.scala typeclass-derivation2.scala +typeclass-derivation2a.scala +typeclass-derivation3.scala +derive-generic.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/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 => diff --git a/docs/docs/internals/syntax.md b/docs/docs/internals/syntax.md index 06da1afd9f4c..8d16b95b48a2 100644 --- a/docs/docs/internals/syntax.md +++ b/docs/docs/internals/syntax.md @@ -86,6 +86,25 @@ 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 @@ -186,7 +205,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 +360,15 @@ 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] -Template ::= ConstrApps [TemplateBody] | TemplateBody Template(constr, parents, self, stats) +ObjectDef ::= id [Template] ModuleDef(mods, name, template) // no constructor +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) ConstrExpr ::= SelfInvocation | ConstrBlock diff --git a/docs/docs/reference/derivation.md b/docs/docs/reference/derivation.md new file mode 100644 index 000000000000..7439be868cc4 --- /dev/null +++ b/docs/docs/reference/derivation.md @@ -0,0 +1,392 @@ +--- +layout: doc-page +title: Typeclass Derivation +--- + +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 generates implicit instances of the `Eq`, `Ordering`, and `Pickling` traits in the companion object `Tree`: +```scala +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 +``` + +### 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 Types + +A trait or class can appear in a `derives` clause if + + - 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](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 +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 + +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[( + 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[T]`: +```scala +Case[Labelled[T], (T, String)] +``` +And here is the one for `Option[T]`: +```scala +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 Generic TypeClass + +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 +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 +```scala +enum Result[+T, +E] derives Logging { + case class Ok[T](result: T) + case class Err[E](err: E) +} +``` +would produce: +```scala +object Result { + import scala.compiletime.Shape._ + + 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`. + +```scala +abstract class Generic[T] { + type Shape <: scala.compiletime.Shape + + /** 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: GenericClass +} +``` +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: + + - `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 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 = adtClass.label(ordinal)(0) + + /** The label of the `n`'th element of the case reflected by this mirror */ + def elementLabel(n: Int): String = adtClass.label(ordinal)(n + 1) +} +``` + +### GenericClass + +Here's the API of `scala.reflect.GenericClass`: + +```scala +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 = + 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 `Generic` instance, recursively. +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 +} +``` +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](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) + inline erasedValue[ev.Shape] 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 `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, 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]`. + +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. 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: + + 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 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 signaled by +calling the `error` method defined in `scala.compiletime`. + +**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`. + +```scala +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 = implicitly[Generic[Tree[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 +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, +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 + +``` +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 in the compiler-generated `Generic` instances: + + - 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 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. 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 diff --git a/library/src-bootstrapped/scala/Tuple.scala b/library/src-bootstrapped/scala/Tuple.scala index ed5e3d43ff25..07b524050612 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 { @@ -307,7 +307,7 @@ object Tuple { } } -abstract sealed class NonEmptyTuple extends Tuple { +sealed trait NonEmptyTuple extends Tuple { import Tuple._ import NonEmptyTuple._ diff --git a/library/src-bootstrapped/scala/compiletime/Shape.scala b/library/src-bootstrapped/scala/compiletime/Shape.scala new file mode 100644 index 000000000000..583080ce8d7e --- /dev/null +++ b/library/src-bootstrapped/scala/compiletime/Shape.scala @@ -0,0 +1,16 @@ +package scala.compiletime + +/** The shape of an ADT. + * This is either 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-bootstrapped/scala/typelevel/package.scala b/library/src-bootstrapped/scala/compiletime/package.scala similarity index 73% rename from library/src-bootstrapped/scala/typelevel/package.scala rename to library/src-bootstrapped/scala/compiletime/package.scala index 6b083dd81b6b..4aa2921066d6 100644 --- a/library/src-bootstrapped/scala/typelevel/package.scala +++ b/library/src-bootstrapped/scala/compiletime/package.scala @@ -1,11 +1,9 @@ package scala -package object typelevel { +package object compiletime { 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-bootstrapped/scala/reflect/Generic.scala b/library/src-bootstrapped/scala/reflect/Generic.scala new file mode 100644 index 000000000000..6b0ed8967774 --- /dev/null +++ b/library/src-bootstrapped/scala/reflect/Generic.scala @@ -0,0 +1,18 @@ +package scala.reflect + +/** A class for mapping between an ADT value and + * the case mirror that represents the value. + */ +abstract class Generic[T] { + + type Shape <: scala.compiletime.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: GenericClass +} diff --git a/library/src-bootstrapped/scala/reflect/GenericClass.scala b/library/src-bootstrapped/scala/reflect/GenericClass.scala new file mode 100644 index 000000000000..2738f3c5c9c9 --- /dev/null +++ b/library/src-bootstrapped/scala/reflect/GenericClass.scala @@ -0,0 +1,76 @@ +package scala.reflect +import annotation.tailrec +import collection.mutable.ArrayBuffer + +/** 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`" + */ +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 = + 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)) + + /** 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)) + + /** 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]]) + + 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 GenericClass { + 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 { + 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-bootstrapped/scala/reflect/Mirror.scala b/library/src-bootstrapped/scala/reflect/Mirror.scala new file mode 100644 index 000000000000..0b2aed2c9645 --- /dev/null +++ b/library/src-bootstrapped/scala/reflect/Mirror.scala @@ -0,0 +1,18 @@ +package scala.reflect + +/** A generic representation of a case in an ADT + * @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 + */ +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 = adtClass.label(ordinal)(0) + + /** The label of the `n`'th element of the case reflected by this mirror */ + def elementLabel(n: Int): String = adtClass.label(ordinal)(n + 1) +} 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 diff --git a/library/src/scala/tasty/reflect/Printers.scala b/library/src/scala/tasty/reflect/Printers.scala index 2c6706f248fe..7ca162e91e80 100644 --- a/library/src/scala/tasty/reflect/Printers.scala +++ b/library/src/scala/tasty/reflect/Printers.scala @@ -186,9 +186,11 @@ 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) + this += ", " + visitList[TypeTree](derived, visitTypeTree) this += ", " += self += ", " ++= body += ")" case PackageDef(name, owner) => this += "PackageDef(\"" += name += "\", " += owner += ")" @@ -569,7 +571,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 +627,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 +1011,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 +1214,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 +1565,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) } 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/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/neg/deriving.scala b/tests/neg/deriving.scala new file mode 100644 index 000000000000..6dd84d6e84c3 --- /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 + } +} 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/neg/typeclass-derivation2.scala b/tests/neg/typeclass-derivation2.scala new file mode 100644 index 000000000000..3cdb001a56b6 --- /dev/null +++ b/tests/neg/typeclass-derivation2.scala @@ -0,0 +1,278 @@ +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 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 = + 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 GenericClass { + /** 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: 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) + + /** 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: GenericClass + } + + /** 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] { + 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 genericClass = new GenericClass("Cons\000hd\000tl\001Nil") + import genericClass.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 = genericClass + } + + // 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) // derives Eq, Pickler, Show + +object Pair { + // common compiler-generated infrastructure + import TypeLevel._ + + type Shape[T] = Shape.Case[Pair[T], (T, T)] + + 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 = genericClass + } +} + +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 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 { + 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 = genericClass + } + 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.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 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 TypeLevel._ + + 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) + | ^ + |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 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() diff --git a/tests/pos-special/typeclass-scaling.scala b/tests/pos-special/typeclass-scaling.scala new file mode 100644 index 000000000000..695eb631d350 --- /dev/null +++ b/tests/pos-special/typeclass-scaling.scala @@ -0,0 +1,389 @@ +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` measures (lines/words/chars): +// +// 39161 97323 3124249 +// +// The command +// +// time sc typeclass-scaling.scala -Xmax-inlines 100 +// +// gives (best of three): +// +// real 0m13.095s +// user 0m50.491s +// sys 0m0.970s +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.compiletime.erasedValue + import compiletime._ + import reflect.{Mirror, Generic} + + 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 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](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) + } + } + } + + 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 compiletime._ + import reflect.{Mirror, Generic} + + 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](gen: Generic[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { + inline val size = constValue[Tuple.Size[Elems]] + inline if (size == 0) + gen.reify(gen.common.mirror(ordinal)) + else { + val elems = new Array[Object](size) + unpickleElems[Elems](buf, elems, 0) + gen.reify(gen.common.mirror(ordinal, elems)) + } + } + + 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](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](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] { + 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 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/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/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) 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] 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/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-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-with-compiler/tasty-consumer.check b/tests/run-with-compiler/tasty-consumer.check index 8140980ca00d..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)), 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)))) 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) +} + 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 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-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()))) 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 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 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.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 69863715976f..464d5e5af976 100644 --- a/tests/run/typeclass-derivation2.scala +++ b/tests/run/typeclass-derivation2.scala @@ -1,66 +1,90 @@ 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) +// 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. + */ + 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 +94,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,84 +113,102 @@ 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 -{ +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("Cons\000hd\000tl\001Nil") + import reflectedClass.mirror + val NilMirror = mirror(1) - implicit def lstShape[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) 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 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 -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)] - implicit def pairShape[T]: Shaped[Pair[T], Shape[T]] = new { + 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 deriving = Pair + def common = reflectedClass } - protected val caseLabels = Array("Pair\000x\000y") + // 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] + )] - // 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 + 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 @@ -175,18 +217,18 @@ trait Eq[T] { } object Eq { - import scala.typelevel._ - import Deriving._ + 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](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 } @@ -194,28 +236,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] { @@ -230,8 +271,8 @@ trait Pickler[T] { } object Pickler { - import scala.typelevel._ - import Deriving._ + import scala.compiletime.{erasedValue, constValue} + import TypeLevel._ def nextInt(buf: mutable.ListBuffer[Int]): Int = try buf.head finally buf.trimStart(1) @@ -278,11 +319,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)) } } @@ -321,8 +362,8 @@ trait Show[T] { def show(x: T): String } object Show { - import scala.typelevel._ - import Deriving._ + import scala.compiletime.erasedValue + import TypeLevel._ inline def tryShow[T](x: T): String = implicit match { case s: Show[T] => s.show(x) @@ -371,7 +412,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)) @@ -424,4 +465,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 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..615b064fa84e --- /dev/null +++ b/tests/run/typeclass-derivation2a.scala @@ -0,0 +1,480 @@ +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 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 = + 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 GenericClass { + /** 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: 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) + + /** The label of the `n`'th element of the case reflected by this mirror */ + def elementLabel(n: Int) = reflected.label(ordinal)(n + 1) + } + + /** 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 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: GenericClass + } +} + +// 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._ + + 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 + } + + // 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._ + + val genericClass = new GenericClass("Pair\000x\000y") + import genericClass.mirror + + private type ShapeOf[T] = Shape.Case[Pair[T], (T, 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 + 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._ + + 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 + } + + 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: 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) + } + } + } + + 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](gen: Generic[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { + inline val size = constValue[Tuple.Size[Elems]] + inline if (size == 0) + gen.reify(gen.common.mirror(ordinal)) + else { + val elems = new Array[Object](size) + unpickleElems[Elems](buf, elems, 0) + gen.reify(gen.common.mirror(ordinal, elems)) + } + } + + 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) + 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: 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] { + 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: Generic[T]): Show[T] = new { + def show(x: T): String = { + val xm = ev.reflect(x) + val args = inline erasedValue[ev.Shape] 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)) +} diff --git a/tests/run/typeclass-derivation3.check b/tests/run/typeclass-derivation3.check new file mode 100644 index 000000000000..688c68e8ee41 --- /dev/null +++ b/tests/run/typeclass-derivation3.check @@ -0,0 +1,14 @@ +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())) +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 new file mode 100644 index 000000000000..519b28d7ab1e --- /dev/null +++ b/tests/run/typeclass-derivation3.scala @@ -0,0 +1,314 @@ +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 {} + + + // A simple product type + case class Pair[T](x: T, y: T) derives Eq, Pickler, Show + + // 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 typeclasses { + // A typeclass + trait Eq[T] { + def eql(x: T, y: T): Boolean + } + + object Eq { + import scala.compiletime.erasedValue + import compiletime._ + import reflect.{Mirror, Generic} + + 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 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](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) + } + } + } + + 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 compiletime._ + import reflect.{Mirror, Generic} + + 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](gen: Generic[T], buf: mutable.ListBuffer[Int], ordinal: Int): T = { + inline val size = constValue[Tuple.Size[Elems]] + inline if (size == 0) + gen.reify(gen.common.mirror(ordinal)) + else { + val elems = new Array[Object](size) + unpickleElems[Elems](buf, elems, 0) + gen.reify(gen.common.mirror(ordinal, elems)) + } + } + + 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](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](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] { + 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 compiletime._ + import reflect.{Mirror, Generic} + + 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](implicit ev: Generic[T]): Show[T] = new { + def show(x: T): String = { + val xm = ev.reflect(x) + val args = inline erasedValue[ev.Shape] 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 + } + } +} +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)) + + 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 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 {