diff --git a/compiler/src/dotty/tools/dotc/ast/Desugar.scala b/compiler/src/dotty/tools/dotc/ast/Desugar.scala index f8c4b11ee8a9..daf9aa7e5c3b 100644 --- a/compiler/src/dotty/tools/dotc/ast/Desugar.scala +++ b/compiler/src/dotty/tools/dotc/ast/Desugar.scala @@ -15,6 +15,7 @@ import reporting.diagnostic.messages._ import reporting.trace import annotation.constructorOnly import printing.Formatting.hl +import config.Printers import scala.annotation.internal.sharable @@ -51,7 +52,7 @@ object desugar { private type VarInfo = (NameTree, Tree) /** Is `name` the name of a method that can be invalidated as a compiler-generated - * case class method that clashes with a user-defined method? + * case class method if it clashes with a user-defined method? */ def isRetractableCaseClassMethodName(name: Name)(implicit ctx: Context): Boolean = name match { case nme.apply | nme.unapply | nme.unapplySeq | nme.copy => true @@ -765,7 +766,7 @@ object desugar { } flatTree(cdef1 :: companions ::: implicitWrappers) - } + }.reporting(res => i"desugared: $res", Printers.desugar) /** Expand * @@ -848,8 +849,7 @@ object desugar { fwd } val moduleName = tdef.name.toTermName - val localRef = Select(Ident(moduleName), tdef.name) - localRef.pushAttachment(SuppressAccessCheck, ()) + val localRef = Select(Ident(moduleName), tdef.name).withAttachment(SuppressAccessCheck, ()) val aliasType = cpy.TypeDef(tdef)(rhs = completeForwarder(localRef)).withSpan(tdef.span.startPos) val localType = tdef.withMods(Modifiers(Synthetic | Opaque).withPrivateWithin(tdef.name)) diff --git a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala index 42cbef882321..2fcf7a107fc7 100644 --- a/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala +++ b/compiler/src/dotty/tools/dotc/ast/DesugarEnums.scala @@ -8,6 +8,7 @@ import Symbols._, StdNames._, Trees._ import Decorators._ import util.{Property, SourceFile} import typer.ErrorReporting._ +import transform.SyntheticMembers.ExtendsSingletonMirror import scala.annotation.internal.sharable @@ -115,11 +116,16 @@ object DesugarEnums { val toStringDef = DefDef(nme.toString_, Nil, Nil, TypeTree(), Ident(nme.name)) .withFlags(Override) - def creator = New(Template(emptyConstructor, enumClassRef :: Nil, Nil, EmptyValDef, - List(enumTagDef, toStringDef) ++ registerCall)) + val creator = New(Template( + constr = emptyConstructor, + parents = enumClassRef :: Nil, + derived = Nil, + self = EmptyValDef, + body = List(enumTagDef, toStringDef) ++ registerCall + ).withAttachment(ExtendsSingletonMirror, ())) DefDef(nme.DOLLAR_NEW, Nil, List(List(param(nme.tag, defn.IntType), param(nme.name, defn.StringType))), - TypeTree(), creator) + TypeTree(), creator).withFlags(Private | Synthetic) } /** The return type of an enum case apply method and any widening methods in which @@ -259,6 +265,7 @@ object DesugarEnums { .withFlags(Override) val (tagMeth, scaffolding) = enumTagMeth(CaseKind.Object) val impl1 = cpy.Template(impl)(body = List(tagMeth, toStringMeth) ++ registerCall) + .withAttachment(ExtendsSingletonMirror, ()) val vdef = ValDef(name, TypeTree(), New(impl1)).withMods(mods | Final) flatTree(scaffolding ::: vdef :: Nil).withSpan(span) } diff --git a/compiler/src/dotty/tools/dotc/ast/tpd.scala b/compiler/src/dotty/tools/dotc/ast/tpd.scala index f6ec2704b967..9db322bb5dc4 100644 --- a/compiler/src/dotty/tools/dotc/ast/tpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/tpd.scala @@ -400,6 +400,26 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { case ConstantType(value) => Literal(value) } + /** A path that corresponds to the given type `tp`. Error if `tp` is not a refinement + * of an addressable singleton type. + */ + def pathFor(tp: Type)(implicit ctx: Context): Tree = { + def recur(tp: Type): Tree = tp match { + case tp: NamedType => + tp.info match { + case TypeAlias(alias) => recur(alias) + case _: TypeBounds => EmptyTree + case _ => singleton(tp) + } + case tp: TypeProxy => recur(tp.superType) + case _ => EmptyTree + } + recur(tp).orElse { + ctx.error(em"$tp is not an addressable singleton type") + TypeTree(tp) + } + } + /** A tree representing a `newXYZArray` operation of the right * kind for the given element type in `elemTpe`. No type arguments or * `length` arguments are given. diff --git a/compiler/src/dotty/tools/dotc/ast/untpd.scala b/compiler/src/dotty/tools/dotc/ast/untpd.scala index e09b5c7562f3..b8885b6118b6 100644 --- a/compiler/src/dotty/tools/dotc/ast/untpd.scala +++ b/compiler/src/dotty/tools/dotc/ast/untpd.scala @@ -263,10 +263,7 @@ object untpd extends Trees.Instance[Untyped] with UntypedTreeInfo { } /** Install the derived type tree as a dependency on `sym` */ - def watching(sym: Symbol): this.type = { - pushAttachment(OriginalSymbol, sym) - this - } + def watching(sym: Symbol): this.type = withAttachment(OriginalSymbol, sym) /** A hook to ensure that all necessary symbols are completed so that * OriginalSymbol attachments are propagated to this tree diff --git a/compiler/src/dotty/tools/dotc/config/Printers.scala b/compiler/src/dotty/tools/dotc/config/Printers.scala index f07dfe9a07a3..155f6984a7e9 100644 --- a/compiler/src/dotty/tools/dotc/config/Printers.scala +++ b/compiler/src/dotty/tools/dotc/config/Printers.scala @@ -19,6 +19,7 @@ object Printers { val cyclicErrors: Printer = noPrinter val debug = noPrinter // no type annotation here to force inlining val derive: Printer = noPrinter + val desugar: 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 ad840ac6c372..2c9703315ae6 100644 --- a/compiler/src/dotty/tools/dotc/core/Definitions.scala +++ b/compiler/src/dotty/tools/dotc/core/Definitions.scala @@ -690,16 +690,23 @@ class Definitions { lazy val ModuleSerializationProxyConstructor: TermSymbol = ModuleSerializationProxyClass.requiredMethod(nme.CONSTRUCTOR, List(ClassType(TypeBounds.empty))) - 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 MirrorType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror") + def MirrorClass(implicit ctx: Context): ClassSymbol = MirrorType.symbol.asClass + + lazy val Mirror_ProductType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror.Product") + def Mirror_ProductClass(implicit ctx: Context): ClassSymbol = Mirror_ProductType.symbol.asClass + + lazy val Mirror_Product_fromProductR: TermRef = Mirror_ProductClass.requiredMethodRef(nme.fromProduct) + def Mirror_Product_fromProduct(implicit ctx: Context): Symbol = Mirror_Product_fromProductR.symbol + + lazy val Mirror_SumType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror.Sum") + def Mirror_SumClass(implicit ctx: Context): ClassSymbol = Mirror_SumType.symbol.asClass + + lazy val Mirror_SingletonType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror.Singleton") + def Mirror_SingletonClass(implicit ctx: Context): ClassSymbol = Mirror_SingletonType.symbol.asClass + + lazy val Mirror_SingletonProxyType: TypeRef = ctx.requiredClassRef("scala.deriving.Mirror.SingletonProxy") + def Mirror_SingletonProxyClass(implicit ctx: Context): ClassSymbol = Mirror_SingletonProxyType.symbol.asClass lazy val LanguageModuleRef: TermSymbol = ctx.requiredModule("scala.language") def LanguageModuleClass(implicit ctx: Context): ClassSymbol = LanguageModuleRef.moduleClass.asClass diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index cceebe50b219..e665c10cb1b8 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -626,6 +626,7 @@ object Flags { /** An enum case */ final val EnumCase: FlagConjunction = allOf(Enum, Case) + final val EnumCaseVal: FlagConjunction = allOf(Enum, CaseVal) /** A term parameter or parameter accessor */ final val TermParamOrAccessor: FlagSet = Param | ParamAccessor diff --git a/compiler/src/dotty/tools/dotc/core/StdNames.scala b/compiler/src/dotty/tools/dotc/core/StdNames.scala index 553f53dd9380..fafe9e4876fc 100644 --- a/compiler/src/dotty/tools/dotc/core/StdNames.scala +++ b/compiler/src/dotty/tools/dotc/core/StdNames.scala @@ -339,6 +339,11 @@ object StdNames { val LiteralAnnotArg: N = "LiteralAnnotArg" val longHash: N = "longHash" val MatchCase: N = "MatchCase" + val MirroredElemTypes: N = "MirroredElemTypes" + val MirroredElemLabels: N = "MirroredElemLabels" + val MirroredLabel: N = "MirroredLabel" + val MirroredMonoType: N = "MirroredMonoType" + val MirroredType: N = "MirroredType" val Modifiers: N = "Modifiers" val NestedAnnotArg: N = "NestedAnnotArg" val NoFlags: N = "NoFlags" @@ -432,6 +437,7 @@ object StdNames { val flagsFromBits : N = "flagsFromBits" val flatMap: N = "flatMap" val foreach: N = "foreach" + val fromProduct: N = "fromProduct" val genericArrayOps: N = "genericArrayOps" val genericClass: N = "genericClass" val get: N = "get" diff --git a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala index b0a6bf25b9d8..83d1aadf93e9 100644 --- a/compiler/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/compiler/src/dotty/tools/dotc/core/TypeComparer.scala @@ -2313,19 +2313,30 @@ class TrackingTypeComparer(initctx: Context) extends TypeComparer(initctx) { cas } def widenAbstractTypes(tp: Type): Type = new TypeMap { + var seen = Set[TypeParamRef]() def apply(tp: Type) = tp match { case tp: TypeRef => - if (tp.symbol.isAbstractOrParamType | tp.symbol.isOpaqueAlias) - WildcardType - else tp.info match { - case TypeAlias(alias) => - val alias1 = widenAbstractTypes(alias) - if (alias1 ne alias) alias1 else tp - case _ => mapOver(tp) + tp.info match { + case info: MatchAlias => + mapOver(tp) + // TODO: We should follow the alias in this case, but doing so + // risks infinite recursion + case TypeBounds(lo, hi) => + if (hi frozen_<:< lo) { + val alias = apply(lo) + if (alias ne lo) alias else mapOver(tp) + } + else WildcardType + case _ => + mapOver(tp) } - + case tp: TypeLambda => + val saved = seen + seen ++= tp.paramRefs + try mapOver(tp) + finally seen = saved case tp: TypeVar if !tp.isInstantiated => WildcardType - case _: TypeParamRef => WildcardType + case tp: TypeParamRef if !seen.contains(tp) => WildcardType case _ => mapOver(tp) } }.apply(tp) diff --git a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala index 6bf763416406..170f3a4020ce 100644 --- a/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala @@ -547,7 +547,7 @@ class PlainPrinter(_ctx: Context) extends Printer { val saved = maxSummarized maxSummarized = ctx.base.toTextRecursions + depth try op - finally maxSummarized = depth + finally maxSummarized = saved } def summarized[T](op: => T): T = summarized(summarizeDepth)(op) diff --git a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala index 69b4ebcb41ad..866e7e29f060 100644 --- a/compiler/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/compiler/src/dotty/tools/dotc/transform/PostTyper.scala @@ -26,7 +26,7 @@ object PostTyper { * field (corresponding = super class field is initialized with subclass field) * (@see ForwardParamAccessors) * - * (3) Add synthetic methods (@see SyntheticMethods) + * (3) Add synthetic members (@see SyntheticMembers) * * (4) Check that `New` nodes can be instantiated, and that annotations are valid * @@ -64,7 +64,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case _ => } - override def changesMembers: Boolean = true // the phase adds super accessors and synthetic methods + override def changesMembers: Boolean = true // the phase adds super accessors and synthetic members override def transformPhase(implicit ctx: Context): Phase = thisPhase.next @@ -73,7 +73,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase val superAcc: SuperAccessors = new SuperAccessors(thisPhase) val paramFwd: ParamForwarding = new ParamForwarding(thisPhase) - val synthMth: SyntheticMethods = new SyntheticMethods(thisPhase) + val synthMbr: SyntheticMembers = new SyntheticMembers(thisPhase) private def newPart(tree: Tree): Option[New] = methPart(tree) match { case Select(nu: New, _) => Some(nu) @@ -230,7 +230,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase case tree: Template => withNoCheckNews(tree.parents.flatMap(newPart)) { val templ1 = paramFwd.forwardParamAccessors(tree) - synthMth.addSyntheticMethods( + synthMbr.addSyntheticMembers( superAcc.wrapTemplate(templ1)( super.transform(_).asInstanceOf[Template])) } diff --git a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala index deaaa39f8884..3a3873977be8 100644 --- a/compiler/src/dotty/tools/dotc/transform/SymUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/SymUtils.scala @@ -12,6 +12,8 @@ import StdNames._ import NameKinds._ import Flags._ import Annotations._ +import ValueClasses.isDerivedValueClass +import Decorators._ import language.implicitConversions import scala.annotation.tailrec @@ -59,10 +61,56 @@ class SymUtils(val self: Symbol) extends AnyVal { def isSuperAccessor(implicit ctx: Context): Boolean = self.name.is(SuperAccessorName) - /** A type or term parameter or a term parameter accessor */ + /** Is this a type or term parameter or a term parameter accessor? */ def isParamOrAccessor(implicit ctx: Context): Boolean = self.is(Param) || self.is(ParamAccessor) + /** Is this a case class for which a product mirror is generated? + * Excluded are value classes, abstract classes and case classes with more than one + * parameter section. + */ + def whyNotGenericProduct(implicit ctx: Context): String = + if (!self.is(CaseClass)) "it is not a case class" + else if (self.is(Abstract)) "it is an abstract class" + else if (self.primaryConstructor.info.paramInfoss.length != 1) "it takes more than one parameter list" + else if (isDerivedValueClass(self)) "it is a value class" + else "" + + def isGenericProduct(implicit ctx: Context): Boolean = whyNotGenericProduct.isEmpty + + /** Is this a sealed class or trait for which a sum mirror is generated? + * It must satisfy the following conditions: + * - it has at least one child class or object + * - none of its children are anonymous classes + * - all of its children are addressable through a path from its companion object + * - all of its children are generic products or singletons + */ + def whyNotGenericSum(implicit ctx: Context): String = + if (!self.is(Sealed)) + s"it is not a sealed ${if (self.is(Trait)) "trait" else "class"}" + else { + val children = self.children + val companion = self.linkedClass + def problem(child: Symbol) = { + + def isAccessible(sym: Symbol): Boolean = + companion.isContainedIn(sym) || sym.is(Module) && isAccessible(sym.owner) + + if (child == self) "it has anonymous or inaccessible subclasses" + else if (!isAccessible(child.owner)) i"its child $child is not accessible" + else if (!child.isClass) "" + else { + val s = child.whyNotGenericProduct + if (s.isEmpty) s + else i"its child $child is not a generic product because $s" + } + } + if (children.isEmpty) "it does not have subclasses" + else children.map(problem).find(!_.isEmpty).getOrElse("") + } + + def isGenericSum(implicit ctx: Context): Boolean = whyNotGenericSum.isEmpty + /** If this is a constructor, its owner: otherwise this. */ final def skipConstructor(implicit ctx: Context): Symbol = if (self.isConstructor) self.owner else self @@ -151,6 +199,10 @@ class SymUtils(val self: Symbol) extends AnyVal { else owner.isLocal } + /** The typeRef with wildcard arguments for each type parameter */ + def rawTypeRef(implicit ctx: Context) = + self.typeRef.appliedTo(self.typeParams.map(_ => TypeBounds.empty)) + /** Is symbol a quote operation? */ def isQuote(implicit ctx: Context): Boolean = self == defn.InternalQuoted_exprQuote || self == defn.InternalQuoted_typeQuote diff --git a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala similarity index 56% rename from compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala rename to compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala index fe99b0544a6b..3256860b4281 100644 --- a/compiler/src/dotty/tools/dotc/transform/SyntheticMethods.scala +++ b/compiler/src/dotty/tools/dotc/transform/SyntheticMembers.scala @@ -2,13 +2,30 @@ package dotty.tools.dotc package transform import core._ -import Symbols._, Types._, Contexts._, StdNames._, Constants._, SymUtils._ +import Symbols._, Types._, Contexts._, Names._, StdNames._, Constants._, SymUtils._ import Flags._ import DenotTransformers._ import Decorators._ import NameOps._ import Annotations.Annotation +import typer.ProtoTypes.constrained +import ast.untpd import ValueClasses.isDerivedValueClass +import SymUtils._ +import util.Property +import config.Printers.derive + +object SyntheticMembers { + + /** Attachment marking an anonymous class as a singleton case that will extend from Mirror.Singleton */ + val ExtendsSingletonMirror: Property.StickyKey[Unit] = new Property.StickyKey + + /** Attachment recording that an anonymous class should extend Mirror.Product */ + val ExtendsProductMirror: Property.StickyKey[Unit] = new Property.StickyKey + + /** Attachment recording that an anonymous class should extend Mirror.Sum */ + val ExtendsSumMirror: Property.StickyKey[Unit] = new Property.StickyKey +} /** Synthetic method implementations for case classes, case objects, * and value classes. @@ -32,7 +49,8 @@ import ValueClasses.isDerivedValueClass * def equals(other: Any): Boolean * def hashCode(): Int */ -class SyntheticMethods(thisPhase: DenotTransformer) { +class SyntheticMembers(thisPhase: DenotTransformer) { + import SyntheticMembers._ import ast.tpd._ private[this] var myValueSymbols: List[Symbol] = Nil @@ -51,6 +69,15 @@ class SyntheticMethods(thisPhase: DenotTransformer) { def caseSymbols(implicit ctx: Context): List[Symbol] = { initSymbols; myCaseSymbols } def caseModuleSymbols(implicit ctx: Context): List[Symbol] = { initSymbols; myCaseModuleSymbols } + private def existingDef(sym: Symbol, clazz: ClassSymbol)(implicit ctx: Context): Symbol = { + val existing = sym.matchingMember(clazz.thisType) + if (existing != sym && !existing.is(Deferred)) existing + else NoSymbol + } + + private def synthesizeDef(sym: TermSymbol, rhsFn: List[List[Tree]] => Context => Tree)(implicit ctx: Context): Tree = + DefDef(sym, rhsFn(_)(ctx.withOwner(sym))).withSpan(ctx.owner.span.focus) + /** If this is a case or value class, return the appropriate additional methods, * otherwise return nothing. */ @@ -68,36 +95,34 @@ class SyntheticMethods(thisPhase: DenotTransformer) { else if (isDerivedValueClass(clazz)) valueSymbols else Nil - def syntheticDefIfMissing(sym: Symbol): List[Tree] = { - val existing = sym.matchingMember(clazz.thisType) - if (existing == sym || existing.is(Deferred)) syntheticDef(sym) :: Nil - else Nil - } + def syntheticDefIfMissing(sym: Symbol): List[Tree] = + if (existingDef(sym, clazz).exists) Nil else syntheticDef(sym) :: Nil def syntheticDef(sym: Symbol): Tree = { val synthetic = sym.copy( owner = clazz, flags = sym.flags &~ Deferred | Synthetic | Override, + info = clazz.thisType.memberInfo(sym), coord = clazz.coord).enteredAfter(thisPhase).asTerm - def forwardToRuntime(vrefss: List[List[Tree]]): Tree = - ref(defn.runtimeMethodRef("_" + sym.name.toString)).appliedToArgs(This(clazz) :: vrefss.head) + def forwardToRuntime(vrefs: List[Tree]): Tree = + ref(defn.runtimeMethodRef("_" + sym.name.toString)).appliedToArgs(This(clazz) :: vrefs) - def ownName(vrefss: List[List[Tree]]): Tree = + def ownName: Tree = Literal(Constant(clazz.name.stripModuleClassSuffix.toString)) - def syntheticRHS(implicit ctx: Context): List[List[Tree]] => Tree = synthetic.name match { - case nme.hashCode_ if isDerivedValueClass(clazz) => vrefss => valueHashCodeBody - case nme.hashCode_ => vrefss => caseHashCodeBody - case nme.toString_ => if (clazz.is(ModuleClass)) ownName else forwardToRuntime - case nme.equals_ => vrefss => equalsBody(vrefss.head.head) - case nme.canEqual_ => vrefss => canEqualBody(vrefss.head.head) - case nme.productArity => vrefss => Literal(Constant(accessors.length)) + def syntheticRHS(vrefss: List[List[Tree]])(implicit ctx: Context): Tree = synthetic.name match { + case nme.hashCode_ if isDerivedValueClass(clazz) => valueHashCodeBody + case nme.hashCode_ => caseHashCodeBody + case nme.toString_ => if (clazz.is(ModuleClass)) ownName else forwardToRuntime(vrefss.head) + case nme.equals_ => equalsBody(vrefss.head.head) + case nme.canEqual_ => canEqualBody(vrefss.head.head) + case nme.productArity => Literal(Constant(accessors.length)) case nme.productPrefix => ownName - case nme.productElement => vrefss => productElementBody(accessors.length, vrefss.head.head) + case nme.productElement => productElementBody(accessors.length, vrefss.head.head) } ctx.log(s"adding $synthetic to $clazz at ${ctx.phase}") - DefDef(synthetic, syntheticRHS(ctx.withOwner(synthetic))).withSpan(ctx.owner.span.focus) + synthesizeDef(synthetic, syntheticRHS) } /** The class @@ -289,9 +314,153 @@ class SyntheticMethods(thisPhase: DenotTransformer) { Nil } - def addSyntheticMethods(impl: Template)(implicit ctx: Context): Template = { + /** The class + * + * ``` + * case class C[T <: U](x: T, y: String*) + * ``` + * + * gets the `fromProduct` method: + * + * ``` + * def fromProduct(x$0: Product): MirroredMonoType = + * new C[U]( + * x$0.productElement(0).asInstanceOf[U], + * x$0.productElement(1).asInstanceOf[Seq[String]]: _*) + * ``` + * where + * ``` + * type MirroredMonoType = C[_] + * ``` + */ + def fromProductBody(caseClass: Symbol, param: Tree)(implicit ctx: Context): Tree = { + val (classRef, methTpe) = + caseClass.primaryConstructor.info match { + case tl: PolyType => + val (tl1, tpts) = constrained(tl, untpd.EmptyTree, alwaysAddTypeVars = true) + val targs = + for (tpt <- tpts) yield + tpt.tpe match { + case tvar: TypeVar => tvar.instantiate(fromBelow = false) + } + (caseClass.typeRef.appliedTo(targs), tl.instantiate(targs)) + case methTpe => + (caseClass.typeRef, methTpe) + } + methTpe match { + case methTpe: MethodType => + val elems = + for ((formal, idx) <- methTpe.paramInfos.zipWithIndex) yield { + val elem = + param.select(defn.Product_productElement).appliedTo(Literal(Constant(idx))) + .ensureConforms(formal.underlyingIfRepeated(isJava = false)) + if (formal.isRepeatedParam) ctx.typer.seqToRepeated(elem) else elem + } + New(classRef, elems) + } + } + + /** For an enum T: + * + * def ordinal(x: MirroredMonoType) = x.enumTag + * + * For sealed trait with children of normalized types C_1, ..., C_n: + * + * def ordinal(x: MirroredMonoType) = x match { + * case _: C_1 => 0 + * ... + * case _: C_n => n - 1 + * } + * + * Here, the normalized type of a class C is C[_, ...., _] with + * a wildcard for each type parameter. The normalized type of an object + * O is O.type. + */ + def ordinalBody(cls: Symbol, param: Tree)(implicit ctx: Context): Tree = + if (cls.is(Enum)) param.select(nme.enumTag) + else { + val cases = + for ((child, idx) <- cls.children.zipWithIndex) yield { + val patType = if (child.isTerm) child.termRef else child.rawTypeRef + val pat = Typed(untpd.Ident(nme.WILDCARD).withType(patType), TypeTree(patType)) + CaseDef(pat, EmptyTree, Literal(Constant(idx))) + } + Match(param, cases) + } + + /** - If `impl` is the companion of a generic sum, add `deriving.Mirror.Sum` parent + * and `MirroredMonoType` and `ordinal` members. + * - If `impl` is the companion of a generic product, add `deriving.Mirror.Product` parent + * and `MirroredMonoType` and `fromProduct` members. + * - If `impl` is marked with one of the attachments ExtendsSingletonMirror, ExtendsProductMirror, + * or ExtendsSumMirror, remove the attachment and generate the corresponding mirror support, + * On this case the represented class or object is referred to in a pre-existing `MirroredMonoType` + * member of the template. + */ + def addMirrorSupport(impl: Template)(implicit ctx: Context): Template = { val clazz = ctx.owner.asClass - cpy.Template(impl)(body = serializableObjectMethod(clazz) ::: caseAndValueMethods(clazz) ::: impl.body) + + var newBody = impl.body + var newParents = impl.parents + def addParent(parent: Type): Unit = { + newParents = newParents :+ TypeTree(parent) + val oldClassInfo = clazz.classInfo + val newClassInfo = oldClassInfo.derivedClassInfo( + classParents = oldClassInfo.classParents :+ parent) + clazz.copySymDenotation(info = newClassInfo).installAfter(thisPhase) + } + def addMethod(name: TermName, info: Type, cls: Symbol, body: (Symbol, Tree, Context) => Tree): Unit = { + val meth = ctx.newSymbol(clazz, name, Synthetic | Method, info, coord = clazz.coord) + if (!existingDef(meth, clazz).exists) { + meth.entered + newBody = newBody :+ + synthesizeDef(meth, vrefss => ctx => body(cls, vrefss.head.head, ctx)) + } + } + val linked = clazz.linkedClass + lazy val monoType = { + val existing = clazz.info.member(tpnme.MirroredMonoType).symbol + if (existing.exists && !existing.is(Deferred)) existing + else { + val monoType = + ctx.newSymbol(clazz, tpnme.MirroredMonoType, Synthetic, TypeAlias(linked.rawTypeRef), coord = clazz.coord) + newBody = newBody :+ TypeDef(monoType).withSpan(ctx.owner.span.focus) + monoType.entered + } + } + def makeSingletonMirror() = + addParent(defn.Mirror_SingletonType) + def makeProductMirror(cls: Symbol) = { + addParent(defn.Mirror_ProductType) + addMethod(nme.fromProduct, MethodType(defn.ProductType :: Nil, monoType.typeRef), cls, + fromProductBody(_, _)(_).ensureConforms(monoType.typeRef)) // t4758.scala or i3381.scala are examples where a cast is needed + } + def makeSumMirror(cls: Symbol) = { + addParent(defn.Mirror_SumType) + addMethod(nme.ordinal, MethodType(monoType.typeRef :: Nil, defn.IntType), cls, + ordinalBody(_, _)(_)) + } + + if (clazz.is(Module)) { + if (clazz.is(Case)) makeSingletonMirror() + else if (linked.isGenericProduct) makeProductMirror(linked) + else if (linked.isGenericSum) makeSumMirror(linked) + else if (linked.is(Sealed)) + derive.println(i"$linked is not a sum because ${linked.whyNotGenericSum}") + } + else if (impl.removeAttachment(ExtendsSingletonMirror).isDefined) + makeSingletonMirror() + else if (impl.removeAttachment(ExtendsProductMirror).isDefined) + makeProductMirror(monoType.typeRef.dealias.classSymbol) + else if (impl.removeAttachment(ExtendsSumMirror).isDefined) + makeSumMirror(monoType.typeRef.dealias.classSymbol) + + cpy.Template(impl)(parents = newParents, body = newBody) } + def addSyntheticMembers(impl: Template)(implicit ctx: Context): Template = { + val clazz = ctx.owner.asClass + addMirrorSupport( + cpy.Template(impl)(body = serializableObjectMethod(clazz) ::: caseAndValueMethods(clazz) ::: impl.body)) + } } diff --git a/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala b/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala index ee6938b78248..1453caca4010 100644 --- a/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala +++ b/compiler/src/dotty/tools/dotc/transform/TypeUtils.scala @@ -7,6 +7,7 @@ import TypeErasure.ErasedValueType import Types._ import Contexts._ import Symbols._ +import Names.Name object TypeUtils { /** A decorator that provides methods on types @@ -63,5 +64,17 @@ object TypeUtils { } extractAlias(lo) } + + def refinedWith(name: Name, info: Type)(implicit ctx: Context) = RefinedType(self, name, info) + + /** The TermRef referring to the companion of the underlying class reference + * of this type, while keeping the same prefix. + */ + def companionRef(implicit ctx: Context): TermRef = self match { + case self @ TypeRef(prefix, _) if self.symbol.isClass => + prefix.select(self.symbol.companionModule).asInstanceOf[TermRef] + case self: TypeProxy => + self.underlying.companionRef + } } } diff --git a/compiler/src/dotty/tools/dotc/typer/Deriving.scala b/compiler/src/dotty/tools/dotc/typer/Deriving.scala index 79e2c9c20e41..426782e88606 100644 --- a/compiler/src/dotty/tools/dotc/typer/Deriving.scala +++ b/compiler/src/dotty/tools/dotc/typer/Deriving.scala @@ -29,104 +29,9 @@ trait Deriving { this: Typer => */ class Deriver(cls: ClassSymbol, codePos: SourcePosition)(implicit ctx: Context) { - /** A buffer for synthesized symbols */ + /** A buffer for synthesized symbols for type class instances */ private var synthetics = new mutable.ListBuffer[Symbol] - private var derivesGeneric = false - - /** 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 = TypeOps.nestedPairs(elems) - 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 = TypeOps.nestedPairs(cases) - 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 @@ -140,12 +45,18 @@ trait Deriving { this: Typer => * an instance with the same name does not exist already. * @param reportErrors Report an error if an instance with the same name exists already */ - private def addDerivedInstance(clsName: Name, info: Type, pos: SourcePosition, reportErrors: Boolean) = { + private def addDerivedInstance(clsName: Name, info: Type, pos: SourcePosition): Unit = { val instanceName = s"derived$$$clsName".toTermName - if (ctx.denotNamed(instanceName).exists) { - if (reportErrors) ctx.error(i"duplicate typeclass derivation for $clsName", pos) + if (ctx.denotNamed(instanceName).exists) + ctx.error(i"duplicate typeclass derivation for $clsName", pos) + else { + // If we set the Synthetic flag here widenImplied will widen too far and the + // derived instance will have too low a priority to be selected over a freshly + // derived instance at the summoning site. + synthetics += + ctx.newSymbol(ctx.owner, instanceName, Implied | Method, info, coord = pos.span) + .entered } - else add(newMethod(instanceName, info, pos.span, Implied)) } /** Check derived type tree `derived` for the following well-formedness conditions: @@ -177,9 +88,16 @@ trait Deriving { this: Typer => val derivedType = checkClassType(underlyingType, derived.sourcePos, traitReq = false, stablePrefixReq = true) val typeClass = derivedType.classSymbol val nparams = typeClass.typeParams.length - if (derivedType.isRef(defn.GenericClass)) - derivesGeneric = true - else { + + lazy val clsTpe = cls.typeRef.EtaExpand(cls.typeParams) + if (nparams == 1 && clsTpe.hasSameKindAs(typeClass.typeParams.head.info)) { + // A "natural" type class instance ... the kind of the data type + // matches the kind of the unique type class type parameter + + val resultType = derivedType.appliedTo(clsTpe) + val instanceInfo = ExprType(resultType) + addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos) + } else { // A matrix of all parameter combinations of current class parameters // and derived typeclass parameters. // Rows: parameters of current class @@ -220,49 +138,7 @@ trait Deriving { this: Typer => val instanceInfo = if (clsParams.isEmpty) ExprType(resultType) else PolyType.fromParams(clsParams, ImplicitMethodType(evidenceParamInfos, resultType)) - addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos, reportErrors = true) - } - } - - /** 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) - } - - /** If any of the instances has a companion with a `derived` member - * that refers to `scala.reflect.Generic`, add an implied instance - * of `Generic`. Note: this is just an optimization to avoid possible - * code duplication. Generic instances are created on the fly if they - * are missing from the companion. - */ - private def maybeAddGeneric(): Unit = { - val genericCls = defn.GenericClass - def refersToGeneric(sym: Symbol): Boolean = { - val companion = sym.info.finalResultType.classSymbol.companionModule - val derivd = companion.info.member(nme.derived) - derivd.hasAltWith(sd => sd.info.existsPart(p => p.typeSymbol == genericCls)) - } - if (derivesGeneric || synthetics.exists(refersToGeneric)) { - derive.println(i"add generic infrastructure for $cls") - addGeneric() - addGenericClass() + addDerivedInstance(originalType.typeSymbol.name, instanceInfo, derived.sourcePos) } } @@ -270,176 +146,15 @@ trait Deriving { this: Typer => * append them to `synthetics` buffer, and enter them into class scope. * Also, add generic instances if needed. */ - def enterDerived(derived: List[untpd.Tree]) = { + def enterDerived(derived: List[untpd.Tree]) = derived.foreach(processDerivedInstance(_)) - maybeAddGeneric() - } - - 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 { + /** The synthesized type class instance definitions */ + def syntheticDefs: List[tpd.Tree] = { 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))).cast(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) = + 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) @@ -450,46 +165,27 @@ trait Deriving { this: Typer => case info: MethodType => info.instantiate(params.map(_.termRef)) case info => info.widenExpr } - def classAndCompanionRef(tp: Type): (ClassSymbol, TermRef) = tp match { + def companionRef(tp: Type): TermRef = tp match { case tp @ TypeRef(prefix, _) if tp.symbol.isClass => - (tp.symbol.asClass, prefix.select(tp.symbol.companionModule).asInstanceOf[TermRef]) + prefix.select(tp.symbol.companionModule).asInstanceOf[TermRef] case tp: TypeProxy => - classAndCompanionRef(tp.underlying) + companionRef(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) - } + val module = untpd.ref(companionRef(resultType)).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) + tpd.polyDefDef(sym.asTerm, typeclassInstance(sym)(ctx.fresh.setOwner(sym).setNewScope)) - def syntheticDefs: List[Tree] = synthetics.map(syntheticDef).toList + 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) + tpd.cpy.TypeDef(stat)(rhs = tpd.cpy.Template(templ)(body = templ.body ++ syntheticDefs)) } } } diff --git a/compiler/src/dotty/tools/dotc/typer/Implicits.scala b/compiler/src/dotty/tools/dotc/typer/Implicits.scala index af7d0c31d621..7a70fede9328 100644 --- a/compiler/src/dotty/tools/dotc/typer/Implicits.scala +++ b/compiler/src/dotty/tools/dotc/typer/Implicits.scala @@ -3,7 +3,7 @@ package dotc package typer import core._ -import ast.{Trees, TreeTypeMap, untpd, tpd} +import ast.{Trees, TreeTypeMap, untpd, tpd, DesugarEnums} import util.Spans._ import util.Stats.{track, record, monitored} import printing.{Showable, Printer} @@ -26,7 +26,11 @@ import ProtoTypes._ import ErrorReporting._ import reporting.diagnostic.Message import Inferencing.fullyDefinedType +import TypeApplications.EtaExpansion import Trees._ +import transform.SymUtils._ +import transform.TypeUtils._ +import transform.SyntheticMembers._ import Hashable._ import util.{Property, SourceFile, NoSource} import config.Config @@ -643,15 +647,12 @@ trait Implicits { self: Typer => } } - /** Find an implicit argument for parameter `formal`. - * Return a failure as a SearchFailureType in the type of the returned tree. - */ - def inferImplicitArg(formal: Type, span: Span)(implicit ctx: Context): Tree = { + /** Handlers to synthesize implicits for special types */ + type SpecialHandler = (Type, Span) => Context => Tree + type SpecialHandlers = List[(ClassSymbol, SpecialHandler)] - /** If `formal` is of the form ClassTag[T], where `T` is a class type, - * synthesize a class tag for `T`. - */ - def synthesizedClassTag(formal: Type): Tree = formal.argInfos match { + lazy val synthesizedClassTag: SpecialHandler = + (formal: Type, span: Span) => implicit (ctx: Context) => formal.argInfos match { case arg :: Nil => fullyDefinedType(arg, "ClassTag argument", span) match { case defn.ArrayOf(elemTp) => @@ -673,7 +674,8 @@ trait Implicits { self: Typer => EmptyTree } - def synthesizedTypeTag(formal: Type): Tree = { + lazy val synthesizedTypeTag: SpecialHandler = + (formal: Type, span: Span) => implicit (ctx: Context) => { def quotedType(t: Type) = { if (StagingContext.level == 0) ctx.compilationUnit.needsStaging = true // We will need to run ReifyQuotes @@ -705,59 +707,60 @@ trait Implicits { self: Typer => } } - def synthesizedTastyContext(formal: Type): Tree = + lazy val synthesizedTastyContext: SpecialHandler = + (formal: Type, span: Span) => implicit (ctx: Context) => if (ctx.inInlineMethod || enclosingInlineds.nonEmpty) ref(defn.TastyReflection_macroContext) else EmptyTree - def synthesizedTupleFunction(formal: Type): Tree = { - formal match { - case AppliedType(_, funArgs @ fun :: tupled :: Nil) => - def functionTypeEqual(baseFun: Type, actualArgs: List[Type], actualRet: Type, expected: Type) = { - expected =:= defn.FunctionOf(actualArgs, actualRet, defn.isImplicitFunctionType(baseFun), defn.isErasedFunctionType(baseFun)) - } - val arity: Int = { - if (defn.isErasedFunctionType(fun) || defn.isErasedFunctionType(fun)) -1 // TODO support? - else if (defn.isFunctionType(fun)) { - // TupledFunction[(...) => R, ?] - fun.dropDependentRefinement.dealias.argInfos match { - case funArgs :+ funRet if functionTypeEqual(fun, defn.tupleType(funArgs) :: Nil, funRet, tupled) => - // TupledFunction[(...funArgs...) => funRet, ?] - funArgs.size - case _ => -1 - } - } else if (defn.isFunctionType(tupled)) { - // TupledFunction[?, (...) => R] - tupled.dropDependentRefinement.dealias.argInfos match { - case tupledArgs :: funRet :: Nil => - defn.tupleTypes(tupledArgs) match { - case Some(funArgs) if functionTypeEqual(tupled, funArgs, funRet, fun) => - // TupledFunction[?, ((...funArgs...)) => funRet] - funArgs.size - case _ => -1 - } - case _ => -1 - } + lazy val synthesizedTupleFunction: SpecialHandler = + (formal: Type, span: Span) => implicit (ctx: Context) => formal match { + case AppliedType(_, funArgs @ fun :: tupled :: Nil) => + def functionTypeEqual(baseFun: Type, actualArgs: List[Type], actualRet: Type, expected: Type) = { + expected =:= defn.FunctionOf(actualArgs, actualRet, defn.isImplicitFunctionType(baseFun), defn.isErasedFunctionType(baseFun)) + } + val arity: Int = { + if (defn.isErasedFunctionType(fun) || defn.isErasedFunctionType(fun)) -1 // TODO support? + else if (defn.isFunctionType(fun)) { + // TupledFunction[(...) => R, ?] + fun.dropDependentRefinement.dealias.argInfos match { + case funArgs :+ funRet if functionTypeEqual(fun, defn.tupleType(funArgs) :: Nil, funRet, tupled) => + // TupledFunction[(...funArgs...) => funRet, ?] + funArgs.size + case _ => -1 } - else { - // TupledFunction[?, ?] - -1 + } else if (defn.isFunctionType(tupled)) { + // TupledFunction[?, (...) => R] + tupled.dropDependentRefinement.dealias.argInfos match { + case tupledArgs :: funRet :: Nil => + defn.tupleTypes(tupledArgs) match { + case Some(funArgs) if functionTypeEqual(tupled, funArgs, funRet, fun) => + // TupledFunction[?, ((...funArgs...)) => funRet] + funArgs.size + case _ => -1 + } + case _ => -1 } } - if (arity == -1) - EmptyTree - else if (arity <= Definitions.MaxImplementedFunctionArity) - ref(defn.InternalTupleFunctionModule).select(s"tupledFunction$arity".toTermName).appliedToTypes(funArgs) - else - ref(defn.InternalTupleFunctionModule).select("tupledFunctionXXL".toTermName).appliedToTypes(funArgs) - case _ => + else { + // TupledFunction[?, ?] + -1 + } + } + if (arity == -1) EmptyTree - } + else if (arity <= Definitions.MaxImplementedFunctionArity) + ref(defn.InternalTupleFunctionModule).select(s"tupledFunction$arity".toTermName).appliedToTypes(funArgs) + else + ref(defn.InternalTupleFunctionModule).select("tupledFunctionXXL".toTermName).appliedToTypes(funArgs) + case _ => + EmptyTree } - /** If `formal` is of the form Eql[T, U], try to synthesize an - * `Eql.eqlAny[T, U]` as solution. - */ - def synthesizedEq(formal: Type)(implicit ctx: Context): Tree = { + /** If `formal` is of the form Eql[T, U], try to synthesize an + * `Eql.eqlAny[T, U]` as solution. + */ + lazy val synthesizedEq: SpecialHandler = + (formal: Type, span: Span) => implicit (ctx: Context) => { /** Is there an `Eql[T, T]` instance, assuming -strictEquality? */ def hasEq(tp: Type)(implicit ctx: Context): Boolean = { @@ -818,10 +821,11 @@ trait Implicits { self: Typer => } } - /** Creates a tree that will produce a ValueOf instance for the requested type. - * An EmptyTree is returned if materialization fails. - */ - def synthesizedValueOf(formal: Type)(implicit ctx: Context): Tree = { + /** Creates a tree that will produce a ValueOf instance for the requested type. + * An EmptyTree is returned if materialization fails. + */ + lazy val synthesizedValueOf: SpecialHandler = + (formal: Type, span: Span) => implicit (ctx: Context) => { def success(t: Tree) = New(defn.ValueOfClass.typeRef.appliedTo(t.tpe), t :: Nil).withSpan(span) formal.argTypes match { @@ -841,40 +845,238 @@ trait Implicits { self: Typer => } } - /** 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 + /** Create an anonymous class `new Object { type MirroredMonoType = ... }` + * and mark it with given attachment so that it is made into a mirror at PostTyper. + */ + private def anonymousMirror(monoType: Type, attachment: Property.StickyKey[Unit], span: Span)(implicit ctx: Context) = { + val monoTypeDef = untpd.TypeDef(tpnme.MirroredMonoType, untpd.TypeTree(monoType)) + val newImpl = untpd.Template( + constr = untpd.emptyConstructor, + parents = untpd.TypeTree(defn.ObjectType) :: Nil, + derived = Nil, + self = EmptyValDef, + body = monoTypeDef :: Nil + ).withAttachment(attachment, ()) + typed(untpd.New(newImpl).withSpan(span)) + } + + /** The mirror type + * + * { + * MirroredMonoType = + * MirroredTypeConstrictor = + * MirroredLabel =